
I just committed a new version to CVS; pretty much the last version I posted with void return, but incorporating John Maddock's changes for generating the name so as to not require std::stringstream, or even strstream. This version is header only, in contrast to the older version (and current versions for other platforms), which required a C++ file. This allows the call_once function to be a template. Note, that windows.h *is not included* --- explicit prototypes are included for the required functions, following the lead of boost/detail/interlocked.hpp (which has been updated to include InterlockedExchange). Despite tendering a version that handles non-void returns, I am not sure how to do it right (destruction order for statics makes it hard), so I have left that out for now. Anthony -- Anthony Williams Software Developer

Anthony Williams wrote: [...]
Despite tendering a version that handles non-void returns, I am not sure how to do it right (destruction order for statics makes it hard), so I have left that out for now.
Why not simply make once_flag POD and provide call_once_reset()? Plus convenient (derived) non-POD class for dynamic context? regards, alexander.

Alexander Terekhov <terekhov@web.de> writes:
Anthony Williams wrote: [...]
Despite tendering a version that handles non-void returns, I am not sure how to do it right (destruction order for statics makes it hard), so I have left that out for now.
Why not simply make once_flag POD and provide call_once_reset()? Plus convenient (derived) non-POD class for dynamic context?
That's easy: because I didn't think of it. I can understand what you're getting at with the derived class for dynamic contexts, such as class members, but how are you envisioning the call_once_reset function working? Would it take a specific once_flag as a parameter, and reset it, as if it has never been called? When would this be used, and how would it help with static destruction? Anthony -- Anthony Williams Software Developer

Anthony Williams wrote: [...]
I can understand what you're getting at with the derived class for dynamic contexts, such as class members, but how are you envisioning the call_once_reset function working? Would it take a specific once_flag as a parameter, and reset it, as if it has never been called?
Yes. And also cleanup result stuff, if any. Pretty much the same notion (reset() vs destroy() aside for moment) as with statically initialized pthread primitives and corresponding _destroy() calls like pthread_mutex_destroy() that can be used for "pedantic" cleanup of statically initialized pthread objects at process termination to please leak detectors (resources may be acquired dynamically on first use) or whatnot. See also http://groups.google.ca/group/comp.programming.threads/msg/92e475f3d9f2c93c
When would this be used,
That's up to the client. For example, in some module_fini().
and how would it help with static destruction?
Static destruction of what? regards, alexander.

Alexander Terekhov <terekhov@web.de> writes:
Anthony Williams wrote: [...]
I can understand what you're getting at with the derived class for dynamic contexts, such as class members, but how are you envisioning the call_once_reset function working? Would it take a specific once_flag as a parameter, and reset it, as if it has never been called?
Yes. And also cleanup result stuff, if any. Pretty much the same notion (reset() vs destroy() aside for moment) as with statically initialized pthread primitives and corresponding _destroy() calls like pthread_mutex_destroy() that can be used for "pedantic" cleanup of statically initialized pthread objects at process termination to please leak detectors (resources may be acquired dynamically on first use) or whatnot.
Makes sense.
When would this be used,
That's up to the client. For example, in some module_fini().
and how would it help with static destruction?
Static destruction of what?
Destruction of once_flags with static storage duration. If we store the return value along with the once_flag, then we really ought to destroy it properly, when the once_flag is destroyed. If a once_flag has static storage duration, and has a non-trivial destructor that does cleanup, then we have a classic singleton cleanup problem, as John Maddock pointed out --- the order of static destruction can now be important. Anthony -- Anthony Williams Software Developer Just Software Solutions Ltd http://www.justsoftwaresolutions.co.uk

Anthony Williams wrote: [...]
Destruction of once_flags with static storage duration.
If we store the return value along with the once_flag, then we really ought to destroy it properly, when the once_flag is destroyed. If a once_flag has static storage duration, and has a non-trivial destructor that does cleanup, then we have a classic singleton cleanup problem, as John Maddock pointed out --- the order of static destruction can now be important.
You mean an extra non-POD version? That's not your problem... as long as you provide a POD version + call_once_reset/destroy(). regards, alexander.

