[proposal] Atomically Thread-Safe Static Initialization For Boost...

Here is the C++ pseudo-code: namespace atomic { template<typename T> class var; template<typename T> class once; namespace mb { enum naked_e { naked }; enum fence_e { fence }; enum release_e { release }; enum depends_e { depends }; enum acquire_depends_e { acquire_depends }; } namespace externlib_api { class hashed_mutex; template<typename T> extern T load(T volatile*, mb::naked_e) const throw(); template<typename T> extern T load(T volatile*, mb::depends_e) const throw(); template<typename T> extern void store(T volatile*, T const&, mb::release_e) throw(); template<typename T> extern void store(T volatile*, T const&, mb::naked_e) throw(); template<typename T> extern bool cas(T volatile*, T const&, T const&, mb::acquire_depends_e) throw(); template<typename T> extern bool cas(T volatile*, T const&, T const&, mb::fence_e) throw(); } template<typename T> class var { mutable T volatile m_state; public: var() throw() {} var(T const &state) throw() : m_state(state) {} public: inline T load(mb::depends_e) const throw() { return externlib_api::load(&m_state, mb::depends); } inline T load(mb::naked_e) const throw() { return externlib_api::load(&m_state, mb::naked); } public: inline void store(T const &xchg, mb::fence_e) throw() { externlib_api::store(&m_state, xchg, mb::fence); } inline void store(T const &xchg, mb::naked_e) throw() { externlib_api::store(&m_state, xchg, mb::naked); } public: inline bool cas(T const &cmp, T const &xchg, mb::fence_e) throw() { return externlib_api::cas(&m_state, cmp, xchg, mb::fence); } inline bool cas(T const &cmp, T const &xchg, mb::acquire_depends_e) throw() { return externlib_api::cas(&m_state, cmp, xchg, mb::acquire_depends); } }; template<typename T> class once { var<T*> m_state; var<intword_t> m_count; public: once() throw() : m_state(0), m_count(1) {} ~once() throw() { if (try_dec()) { try { delete local; } catch(...) { assert(false); throw; } } } private: bool try_dec() throw() { intword_t local; do { local = m_count.load(mb::naked); if (! local || local < 1) { return false; } // use mb::fence to cover *both acquire and release // wrt the result of the decrement } while(! m_count.cas(local, local - 1, mb::fence)); return (local == 1); } bool try_inc() throw() { intword_t local; do { local = m_count.load(mb::naked); if (! local || local < 1) { return false; } } while(! m_count.cas(local, local + 1, mb::acquire_depends)); return true; } public: // atomic load / thread-saftey: strong T* load() const { T *local = m_state.load(mb::depends); if (! local) { externlib_api::hashed_mutex::guard_t const &lock(this); if (! try_inc()) { return 0; } local = m_state.load(mb::naked); if (! local) { // call try_dec on exceptions... local = new T; m_state.store(local, mb::release); } } else if(! try_inc()) { return 0; } return local; } // dec the refcount void dec() { if (try_dec()) { delete local; } } }; } okay, here is sample usage now: static atomic::once<foo> g_foo; void some_threads(...) { for(;;) { // whatever... foo *myfoo = g_foo.load(); if (myfoo) { // use myfoo... // okay, we are finished with myfoo g_foo.dec(); } } } This should be a workable solution to the static initialization problem... What do you all think?

Darn, I forgot to take out the redundant assertions again! Okay, every line in the pseudo-code that looks like this:
if (! local || local < 1) { return false; }
can look like this: if (local < 1) { return false; }

Chris Thomasson wrote:
Here is the C++ pseudo-code:
Please don't get me wrong. I very much appreciate your efforts, but since I am very short of time (I think I am not the only one), could you please also use some plain English words to describe your idea, so one can judge if it is worth spending the time studying your code. Also I did not see any response from you how your proposal deviates from previous similar ones with respect to: Initialization order problem, i.e beeing able to be called from within global ctors. Solving destruction of static objects (preferably at process shutdown). Thank you Roland

"Roland Schwarz" <roland.schwarz@chello.at> wrote in message news:4549A086.3050005@chello.at...
Chris Thomasson wrote:
Here is the C++ pseudo-code:
Please don't get me wrong. I very much appreciate your efforts, but since I am very short of time (I think I am not the only one), could you please also use some plain English words to describe your idea, so one can judge if it is worth spending the time studying your code.
I am very sorry here... I too am sometimes short of time, so I have to blast some pseudo-code out in order to get my point across, rather than doing a quick write-up... I promise you that once I get my atomic::once out of the pseudo-code stage, and transform it into a fully working prototype it will have documentation... ;^)
Also I did not see any response from you how your proposal deviates from previous similar ones with respect to:
Initialization order problem, i.e beeing able to be called from within global ctors.
I removed the ctor from atomic::once, but I need to think about that some more here...
Solving destruction of static objects (preferably at process shutdown).
I am using an augmented type of reference counting for this. It will dtor the static object only during/after the dtor for atomic::once runs and when the count drops to (-1). I used to wait until it dropped to zero in the version with the ctor because I was using it to init to 1.

Chris Thomasson wrote:
I promise you that once I get my atomic::once out of the pseudo-code stage, and transform it into a fully working prototype it will have documentation...
I would be interested in why you think once is superior to statically initializeable mutex? My personal believe is, if we have a clean (static) mutex we should prefer it, because it is one concept less the programmer has to learn and remeber. "once" concept was and is just a work-around a deficiency. Roland

Roland Schwarz <roland.schwarz@chello.at> writes:
I would be interested in why you think once is superior to statically initializeable mutex?
My personal believe is, if we have a clean (static) mutex we should prefer it, because it is one concept less the programmer has to learn and remeber.
"once" concept was and is just a work-around a deficiency.
"Once" is more low-level. It can be used to initialize a mutex, *or any other type of object*. Yes, if you have a static mutex, you can lock the mutex around the initialization of another object, but that's unnecessarily complex: void f() { static mutex m; m.lock(); static some_class x; m.unlock(); // access x } You can't use a scoped_lock for this unless it has an unlock() member (which IMO defeats the point of it being scoped), since otherwise you end up serializing the "access x" part too. Anthony -- Anthony Williams Software Developer Just Software Solutions Ltd http://www.justsoftwaresolutions.co.uk

Anthony Williams wrote:
"Once" is more low-level. It can be used to initialize a mutex, *or any other type of object*. Yes, if you have a static mutex, you can lock the mutex around the initialization of another object, but that's unnecessarily complex:
Sorry, but I disagree. My reasoning is that only one concept is better than two for the same purpose, i.e. lock, instead of two i.e. lock and once. Once you understand locking, you necessarily understand protection by locks. On the other hand you will need to look up what once exactly is about the first time you encounter it. To me at least its purpose was not obvious on first encounter.
You can't use a scoped_lock for this unless it has an unlock() member (which IMO defeats the point of it being scoped), since otherwise you end up serializing the "access x" part too.
I don't think so: void f() { static mutex m; scoped_lock lk(m); static some_class x; m.unlock(); // access x } You cannot defeat the purpose of scoped_lock! In this case it is conditionally unlocked when the scope is left. This in the first place is, why Peter Dimov came up with an example that proved me that you need be able to do locking directly on the mutex too. This also is why the scoped_lock isn't thread safe. In the destructor it has to check an unprotected variable that tests the current lock status. Roland

Roland Schwarz <roland.schwarz@chello.at> writes:
Anthony Williams wrote:
"Once" is more low-level. It can be used to initialize a mutex, *or any other type of object*. Yes, if you have a static mutex, you can lock the mutex around the initialization of another object, but that's unnecessarily complex:
Sorry, but I disagree. My reasoning is that only one concept is better than two for the same purpose, i.e. lock, instead of two i.e. lock and once.
Once you understand locking, you necessarily understand protection by locks. On the other hand you will need to look up what once exactly is about the first time you encounter it. To me at least its purpose was not obvious on first encounter.
OK, call it "locked initialization", or "thread-safe initialization" or "synchronized initialization". IMO, the concept of "once" is more fundamental than a mutex, and we should certainly provide both.
You can't use a scoped_lock for this unless it has an unlock() member (which IMO defeats the point of it being scoped), since otherwise you end up serializing the "access x" part too.
I don't think so:
void f() { static mutex m; scoped_lock lk(m); static some_class x; m.unlock(); // access x }
Surely you mean lk.unlock()? Otherwise scoped_lock will try and unlock the mutex again. I did say "unless it has an unlock() member" above. How is this different from using straight-forward lock() and unlock() functions on the mutex?
You cannot defeat the purpose of scoped_lock! In this case it is conditionally unlocked when the scope is left. This in the first place is, why Peter Dimov came up with an example that proved me that you need be able to do locking directly on the mutex too.
Using scoped lock here when all you really want is lock and unlock just seems wrong to me. The lock isn't scoped, since there's an explicit unlock. I generally feel uneasy about scoped_lock::unlock, since it makes reasoning about code harder. void f(mutex& m) { scoped_lock lk(m); if(xyz()) { lk.unlock(); } // is lk locked or not? }
This also is why the scoped_lock isn't thread safe. In the destructor it has to check an unprotected variable that tests the current lock status.
I know that. Anthony -- Anthony Williams Software Developer Just Software Solutions Ltd http://www.justsoftwaresolutions.co.uk

"Chris Thomasson" <cristom@comcast.net> writes:
once() throw() : m_state(0), m_count(1) {}
static atomic::once<foo> g_foo;
void some_threads(...) { for(;;) { // whatever...
foo *myfoo = g_foo.load(); if (myfoo) { // use myfoo...
// okay, we are finished with myfoo g_foo.dec(); }
} }
This should be a workable solution to the static initialization problem...
What do you all think?
your once<> class has a constructor, so it is therefore dynamically initialized, rather than statically, so there is a potential race condition in calling the constructor. Not only might multiple threads run the constructor, but some threads might get on to the load() call before the constructor is complete, because the constructor is running in another thread. Oh, and if the constructor is run multiple times, when is the destructor called, and how many times? This is particularly apparent in void some_thread_func() { static atomic::once<foo> g_foo; foo *myfoo = g_foo.load(); if (myfoo) { } } since g_foo won't be initialized until the first call. The problem still exists for global objects, but is rarer, since it will only happen with threads that run before main(), or as a consequence of the general global-initialization-order problem, which is independent of threads. Anthony -- Anthony Williams Software Developer Just Software Solutions Ltd http://www.justsoftwaresolutions.co.uk

[...]
your once<> class has a constructor, so it is therefore dynamically initialized, rather than statically, so there is a potential race condition in calling the constructor.
Here is one without a ctor: template<typename T> class once { var<T*> m_state; var<intword_t> m_count; public: // once() throw() : m_state(0), m_count(0) {} // m_state and m_count must be init to NULL! ~once() throw() { if (try_dec()) { try { delete local; } catch(...) { assert(false); throw; } } } private: bool try_dec() throw() { intword_t local; do { local = m_count.load(mb::naked); if (local < 0) { return false; } // use mb::fence to cover *both acquire and release // wrt the result of the decrement } while(! m_count.cas(local, local - 1, mb::fence)); return (local == -1); } bool try_inc() throw() { intword_t local; do { local = m_count.load(mb::naked); if (local < 0) { return false; } } while(! m_count.cas(local, local + 1, mb::acquire_depends)); return true; } public: // atomic load / thread-saftey: strong T* load() const { T *local = m_state.load(mb::depends); if (! local) { externlib_api::hashed_mutex::guard_t const &lock(this); if (! try_inc()) { return 0; } local = m_state.load(mb::naked); if (! local) { // call try_dec on exceptions... local = new T; m_state.store(local, mb::release); } } else if(! try_inc()) { return 0; } return local; } // dec the refcount void dec() { if (try_dec()) { delete m_state.load(mb::depends); } } }; The destructor runs if m_state is not null and if the refcount drops to -1. I can't really see a problem here...

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. Btw.: The usage scenario you were showing earlier is not quite what we had in mind. We were speaking about local statics. In your example usage, you have moved the static part to global namespace. There are far more less problems. 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. 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. 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.) Roland

Roland Schwarz <roland.schwarz@chello.at> writes:
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.)
boost/thread/win32/basic_timed_mutex.hpp on the thread-rewrite branch provides a win32-specific statically-initializable-with-zero mutex (that I use as the basis for boost::mutex on win32). The thing is, it does require initialization when used with non-static storage duration. If the constexpr proposal gets accepted into the C++ Standard, we won't have to worry about this in the future --- constructors that just use constant expressions can be tagged "constexpr", and used for static initialization. Anthony -- Anthony Williams Software Developer Just Software Solutions Ltd http://www.justsoftwaresolutions.co.uk

Anthony Williams wrote:
If the constexpr proposal gets accepted into the C++ Standard, we won't have to worry about this in the future --- constructors that just use constant expressions can be tagged "constexpr", and used for static initialization.
What does this mean, can you please illustrate it with an example? Thank you, Roland

Roland Schwarz <roland.schwarz@chello.at> writes:
Anthony Williams wrote:
If the constexpr proposal gets accepted into the C++ Standard, we won't have to worry about this in the future --- constructors that just use constant expressions can be tagged "constexpr", and used for static initialization.
What does this mean, can you please illustrate it with an example?
struct A { constexpr A(int i_): i(i_),j(0) {} int i; int j; }; static A a(6); // static initialization, same as {6,0}, but with a constructor. Anthony -- Anthony Williams Software Developer Just Software Solutions Ltd http://www.justsoftwaresolutions.co.uk

Anthony Williams wrote:
struct A { constexpr A(int i_): i(i_),j(0) {}
int i; int j; };
static A a(6); // static initialization, same as {6,0}, but with a constructor.
And such a struct shall be aggregate? What if there is something in the body of the ctor? Roland

Roland Schwarz <roland.schwarz@chello.at> writes:
Anthony Williams wrote:
struct A { constexpr A(int i_): i(i_),j(0) {}
int i; int j; };
static A a(6); // static initialization, same as {6,0}, but with a constructor.
And such a struct shall be aggregate?
No.
What if there is something in the body of the ctor?
That's not allowed. The members can be initialized with "constant expressions" in the member init list. Constant expressions are generalized to include calls to functions which are themselves marked constexpr. N1980 is the latest public version of the proposal. http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2006/n1980.pdf There is a revised version on the committee wiki, which hopefully will be in the post-Portland mailing. Anthony -- Anthony Williams Software Developer Just Software Solutions Ltd http://www.justsoftwaresolutions.co.uk

Anthony Williams wrote:
Roland Schwarz <roland.schwarz@chello.at> writes:
And such a struct shall be aggregate?
No.
Don't understand: 8.5.1/1 An aggregate is an array or class with no user-declared constructors, no private or protected non-static data members, no base classes, and no virtual functions. Our example class would fall out of this definition? Can't imagine why it should. To me the concept simply appears as an extension to aggregate initializers. Isn't it?
N1980 is the latest public version of the proposal.
http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2006/n1980.pdf Thank you.
Roland

Roland Schwarz <roland.schwarz@chello.at> writes:
Anthony Williams wrote:
Roland Schwarz <roland.schwarz@chello.at> writes:
And such a struct shall be aggregate?
No.
Don't understand:
8.5.1/1 An aggregate is an array or class with no user-declared constructors, no private or protected non-static data members, no base classes, and no virtual functions.
Our example class would fall out of this definition? Can't imagine why it should. To me the concept simply appears as an extension to aggregate initializers. Isn't it?
The class has a user-declared constructor, so it isn't an aggregate. Anthony -- Anthony Williams Software Developer Just Software Solutions Ltd http://www.justsoftwaresolutions.co.uk

Anthony Williams wrote:
The class has a user-declared constructor, so it isn't an aggregate.
But a constructor in the sense of an initializer list. I.e. something that can be converted to a POD and moved in at static init time. This behavior is what makes it different to a normal class. But perhaps they will invent a new name.... Roland

"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; }

[...]
This time, no class, just a single C function. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^6
strike that; it was suppose to read: This time there is no ctor or dtor, just two POD types and a smart pointer. sorry for any confusion! ;^(... Anyway, I think I created a correct atomically thread-safe static initialization algorithm this time... :^)

I found and killed a race-condition; the following three functions need to be changed: atomic::sys::once_dec(...) template<typename T> atomic::sys::once_def_POD<T>::release() Here is the fix: // System API Def namespace atomic { namespace sys { [...] // 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, xchg; do { local = ::ac_mb_load_naked(_this); if (local < 0) { return false; } xchg = (local == 1) ? -1 : local - 1; cmp = local; local = ac_atomic_cas_release(_this, local, xchg); } while(cmp != local); std::printf("atomic::sys::once_dec(); - %i\n", local - 1); return (local == 1); } [...] // define is a POD that holds a once POD template<typename T> struct once_def_POD { [...] // Acquires a reference and calls ctor for first ref. // returns pointer to ref, // otherwise NULL type_t* acquire() { type_t *local = 0, *cmp; do { if (local) { ac_atomic_dec_release(&s_this.m_refs); } 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); return local; } } else if(! once_inc(&s_this.m_refs)) { return 0; } cmp = (type_t*)ac_mb_loadptr_depends(&s_this.m_state); } while(local != cmp); 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)) { ac_mb_storeptr_fence(&s_this.m_state, 0); ac_mb_store_fence(&s_this.m_refs, 0); if (local) { delete local; std::printf("(%p)once_def_POD::release(); - %p\n\n", (void*)this, (void*)local); dbg_allocs_dec(); } } } }; [...] }} // namespace atomic::sys I attached the complete fixed code; once-experimental.cpp. The race-condition allowed a thread to load a state pointer and inc a reference count to a static object that was possibly being dtor'd. I fixed this by zeroing the state pointer and invalidating the reference count when the count dropped to zero. I also had to make a thread double check the load of a state pointer after incrementing a valid reference count. Now I can't seem to find any other race-conditions after this... Can you? ;^) IMHO, this particular algorithm may be worth spending a little time on... Humm... begin 666 once-experimental.cpp M(VEN8VQU9&4@/&-A<W-E<G0^#0HC:6YC;'5D92 \8W-T9&EO/@T*(VEN8VQU M9&4@/&%P<&-O<F4N:#X-"@T*#0HO+R!5<V5R($%022!$96-L#0IN86UE<W!A M8V4@871O;6EC('L@#0H@('1E;7!L871E/'1Y<&5N86UE(%0^(&-L87-S(&]N M8V5?<'1R.PT*(" -"GT@+R\@;F%M97-P86-E(&%T;VUI8PT*#0H-"B\O(%-Y M<W1E;2!!4$D@1&5C; T*;F%M97-P86-E(&%T;VUI8R![( T*;F%M97-P86-E M('-Y<R![( T*("!T>7!E9&5F(&%C7VEN='=O<F1?="!R969S7W0[(" -"B @ M=&5M<&QA=&4\='EP96YA;64@5#X@<W1R=6-T(&]N8V5?4$]$.PT*("!T96UP M;&%T93QT>7!E;F%M92!4/B!S=')U8W0@;VYC95]D969?4$]$.R @#0H@('-T M871I8R!B;V]L(&]N8V5?:6YC*')E9G-?="HI('1H<F]W*"D[#0H@('-T871I M8R!B;V]L(&]N8V5?9&5C*')E9G-?="HI('1H<F]W*"D[(" -"B @<W1A=&EC M('9O:60@9&)G7V%L;&]C<U]I;F,H*2!T:')O=R@I.PT*("!S=&%T:6,@=F]I M9"!D8F=?86QL;V-S7V1E8R@I('1H<F]W*"D[(" -"B @#0I]?2 O+R!N86UE M<W!A8V4@871O;6EC.CIS>7,-"@T*(" -"B\O(%-Y<W1E;2!!4$D@1&5F#0IN M86UE<W!A8V4@871O;6EC('L@#0IN86UE<W!A8V4@<WES('L@(" -"B @(" @ M(" @(" -"B @+R\@1&5B=6<@8V]U;G1E<B -"B @<W1A=&EC(&%C7VEN='=O M<F1?="!D8F=?86QL;V-S(#T@,#L@(" @(" @(" @#0H-"@T*(" O+R!);F,@ M=&AE(&1E8G5G(&-O=6YT97(@(" @(" @(" @(" -"B @=F]I9"!D8F=?86QL M;V-S7VEN8R@I('1H<F]W*"D@>PT*(" @(&%C7VEN='=O<F1?="!A;&QO8W,@ M/2!A8U]A=&]M:6-?:6YC7V%C<75I<F4H)F1B9U]A;&QO8W,I.PT*(" @('-T M9#HZ<')I;G1F*")A=&]M:6,Z.G-Y<SHZ9&)G7V%L;&]C<U]I;F,@+2 E:5QN M(BP@86QL;V-S*3L@(" @(" @(" @( T*("!]#0H@( T*(" -"B @+R\@26YC M('1H92!D96)U9R!C;W5N=&5R#0H@('9O:60@9&)G7V%L;&]C<U]D96,H*2!T M:')O=R@I('L-"B @("!A8U]I;G1W;W)D7W0@86QL;V-S(#T@86-?871O;6EC M7V1E8U]A8W%U:7)E*"9D8F=?86QL;V-S*3L-"B @("!S=&0Z.G!R:6YT9B@B M871O;6EC.CIS>7,Z.F1B9U]A;&QO8W-?9&5C("T@)6E<;B(L(&%L;&]C<RD[ M(" -"B @?0T*(" @(" @(" @(" @(" -"B @(" @#0H@("\O($EN8R!T:&4@ M<F5F8V]U;G0@:68@:71S(#X](# N#0H@("\O(')E='5R;G,@=')U92!I9B!T M:&4@<F5F8V]U;G0@=V%S(&EN8R=D+ T*(" O+R!O=&AE<G=I<V4@9F%L<V4@ M(" @#0H@(&)O;VP@;VYC95]I;F,H<F5F<U]T("I?=&AI<RD@=&AR;W<H*2![ M#0H@(" @<F5F<U]T(&QO8V%L+"!C;7 [#0H@(" @9&\@>PT*(" @(" @;&]C M86P@/2 Z.F%C7VUB7VQO861?;F%K960H7W1H:7,I.PT*(" @(" @:68@*&QO M8V%L(#P@,"D@>R!R971U<FX@9F%L<V4[('T-"B @(" @(&-M<" ](&QO8V%L M.PT*(" @(" @;&]C86P@/2!A8U]A=&]M:6-?8V%S7V%C<75I<F4H7W1H:7,L M(&QO8V%L+"!L;V-A;" K(#$I.PT*(" @('T@=VAI;&4H8VUP("$](&QO8V%L M*3L-"B @("!S=&0Z.G!R:6YT9B@B871O;6EC.CIS>7,Z.F]N8V5?:6YC*"D[ M("T@)6E<;B(L(&QO8V%L("L@,2D[(" @( T*(" @( T*(" @(')E='5R;B!T M<G5E.PT*("!]( T*(" @( T*(" @( T*(" O+R!$96,@=&AE(')E9F-O=6YT M(&EF(&ET<R ^/2 P+@T*(" O+R!R971U<FYS('1R=64@9F]R('1H92!L87-T M(')E9BP-"B @+R\@;W1H97)W:7-E(&9A;'-E#0H@(&)O;VP@;VYC95]D96,H M<F5F<U]T("I?=&AI<RD@=&AR;W<H*2![#0H@(" @<F5F<U]T(&QO8V%L+"!C M;7 L('AC:&<[#0H@(" @9&\@>PT*(" @(" @;&]C86P@/2 Z.F%C7VUB7VQO M861?;F%K960H7W1H:7,I.PT*(" @(" @:68@*&QO8V%L(#P@,"D@>R!R971U M<FX@9F%L<V4[('T-"B @(" @('AC:&<@/2 H;&]C86P@/3T@,2D@/R M,2 Z M(&QO8V%L("T@,3L-"B @(" @(&-M<" ](&QO8V%L.PT*(" @(" @;&]C86P@ M/2!A8U]A=&]M:6-?8V%S7W)E;&5A<V4H7W1H:7,L(&QO8V%L+"!X8VAG*3L- M"B @("!]('=H:6QE*&-M<" A/2!L;V-A;"D[( T*(" @('-T9#HZ<')I;G1F M*")A=&]M:6,Z.G-Y<SHZ;VYC95]D96,H*3L@+2 E:5QN(BP@;&]C86P@+2 Q M*3L@#0H@( T*(" @(')E='5R;B H;&]C86P@/3T@,2D[#0H@('T-"B @#0H@ M( T*(" O+R!O;F-E(&ES(&$@4$]$#0H@("-D969I;F4@051/34E#7T].0T5? M4UE37U-4051)0TE.250H*2![,"P@,'T-"B @=&5M<&QA=&4\='EP96YA;64@ M5#X-"B @<W1R=6-T(&]N8V5?4$]$('L-"B @("!T>7!E9&5F(%0@='EP95]T M.PT*(" @('1Y<&5D968@;VYC95]D969?4$]$/&]N8V5?4$]$/B!D969I;F5? M4$]$7W0[#0H@(" @<F5F<U]T(&U?<F5F<SL@(" @#0H@(" @='EP95]T("IM M7W-T871E.PT*("!].R @( T*(" -"B @#0H@("\O(&1E9FEN92!I<R!A(%!/ M1"!T:&%T(&AO;&1S(&$@;VYC92!03T0-"B @=&5M<&QA=&4\='EP96YA;64@ M5#X-"B @<W1R=6-T(&]N8V5?9&5F7U!/1"![#0H@(" @='EP961E9B!T>7!E M;F%M92!4.CIT>7!E7W0@='EP95]T.PT*(" @('-T871I8R!4('-?=&AI<SL- M"B @(" -"B @(" -"B @(" O+R!!8W%U:7)E<R!A(')E9F5R96YC92!A;F0@ M8V%L;',@8W1O<B!F;W(@9FER<W0@<F5F+@T*(" @("\O(')E='5R;G,@<&]I M;G1E<B!T;R!R968L#0H@(" @+R\@;W1H97)W:7-E($Y53$P@( T*(" @('1Y M<&5?="H@86-Q=6ER92@I('L-"B @(" @('1Y<&5?=" J;&]C86P@/2 P+" J M8VUP.PT*(" @(" @#0H@(" @("!D;R![#0H@(" @(" @(&EF("AL;V-A;"D@ M>R!A8U]A=&]M:6-?9&5C7W)E;&5A<V4H)G-?=&AI<RYM7W)E9G,I.R!]#0H@ M(" @(" @(&QO8V%L(#T@*'1Y<&5?="HI86-?;6)?;&]A9'!T<E]D97!E;F1S M*"9S7W1H:7,N;5]S=&%T92D[#0H@(" @(" @(&EF("@A(&QO8V%L*2![#0H@ M(" @(" @(" @+R\@:&%S:&5D7VUU=&5X.CIG=6%R9%]T(&QO8VLH)E]T:&ES M*3L-"B @(" @(" @("!L;V-A;" ]("AT>7!E7W0J*6%C7VUB7VQO861P=')? M9&5P96YD<R@F<U]T:&ES+FU?<W1A=&4I.PT*(" @(" @(" @(&EF("@A(&]N M8V5?:6YC*"9S7W1H:7,N;5]R969S*2D@>R!R971U<FX@,#L@?0T*(" @(" @ M(" @(&EF("@A(&QO8V%L*2![#0H@(" @(" @(" @("!T<GD@>R!L;V-A;" ] M(&YE=R!T>7!E7W0[('T@#0H@(" @(" @(" @("!C871C:"@N+BXI('L-"B @ M(" @(" @(" @(" @*'9O:60I;VYC95]D96,H)G-?=&AI<RYM7W)E9G,I.PT* M(" @(" @(" @(" @("!T:')O=SL-"B @(" @(" @(" @('T-"B @(" @(" @ M(" @(&1B9U]A;&QO8W-?:6YC*"D[#0H@(" @(" @(" @("!S=&0Z.G!R:6YT M9B@B7&XH)7 I;VYC95]D969?4$]$.CIA8W%U:7)E*"D[("T@)7!<;B(L("AV M;VED*BET:&ES+" H=F]I9"HI;&]C86PI.PT*(" @(" @(" @(" @86-?;6)? M<W1O<F5P=')?<F5L96%S92@F<U]T:&ES+FU?<W1A=&4L(&QO8V%L*3L-"B @ M(" @(" @(" @(')E='5R;B!L;V-A;#L-"B @(" @(" @("!]#0H@(" @(" @ M('T@96QS92!I9B@A(&]N8V5?:6YC*"9S7W1H:7,N;5]R969S*2D@>PT*(" @ M(" @(" @(')E='5R;B P.PT*(" @(" @("!]#0H@(" @(" @( T*(" @(" @ M("!C;7 @/2 H='EP95]T*BEA8U]M8E]L;V%D<'1R7V1E<&5N9',H)G-?=&AI M<RYM7W-T871E*3L-"B @(" @('T@=VAI;&4H;&]C86P@(3T@8VUP*3L-"B @ M(" @( T*(" @(" @<F5T=7)N(&QO8V%L.PT*(" @('T@( T*(" -"B @#0H@ M(" @+R\@4F5L96%S97,@82!R969E<F5N8V4@86YD(&-A;&QS(&1T;W(@:68@ M;&%S="!R968N#0H@(" @+R\@<F5T=7)N<R!N;W1H:6YG+ T*(" @("\O(&]T M:&5R=VES92!F86QS92 @#0H@(" @=F]I9"!R96QE87-E*"D@>R @(" @( T* M(" @(" @='EP95]T("IL;V-A;" ]("AT>7!E7W0J*6%C7VUB7VQO861P=')? M9&5P96YD<R@F<U]T:&ES+FU?<W1A=&4I.PT*(" @(" @:68@*&]N8V5?9&5C M*"9S7W1H:7,N;5]R969S*2D@>PT*(" @(" @("!A8U]M8E]S=&]R97!T<E]F M96YC92@F<U]T:&ES+FU?<W1A=&4L(# I.PT*(" @(" @("!A8U]M8E]S=&]R M95]F96YC92@F<U]T:&ES+FU?<F5F<RP@,"D[(" @(" @(" @(" @(" -"B @ M(" @(" @:68@*&QO8V%L*2![#0H@(" @(" @(" @9&5L971E(&QO8V%L.R - M"B @(" @(" @("!S=&0Z.G!R:6YT9B@B*"5P*6]N8V5?9&5F7U!/1#HZ<F5L M96%S92@I.R M("5P7&Y<;B(L("AV;VED*BET:&ES+" H=F]I9"HI;&]C86PI M.PT*(" @(" @(" @(&1B9U]A;&QO8W-?9&5C*"D[#0H@(" @(" @('T-"B @ M(" @('T-"B @("!]( T*(" @( T*(" @(" @+R\@<W1A=&EC(%!/1"!I;FET M( T*("!].R @=&5M<&QA=&4\='EP96YA;64@5#X@#0H@(" @("!4(&]N8V5? M9&5F7U!/1#Q4/CHZ<U]T:&ES(#T@051/34E#7T].0T5?4UE37U-4051)0TE. M250H*3L-"B @#0I]?2 O+R!N86UE<W!A8V4@871O;6EC.CIS>7,-"@T*#0HO M+R!5<V5R($%022!$968-"FYA;65S<&%C92!A=&]M:6,@>PT*(" -"B @+R\@ M2&]L9',@86X@86-Q=6ER960@<F5F97)E;F-E+@T*("!T96UP;&%T93QT>7!E M;F%M92!4/@T*("!C;&%S<R!O;F-E7W!T<B![#0H@('!U8FQI8SH-"B @("!T M>7!E9&5F('1Y<&5N86UE('-Y<SHZ;VYC95]03T0\5#XZ.F1E9FEN95]03T1? M="!D969I;F5?4$]$7W0[#0H-"B @(" -"B @<')I=F%T93H@(" @#0H@(" @ M9&5F:6YE7U!/1%]T("IM7V]N8V4[(" @#0H@(" @5" J;5]S=&%T93L-"B @ M(" -"B @(" -"B @<')I=F%T93H-"B @("!4*B!A8W%U:7)E*"D@>PT*(" @ M(" @<F5T=7)N("AM7V]N8V4I(#\@;5]O;F-E+3YA8W%U:7)E*"D@.B P.PT* M(" @('T-"B @(" -"B @("!V;VED(')E;&5A<V4H*2![#0H@(" @("!I9B H M;5]O;F-E*2![(&%S<V5R="AM7W-T871E*3L@;5]O;F-E+3YR96QE87-E*"D[ M('T-"B @("!](" @( T*#0H-"B @<'5B;&EC.@T*(" @(&]N8V5?<'1R*"D@ M=&AR;W<H*2 Z(&U?;VYC92@P*2P@;5]S=&%T92@P*2![?0T*(" @(" @(" - M"B @("!O;F-E7W!T<BAD969I;F5?4$]$7W0@)E]O;F-E*2 -"B @(" @(#H@ M;5]O;F-E*"9?;VYC92DL(&U?<W1A=&4H7V]N8V4N86-Q=6ER92@I*2![?0T* M(" @( T*(" @('YO;F-E7W!T<B@I('L@<F5L96%S92@I.R!]#0H@(" @#0H@ M(" @#0H@('!U8FQI8SH-"B @("!O;F-E7W!T<BAO;F-E7W!T<B!C;VYS=" F M<FAS*2 -"B @(" @(#H@;5]O;F-E*')H<RYM7V]N8V4I+"!M7W-T871E*')H M<RYA8W%U:7)E*"DI('M]#0H@(" @#0H@(" @;VYC95]P='(@8V]N<W0F(&]P M97)A=&]R(#TH;VYC95]P='(@)G)H<RD@>PT*(" @(" @9&5F:6YE7U!/1%]T M("IO;&0@/2!M7V]N8V4[#0H@(" @("!I9B H;VQD("$](')H<RYM7V]N8V4I M('L-"B @(" @(" @9&5F:6YE7U!/1%]T("I?;VYC92 ](')H<RYM7V]N8V4[ M#0H@(" @(" @(%0@*E]S=&%T92 ](')H<RYA8W%U:7)E*"D[#0H@(" @(" @ M(')E;&5A<V4H*3L-"B @(" @(" @;5]O;F-E(#T@7V]N8V4[#0H@(" @(" @ M(&U?<W1A=&4@/2!?<W1A=&4[#0H@(" @("!]#0H@(" @("!R971U<FX@*G1H M:7,[#0H@(" @?0T*(" @( T*(" @(&]N8V5?<'1R(&-O;G-T)B!O<&5R871O M<B ]*&1E9FEN95]03T1?=" F<FAS*2![#0H@(" @("!D969I;F5?4$]$7W0@ M*F]L9" ](&U?;VYC93L-"B @(" @(&EF("AO;&0@(3T@)G)H<RD@>PT*(" @ M(" @("!D969I;F5?4$]$7W0@*E]O;F-E(#T@)G)H<SL-"B @(" @(" @5" J M7W-T871E(#T@<FAS+F%C<75I<F4H*3L-"B @(" @(" @<F5L96%S92@I.PT* M(" @(" @("!M7V]N8V4@/2!?;VYC93L-"B @(" @(" @;5]S=&%T92 ](%]S M=&%T93L-"B @(" @('T-"B @(" @(')E='5R;B J=&AI<SL-"B @("!](" @ M( T*(" @(" @(" -"B @(" @(" @(" @( T*("!P=6)L:6,Z#0H@(" @5"H@ M;&]A9"@I(&-O;G-T('1H<F]W*"D@>R!R971U<FX@;5]S=&%T93L@?0T*#0H- M"B @<'5B;&EC.B @(" -"B @("!4*B!O<&5R871O<B M/B@I('L@<F5T=7)N M(&QO860H*3L@?0T*(" @(%0F(&]P97)A=&]R("HH*2![(')E='5R;B J;&]A M9"@I.R!]#0H@(" @#0H@(" @#0H@('!U8FQI8SH-"B @("!O<&5R871O<B!B M;V]L*"D@=&AR;W<H*2![(')E='5R;B H;5]S=&%T92 A/2 P*3L@?0T*(" @ M(&)O;VP@;W!E<F%T;W(@(2@I('1H<F]W*"D@>R!R971U<FX@*&U?<W1A=&4@ M/3T@,"D[('T-"B @?3L-"B @#0I]("\O(&YA;65S<&%C92!A=&]M:6,-"@T* M#0H-"@T*+R\@2&5R92!I<R!S86UP;&4@=7-A9V4Z#0IS=')U8W0@9F]O,2![ M( T*("!T>7!E9&5F(&%T;VUI8SHZ;VYC95]P='(\9F]O,3X@;VYC95]P=')? M=#L-"B @#0H@('9O:60@=VAA=&5V97(H*2![#0H@(" @<W1D.CIP<FEN=&8H M(B@E<"EF;V\Q.CIW:&%T979E<B@I.UQN(BP@*'9O:60J*71H:7,I.PT*("!] M#0H@( T*("!F;V\Q*"D@>R!S=&0Z.G!R:6YT9B@B*"5P*69O;S$Z.F9O;S$H M*3M<;B(L("AV;VED*BET:&ES*3L@?0T*(" -"B @?F9O;S$H*2![('-T9#HZ M<')I;G1F*"(H)7 I9F]O,3HZ?F9O;S$H*3M<;B(L("AV;VED*BET:&ES*3L@ M?0T*?3L-"@T*#0IS=&%T:6,@=F]I9"!F=6YC85]F;W)?;75L=&EP;&5?=&AR M96%D<R@I('L-"B @<W1A=&EC(&9O;S$Z.F]N8V5?<'1R7W0Z.F1E9FEN95]0 M3T1?="!S7V9O;SL-"@T*("!F;V\Q.CIO;F-E7W!T<E]T(&UY9F]O,2AS7V9O M;RD[#0H@(&EF("AM>69O;S$I('L-"B @("!M>69O;S$M/G=H871E=F5R*"D[ M#0H@('T-"GT-"@T*#0IS=')U8W0@9F]O,B![( T*("!T>7!E9&5F(&%T;VUI M8SHZ;VYC95]P='(\9F]O,3X@;VYC95]P=')?=#L-"B @#0H@('9O:60@=VAA M=&5V97(H*2![#0H@(" @<W1D.CIP<FEN=&8H(B@E<"EF;V\R.CIW:&%T979E M<B@I.UQN(BP@*'9O:60J*71H:7,I.PT*(" @(&9U;F-A7V9O<E]M=6QT:7!L M95]T:')E861S*"D[#0H@('T@( T*(" -"B @9F]O,B@I('L@#0H@(" @<W1A M=&EC(&9O;S$Z.F]N8V5?<'1R7W0Z.F1E9FEN95]03T1?="!S7V9O;SL@(" @ M(" @( T*(" @(&9O;S$Z.F]N8V5?<'1R7W0@;7EF;V\Q*'-?9F]O*3L-"B @ M("!I9B H;7EF;V\Q*2![#0H@(" @("!M>69O;S$M/G=H871E=F5R*"D[#0H@ M(" @?2 @#0H@(" @(" -"B @("!S=&0Z.G!R:6YT9B@B*"5P*69O;S(Z.F9O M;S(H*3M<;B(L("AV;VED*BET:&ES*3L-"B @?2 @#0H@( T*("!^9F]O,B@I M('L@<W1D.CIP<FEN=&8H(B@E<"EF;V\R.CI^9F]O,B@I.UQN(BP@*'9O:60J M*71H:7,I.R!]#0I].PT*#0H-"G-T<G5C="!F;V\S('L@#0H@('1Y<&5D968@ M871O;6EC.CIO;F-E7W!T<CQF;V\S/B!O;F-E7W!T<E]T.PT*(" -"B @<W1A M=&EC(&9O;S$Z.F]N8V5?<'1R7W0Z.F1E9FEN95]03T1?="!S7V9O;S$[(" - M"B @#0H@('9O:60@=VAA=&5V97(H*2![#0H@(" @<W1D.CIP<FEN=&8H(B@E M<"EF;V\S.CIW:&%T979E<B@I.UQN(BP@*'9O:60J*71H:7,I.PT*(" @(&9O M;S$Z.F]N8V5?<'1R7W0@;7EF;V\Q*'-?9F]O,2D[#0H@(" @:68@*&UY9F]O M,2D@>PT*(" @(" @;7EF;V\Q+3YW:&%T979E<B@I.PT*(" @('T@( T*(" @ M(&9U;F-A7V9O<E]M=6QT:7!L95]T:')E861S*"D[(" @( T*("!](" -"B @ M#0H@(&9O;S,H*2![( T*(" @('-T871I8R!F;V\R.CIO;F-E7W!T<E]T.CID M969I;F5?4$]$7W0@<U]F;V\R.R @( T*(" @( T*(" @(&9O;S(Z.F]N8V5? M<'1R7W0@;7EF;V\R*'-?9F]O,BDL(&UY9F]O,F$[#0H@(" @:68@*&UY9F]O M,BD@>PT*(" @(" @;7EF;V\R+3YW:&%T979E<B@I.R @(" @(" @(" @(" @ M(" -"B @(" @(&9O;S$Z.F]N8V5?<'1R7W0@;7EF;V\Q*'-?9F]O,2D[#0H@ M(" @("!M>69O;S)A(#T@;7EF;V\R.PT*(" @(" @:68@*&UY9F]O,2D@>PT* M(" @(" @("!M>69O;S$M/G=H871E=F5R*"D[#0H@(" @("!](" @(" @(" @ M(" @(" @(" @#0H@(" @("!M>69O;S)A+3YW:&%T979E<B@I.PT*(" @('T@ M(" @#0H-"B @("!S=&0Z.G!R:6YT9B@B*"5P*69O;S,Z.F9O;S,H*3M<;B(L M("AV;VED*BET:&ES*3L-"B @?2 @#0H@( T*("!^9F]O,R@I('L@#0H@(" @ M=VAA=&5V97(H*3L-"B @("!S=&0Z.G!R:6YT9B@B*"5P*69O;S,Z.GYF;V\S M*"D[7&XB+" H=F]I9"HI=&AI<RD[( T*("!]#0I].R @9F]O,3HZ;VYC95]P M=')?=#HZ9&5F:6YE7U!/1%]T(&9O;S,Z.G-?9F]O,3L-"@T*#0IS=&%T:6,@ M=F]I9"!F=6YC8E]F;W)?;75L=&EP;&5?=&AR96%D<R@I('L-"B @9G5N8V%? M9F]R7VUU;'1I<&QE7W1H<F5A9',H*3L@(" @#0H@('-T871I8R!F;V\R.CIO M;F-E7W!T<E]T.CID969I;F5?4$]$7W0@<U]M>69O;S)A.R @(" -"B @9G5N M8V%?9F]R7VUU;'1I<&QE7W1H<F5A9',H*3L-"B @#0H@(&9O;S(Z.F]N8V5? M<'1R7W0@<VUY9F]O82AS7VUY9F]O,F$I+"!S;7EF;V]A83L-"B @:68@*'-M M>69O;V$I('L-"B @("!S;7EF;V]A+3YW:&%T979E<B@I.PT*("!](" @(" @ M( T*(" -"B @>PT*(" @(&9U;F-A7V9O<E]M=6QT:7!L95]T:')E861S*"D[ M#0H@(" @#0H@(" @<W1A=&EC(&9O;S(Z.F]N8V5?<'1R7W0Z.F1E9FEN95]0 M3T1?="!S7VUY9F]O,F([#0H@(" @#0H@(" @9F]O,2!M>69O;S$[(" @(" - M"B @(" -"B @("!F;V\R.CIO;F-E7W!T<E]T('-M>69O;V$H<U]M>69O;S)A M*3L-"B @("!I9B H<VUY9F]O82D@>PT*(" @(" @<VUY9F]O86$@/2!S;7EF M;V]A.PT*(" @(" @<VUY9F]O86$M/G=H871E=F5R*"D[#0H@(" @?2 @(" @ M#0H@(" @(" @(" @(" -"B @("!F;V\R(&UY9F]O,CL@(" -"B @(" @(" @ M(" @(" @#0H@(" @>PT*(" @(" @;7EF;V\Q+G=H871E=F5R*"D[#0H@(" @ M(" -"B @(" @(&9O;S(Z.F]N8V5?<'1R7W0@<VUY9F]O,BAS7VUY9F]O,F$I M.PT*(" @(" @:68@*'-M>69O;S(I('L-"B @(" @(" @<VUY9F]O,BT^=VAA M=&5V97(H*3L-"B @(" @('T@(" @(" -"B @(" @( T*(" @(" @;7EF;V\R M+G=H871E=F5R*"D[(" @( T*(" @(" @#0H@(" @("!F=6YC85]F;W)?;75L M=&EP;&5?=&AR96%D<R@I.R @(" -"B @(" @(" @(" @(" @(" @(" @(" @ M( T*(" @(" @>PT*(" @(" @("!F;V\S(&UY9F]O,SL-"B @(" @(" @;7EF M;V\S+G=H871E=F5R*"D[(" @( T*(" @(" @(" @(" @#0H@(" @(" @(&9O M;S(@;7EF;V\R.PT*(" @(" @("!M>69O;S(N=VAA=&5V97(H*3L@(" @(" @ M(" -"B @(" @('T-"B @(" @( T*(" @(" @;7EF;V\R+G=H871E=F5R*"D[ M(" @(" @( T*(" @('T-"B @(" -"B @("!F;V\S(&UY9F]O,SL-"B @(" - M"B @("!F;V\R.CIO;F-E7W!T<E]T('-M>69O;S(H<U]M>69O;S)B*3L-"B @ M("!I9B H<VUY9F]O,BD@>PT*(" @(" @<VUY9F]O,BT^=VAA=&5V97(H*3L- M"B @(" @(&9U;F-A7V9O<E]M=6QT:7!L95]T:')E861S*"D[#0H@(" @("!M M>69O;S,N=VAA=&5V97(H*3L@( T*(" @('T@( T*(" @( T*(" @(&9U;F-A M7V9O<E]M=6QT:7!L95]T:')E861S*"D[#0H@('T-"B @#0H@(&EF("AS;7EF M;V]A82D@>PT*(" @('-M>69O;V%A+3YW:&%T979E<B@I.PT*("!](" @(" @ M#0H@( T*("!F=6YC85]F;W)?;75L=&EP;&5?=&AR96%D<R@I.PT*?0T*#0H- M"FEN="!M86EN*&EN="!A<F=C+"!C:&%R("IA<F=V6UTI#0I[#0H@(&9U;F-A M7V9O<E]M=6QT:7!L95]T:')E861S*"D[(" @( T*("!F=6YC8E]F;W)?;75L M=&EP;&5?=&AR96%D<R@I.PT*("!F=6YC85]F;W)?;75L=&EP;&5?=&AR96%D M<R@I.R @(" -"B @9G5N8V)?9F]R7VUU;'1I<&QE7W1H<F5A9',H*3L@( T* 6#0H@(')E='5R;B P.PT*?0T*#0H-"@`` ` end

Okay... The last algorithm I posted in this thread actually works. It shows how to do atomically thread-safe reference counted static initialization of c++ objects which adhere to a thread-safety level of strong. I am almost finished creating a technical white paper which will give a fairly detailed description of every aspect of my algorithm, and the implementation details of a fully working prototype. After reading it, you should have no problem implementing and experimenting with my algorithm for yourself. The basic concept of my technique is, IMHO, very straightforward and fairly simple to understand: -- Correct multi-threaded referenced counted static initialization of C++ objects can be realized through the interface of a smart pointer which wraps a low-level API that uses atomic operations to access the contents of a statically initialized POD. The paper and library will be posted today or tomorrow. I think I have a good solution to this particular "C++ problem". IMHO, my static initialization algorithm works well with and could be an asset to the Boost Library. Therefore, I am really looking forward to, and will be very interested in, reading any comments/critiques/suggestions on my technique the Boost community may have. Thank you all for your time, Chris Thomasson

Here is a very rough draft of a paper I am creating that describes how my static initialization algorithms is implemented. The document is certinaly not incomplete, however I thought that I should try to gather any feedback I can before I go ahead and move the paper out of the "rough draft " stage: http://appcore.home.comcast.net/vzdoc/atomic/static-init/ I am look forward to reading any comment you may have. Thank you.

"Chris Thomasson" <cristom@comcast.net> wrote in message news:eiu626$sgo$1@sea.gmane.org... [...]
[...] I have not forgot about this paper. I got sidetracked with some other business that involves $... ;^) So, I will definitely let you know when the code that goes with the paper is complete. BTW, I posted the refcounting functions that are missing in the paper, in this thread. Any comments on my technique?
participants (3)
-
Anthony Williams
-
Chris Thomasson
-
Roland Schwarz