
"Roland Schwarz" <roland.schwarz@chello.at> wrote in message news:454A9E56.8050901@chello.at...
Chris Thomasson wrote:
Here is one without a ctor:
It still has a dtor. Same as Anmthony already said applies to dtor too.
You will end up needing to drop the class and use a function with a flag I suspect.
Yup. I think so...
Btw.: The usage scenario you were showing earlier is not quite what we had in mind. We were speaking about local statics.
Ahhh... Okay, I was thinking global.
In your example usage, you have moved the static part to global namespace. There are far more less problems.
Indeed! :^)
The problems are getting nasty when needing local statics as in
void foo() { static bar mybar;
}
You really might need them, because the static possibly depending an a template parameter, which makes it impossible to move it to global namespace.
So you will need to be able to declare the once class inside the function body scope.
I see.
Back to ctor/dtor. Once you have found out that ctors and dtors are bad, what is left? Well several options:
1) a once function and a flag (instead of a class) 2) a static initializeable mutex
1) and 2) are roughly equivalent in that one can be built out of the other.
Agreed. However, one can be lock-free after the initialization takes place... IMHO, this is a fairly important aspect of this type of algorithm. I am going to give my pseudo-code one more shot at the end of the post in the form of compliable code that uses my AppCore library. This time, no class, just a single C function. I urge you to bear with me, and try to take a quick look at it... Thank you all for your time and patience! :^)
1) has been used in pthreads (before a static mutex type was available) Boost.Thread also uses this also mainly in implementation code.
2) I have tried to find out if it is possible to implement a mutex that is statically initializeable with the additional constraint that zero initialization suffices. I have provided a prototype that let me believe it really can be done. (Such a mutex has the additional benefit that you need no explicit memory management.)
You can do a spinlock for sure... How are you setting the mutex's waitset? Are you deferring waitset allocation until first point of contention (e.g., lazy mutex) ? Okay here is a link to AppCore: http://appcore.home.comcast.net/ And here is the once code; please tell me what you think of my technique: once-experimental.cpp --------------- #include <cassert> #include <cstdio> #include <appcore.h> // User API Decl namespace atomic { template<typename T> class once_ptr; } // namespace atomic // System API Decl namespace atomic { namespace sys { typedef ac_intword_t refs_t; template<typename T> struct once_POD; template<typename T> struct once_def_POD; static bool once_inc(refs_t*) throw(); static bool once_dec(refs_t*) throw(); static void dbg_allocs_inc() throw(); static void dbg_allocs_dec() throw(); }} // namespace atomic::sys // System API Def namespace atomic { namespace sys { // Debug counter static ac_intword_t dbg_allocs = 0; // Inc the debug counter void dbg_allocs_inc() throw() { ac_intword_t allocs = ac_atomic_inc_acquire(&dbg_allocs); std::printf("atomic::sys::dbg_allocs_inc - %i\n", allocs); } // Inc the debug counter void dbg_allocs_dec() throw() { ac_intword_t allocs = ac_atomic_dec_acquire(&dbg_allocs); std::printf("atomic::sys::dbg_allocs_dec - %i\n", allocs); } // Inc the refcount if its >= 0. // returns true if the refcount was inc'd, // otherwise false bool once_inc(refs_t *_this) throw() { refs_t local, cmp; do { local = ::ac_mb_load_naked(_this); if (local < 0) { return false; } cmp = local; local = ac_atomic_cas_acquire(_this, local, local + 1); } while(cmp != local); std::printf("atomic::sys::once_inc(); - %i\n", local + 1); return true; } // Dec the refcount if its >= 0. // returns true for the last ref, // otherwise false bool once_dec(refs_t *_this) throw() { refs_t local, cmp; do { local = ::ac_mb_load_naked(_this); if (local < 0) { return false; } cmp = local; local = ac_atomic_cas_acquire(_this, local, local - 1); } while(cmp != local); std::printf("atomic::sys::once_dec(); - %i\n", local - 1); if (local == 1) { ac_mb_store_release(_this, 0); return true; } else if (! local) { return true; } return false; } // once is a POD #define ATOMIC_ONCE_SYS_STATICINIT() {0, 0} template<typename T> struct once_POD { typedef T type_t; typedef once_def_POD<once_POD> define_POD_t; refs_t m_refs; type_t *m_state; }; // define is a POD that holds a once POD template<typename T> struct once_def_POD { typedef typename T::type_t type_t; static T s_this; // Acquires a reference and calls ctor for first ref. // returns pointer to ref, // otherwise NULL type_t* acquire() { type_t *local = (type_t*)ac_mb_loadptr_depends(&s_this.m_state); if (! local) { // hashed_mutex::guard_t lock(&_this); local = (type_t*)ac_mb_loadptr_depends(&s_this.m_state); if (! once_inc(&s_this.m_refs)) { return 0; } if (! local) { try { local = new type_t; } catch(...) { (void)once_dec(&s_this.m_refs); throw; } dbg_allocs_inc(); std::printf("\n(%p)once_def_POD::acquire(); - %p\n", (void*)this, (void*)local); ac_mb_storeptr_release(&s_this.m_state, local); } } else if(! once_inc(&s_this.m_refs)) { return 0; } return local; } // Releases a reference and calls dtor if last ref. // returns nothing, // otherwise false void release() { type_t *local = (type_t*)ac_mb_loadptr_depends(&s_this.m_state); if (once_dec(&s_this.m_refs)) { if (local) { ac_atomic_casptr_acquire(&s_this.m_state, local, 0); delete local; std::printf("(%p)once_def_POD::release(); - %p\n\n", (void*)this, (void*)local); dbg_allocs_dec(); } } } // static POD init }; template<typename T> T once_def_POD<T>::s_this = ATOMIC_ONCE_SYS_STATICINIT(); }} // namespace atomic::sys // User API Def namespace atomic { // Holds an acquired reference. template<typename T> class once_ptr { public: typedef typename sys::once_POD<T>::define_POD_t define_POD_t; private: define_POD_t *m_once; T *m_state; private: T* acquire() { return (m_once) ? m_once->acquire() : 0; } void release() { if (m_once) { assert(m_state); m_once->release(); } } public: once_ptr() throw() : m_once(0), m_state(0) {} once_ptr(define_POD_t &_once) : m_once(&_once), m_state(_once.acquire()) {} ~once_ptr() { release(); } public: once_ptr(once_ptr const &rhs) : m_once(rhs.m_once), m_state(rhs.acquire()) {} once_ptr const& operator =(once_ptr &rhs) { define_POD_t *old = m_once; if (old != rhs.m_once) { define_POD_t *_once = rhs.m_once; T *_state = rhs.acquire(); release(); m_once = _once; m_state = _state; } return *this; } once_ptr const& operator =(define_POD_t &rhs) { define_POD_t *old = m_once; if (old != &rhs) { define_POD_t *_once = &rhs; T *_state = rhs.acquire(); release(); m_once = _once; m_state = _state; } return *this; } public: T* load() const throw() { return m_state; } public: T* operator ->() { return load(); } T& operator *() { return *load(); } public: operator bool() throw() { return (m_state != 0); } bool operator !() throw() { return (m_state == 0); } }; } // namespace atomic // Here is sample usage: struct foo1 { typedef atomic::once_ptr<foo1> once_ptr_t; void whatever() { std::printf("(%p)foo1::whatever();\n", (void*)this); } foo1() { std::printf("(%p)foo1::foo1();\n", (void*)this); } ~foo1() { std::printf("(%p)foo1::~foo1();\n", (void*)this); } }; static void funca_for_multiple_threads() { static foo1::once_ptr_t::define_POD_t s_foo; foo1::once_ptr_t myfoo1(s_foo); if (myfoo1) { myfoo1->whatever(); } } struct foo2 { typedef atomic::once_ptr<foo1> once_ptr_t; void whatever() { std::printf("(%p)foo2::whatever();\n", (void*)this); funca_for_multiple_threads(); } foo2() { static foo1::once_ptr_t::define_POD_t s_foo; foo1::once_ptr_t myfoo1(s_foo); if (myfoo1) { myfoo1->whatever(); } std::printf("(%p)foo2::foo2();\n", (void*)this); } ~foo2() { std::printf("(%p)foo2::~foo2();\n", (void*)this); } }; struct foo3 { typedef atomic::once_ptr<foo3> once_ptr_t; static foo1::once_ptr_t::define_POD_t s_foo1; void whatever() { std::printf("(%p)foo3::whatever();\n", (void*)this); foo1::once_ptr_t myfoo1(s_foo1); if (myfoo1) { myfoo1->whatever(); } funca_for_multiple_threads(); } foo3() { static foo2::once_ptr_t::define_POD_t s_foo2; foo2::once_ptr_t myfoo2(s_foo2), myfoo2a; if (myfoo2) { myfoo2->whatever(); foo1::once_ptr_t myfoo1(s_foo1); myfoo2a = myfoo2; if (myfoo1) { myfoo1->whatever(); } myfoo2a->whatever(); } std::printf("(%p)foo3::foo3();\n", (void*)this); } ~foo3() { whatever(); std::printf("(%p)foo3::~foo3();\n", (void*)this); } }; foo1::once_ptr_t::define_POD_t foo3::s_foo1; static void funcb_for_multiple_threads() { funca_for_multiple_threads(); static foo2::once_ptr_t::define_POD_t s_myfoo2a; funca_for_multiple_threads(); foo2::once_ptr_t smyfooa(s_myfoo2a), smyfooaa; if (smyfooa) { smyfooa->whatever(); } { funca_for_multiple_threads(); static foo2::once_ptr_t::define_POD_t s_myfoo2b; foo1 myfoo1; foo2::once_ptr_t smyfooa(s_myfoo2a); if (smyfooa) { smyfooaa = smyfooa; smyfooaa->whatever(); } foo2 myfoo2; { myfoo1.whatever(); foo2::once_ptr_t smyfoo2(s_myfoo2a); if (smyfoo2) { smyfoo2->whatever(); } myfoo2.whatever(); funca_for_multiple_threads(); { foo3 myfoo3; myfoo3.whatever(); foo2 myfoo2; myfoo2.whatever(); } myfoo2.whatever(); } foo3 myfoo3; foo2::once_ptr_t smyfoo2(s_myfoo2b); if (smyfoo2) { smyfoo2->whatever(); funca_for_multiple_threads(); myfoo3.whatever(); } funca_for_multiple_threads(); } if (smyfooaa) { smyfooaa->whatever(); } funca_for_multiple_threads(); } int main(int argc, char *argv[]) { funca_for_multiple_threads(); funcb_for_multiple_threads(); funca_for_multiple_threads(); funcb_for_multiple_threads(); return 0; }