Alexander Terekhov wrote:
Anthony Williams wrote: [...]
Destruction of once_flags with static storage duration.
If we store the return value along with the once_flag, then we really ought to destroy it properly, when the once_flag is destroyed. If a once_flag has static storage duration, and has a non-trivial destructor that does cleanup, then we have a classic singleton cleanup problem, as John Maddock pointed out --- the order of static destruction can now be important.
You mean an extra non-POD version? That's not your problem... as long as you provide a POD version + call_once_reset/destroy().
Plus _init(). regards, alexander.

Alexander Terekhov <terekhov@web.de> writes:
Anthony Williams wrote: [...]
Destruction of once_flags with static storage duration.
If we store the return value along with the once_flag, then we really ought to destroy it properly, when the once_flag is destroyed. If a once_flag has static storage duration, and has a non-trivial destructor that does cleanup, then we have a classic singleton cleanup problem, as John Maddock pointed out --- the order of static destruction can now be important.
You mean an extra non-POD version? That's not your problem... as long as you provide a POD version + call_once_reset/destroy().
So, you're suggesting that we store the result with the once_flag, and then leave it up to users to schedule a call to call_once_reset, to ensure the result gets cleared up? Hmm. I'd still like to know how the user can safely schedule the call to call_once_reset, so it doesn't cause problems. I suppose there's an element to which it doesn't really matter; the result will just be reinitialized if there's a subsequent call_once. I don't like leaving things as "not my problem", unless I can see that there is a safe solution. Anthony -- Anthony Williams Software Developer Just Software Solutions Ltd http://www.justsoftwaresolutions.co.uk

If we store the return value along with the once_flag, then we really ought to destroy it properly, when the once_flag is destroyed. If a once_flag has static storage duration, and has a non-trivial destructor that does cleanup, then we have a classic singleton cleanup problem, as John Maddock pointed out --- the order of static destruction can now be important.
You mean an extra non-POD version? That's not your problem... as long as you provide a POD version + call_once_reset/destroy().
So, you're suggesting that we store the result with the once_flag, and then leave it up to users to schedule a call to call_once_reset, to ensure the result gets cleared up?
Hmm.
I'd still like to know how the user can safely schedule the call to call_once_reset, so it doesn't cause problems. I suppose there's an element to which it doesn't really matter; the result will just be reinitialized if there's a subsequent call_once.
I don't like leaving things as "not my problem", unless I can see that there is a safe solution.
Me neither, and I still don't see what having a return value really gains us in this case (I would much rather be able to pass parameters to the call_once procedure, but that's a whole other ball game). If a return value is truly required, then we could use aligned_storage to provide raw storage for the return value and construct the object on first call to the one procedure, but that still doesn't solve the destruction problem. John.

John Maddock wrote: [...]
Me neither, and I still don't see what having a return value really gains us in this case (I would much rather be able to pass parameters to the call_once procedure, but that's a whole other ball game).
Both are useful.
If a return value is truly required, then we could use aligned_storage to provide raw storage for the return value and construct the object on first call to the one procedure, but that still doesn't solve the destruction problem.
That's not your problem as long as you don't destruct and instead provide _reset/destroy/init() calls. regards, alexander.

Alexander Terekhov <terekhov@web.de> writes:
John Maddock wrote:
If a return value is truly required, then we could use aligned_storage to provide raw storage for the return value and construct the object on first call to the one procedure, but that still doesn't solve the destruction problem.
That's not your problem as long as you don't destruct and instead provide _reset/destroy/init() calls.
OK. Lets assume that solving the destruction problem is not the library writer's problem. Assume also that the library supplies a once_flag, a call_once function that returns the same result every time, but only calls the function once, and once_init, once_reset and once_destroy. Each once_flag must be passed to once_destroy in order to clean up the stored return value. As a user of this library, how do I ensure that every once_flag is correctly cleaned up? Anthony -- Anthony Williams Software Developer Just Software Solutions Ltd http://www.justsoftwaresolutions.co.uk

Anthony Williams wrote: [...]
As a user of this library, how do I ensure that every once_flag is correctly cleaned up?
Same answer as to Q) As a user of POSIX, how do you ensure that every statically initializied (and may be even used) pthread_mutex_t or pthread_cond_t object is correctly cleaned up? regards, alexander.

Alexander Terekhov <terekhov@web.de> writes:
Anthony Williams wrote: [...]
As a user of this library, how do I ensure that every once_flag is correctly cleaned up?
Same answer as to
Q) As a user of POSIX, how do you ensure that every statically initializied (and may be even used) pthread_mutex_t or pthread_cond_t object is correctly cleaned up?
I might well let the OS clean up such things on process termination, which is fine (assuming it does so), since these are all OS objects. Is that what you had in mind? However, our once_flag can store a result of a user-defined type, which may therefore require proper destruction, so that it can notify other processes, send network packets, write to a file, or whatever; this therefore needs dealing with somehow before process termination. Anthony -- Anthony Williams Software Developer Just Software Solutions Ltd http://www.justsoftwaresolutions.co.uk

Anthony Williams wrote: [...]
I might well let the OS clean up such things on process termination, which is fine (assuming it does so), since these are all OS objects. Is that what you had in mind?
Not quite. include <pthread.h> pthread_t mutex = PTHREAD_MUTEX_INITIALIZER; int main() { pthread_mutex_destroy(&mutex); } Is OK.
However, our once_flag can store a result of a user-defined type, which may therefore require proper destruction, so that it can notify other processes, send network packets, write to a file, or whatever; this therefore needs dealing with somehow before process termination.
User would then call _destroy() or _reset() (the later is likely to be called multiple times). I won't tell you "how". Trade secret. regards, alexander.

Alexander Terekhov <terekhov@web.de> writes:
Anthony Williams wrote:
However, our once_flag can store a result of a user-defined type, which may therefore require proper destruction, so that it can notify other processes, send network packets, write to a file, or whatever; this therefore needs dealing with somehow before process termination.
User would then call _destroy() or _reset() (the later is likely to be called multiple times). I won't tell you "how". Trade secret.
Don't be such a tease! class X { public X() { acquire_resource(); } void do_something(); ~X() { release_resource(); } }; X get_X(); void f() { static once_flag once; call_once(get_X,once).do_something(); // how do we clean up the X associated with "once", and release it's // resource? // We can't sensibly call once_destroy() here. } Anthony -- Anthony Williams Software Developer Just Software Solutions Ltd http://www.justsoftwaresolutions.co.uk

Anthony Williams wrote:
Alexander Terekhov <terekhov@web.de> writes:
Anthony Williams wrote:
However, our once_flag can store a result of a user-defined type, which may therefore require proper destruction, so that it can notify other processes, send network packets, write to a file, or whatever; this therefore needs dealing with somehow before process termination.
User would then call _destroy() or _reset() (the later is likely to be called multiple times). I won't tell you "how". Trade secret.
Don't be such a tease!
class X { public X() { acquire_resource(); } void do_something(); ~X() { release_resource(); } };
X get_X();
void f() { static once_flag once; call_once(get_X,once).do_something(); // how do we clean up the X associated with "once", and release it's // resource? // We can't sensibly call once_destroy() here. }
I have written a static_<...> template class that uses boost::call_once to (I suppose) offer thread-safe initialization of what amounts to static data. Use case: void f() { static_<X> x; } This seems equal in purpose to what you're now seeking to offer with the new call_once. Here's the abbreviated implementation of static_, template < ... > struct static_ { static_() { struct constructor { static void construct() { new (get_address()) value_type(); // Schedule the object for destruction static destructor d; } }; // Ensure constructor is called only once boost::call_once(&constructor::construct, constructed_); } private: static pointer get_address() { return static_cast<pointer>(data_.address()); } struct destructor { ~destructor() { get_address()->~value_type(); } }; // static storage for object and flag static boost::aligned_storage< ... > data_; static boost::once_flag constructed_; }; I'd like to hear your views on the flaws and shortcomings with this approach. Regards, João

John Maddock wrote:
Me neither, and I still don't see what having a return value really gains us in this case (I would much rather be able to pass parameters to the call_once procedure, but that's a whole other ball game).
With a call_once taking a function object, you can use boost::bind for parameter passing. I can see one immediate use for a return value, instead of X * px; void init_px(); int main() { call_once( init_px ); // use px } one might use X * init_px(); int main() { X * px = call_once( init_px ); // use px } (and similarly with a reference.) I can't think of a compelling example that returns an X by value.

Anthony Williams schrieb:
I just committed a new version to CVS; pretty much the last version I posted with void return, but incorporating John Maddock's changes for generating the name so as to not require std::stringstream, or even strstream.
Just one observation: The name may be prepended by Global\ or Local\ or Session\. This is not true for < NT4.0. However as I understand the documentation, when omitting these leadin parts, the default can depend on the platform. I.e. on Terminal Services the default is the Global space. I am not sure if this observation is of relevance though. Regards, Roland

Roland Schwarz <roland.schwarz@chello.at> writes:
Anthony Williams schrieb:
I just committed a new version to CVS; pretty much the last version I posted with void return, but incorporating John Maddock's changes for generating the name so as to not require std::stringstream, or even strstream.
Just one observation: The name may be prepended by Global\ or Local\ or Session\. This is not true for < NT4.0. However as I understand the documentation, when omitting these leadin parts, the default can depend on the platform. I.e. on Terminal Services the default is the Global space. I am not sure if this observation is of relevance though.
Yes, I know. I deliberately ignored it for now, on the basis that it doesn't affect the implementation, so we can easily change that later when we've decided what we want the prefix to be (if any). Anthony -- Anthony Williams Software Developer Just Software Solutions Ltd http://www.justsoftwaresolutions.co.uk

I have come across another problem: You might try to CloseHandle on the mutex while another thread is still waiting on this. While the documentation is explicit about a dying thread that did not realease a mutex he had a hold on (it is abandoned then), I could nothing find for the other case. Any ideas? Regards, Roland

Roland Schwarz <roland.schwarz@chello.at> writes:
I have come across another problem:
You might try to CloseHandle on the mutex while another thread is still waiting on this. While the documentation is explicit about a dying thread that did not realease a mutex he had a hold on (it is abandoned then), I could nothing find for the other case.
Any ideas?
Each thread gets its own handle from the call to CreateMutex, which it then must close, so this is not a problem. Anthony -- Anthony Williams Software Developer Just Software Solutions Ltd http://www.justsoftwaresolutions.co.uk

Roland Schwarz schrieb:
While the documentation is explicit about a dying thread that did not realease a mutex he had a hold on (it is abandoned then), I could nothing find for the other case.
The situation unfortunately is even worse: Even if the WaitFor SingleObject would return, this would result in an attempt to close the handle twice. The documentation is explicit on this, stating that this will raise an exception. One way to get around this could be the use of reference counting.... Regards, Roland

I have come across another problem:
You might try to CloseHandle on the mutex while another thread is still waiting on this. While the documentation is explicit about a dying thread that did not realease a mutex he had a hold on (it is abandoned then), I could nothing find for the other case.
Any ideas?
I don't think that's a problem, if the other thread is waiting on the mutex then it has it's own handle to the mutex open, so the mutex doesn't actually get deleted as a system object until the last waiting thread has completed it's wait and closed it's own handle. John.
participants (6)
-
Alexander Terekhov
-
Anthony Williams
-
Joao Abecasis
-
John Maddock
-
Peter Dimov
-
Roland Schwarz