
How does call_once work? Does it maintain a separate mutex for each unique function pointer passed in, or is there only one global mutex shared by all calls to call_once? In other words, can call_once(foo, flag) block call_once(bar, flag2)? -Jason

How does call_once work? Does it maintain a separate mutex for each unique function pointer passed in, or is there only one global mutex shared by all calls to call_once? In other words, can call_once(foo, flag) block call_once(bar, flag2)?
The mutex is unique to both the calling process and the call_once flag, but not the function pointer (however there should always be a one to one correspondence between the once-flag and the function pointer). Hope this makes sense, John.

John Maddock wrote:
The mutex is unique to both the calling process and the call_once flag, but not the function pointer (however there should always be a one to one correspondence between the once-flag and the function pointer).
I should probably elaborate on my problem a little bit more. I wanted to create a function that makes a given call atomic, but doesn't enforce that the function be called only once. I thought I could do so by using a once_flag allocated on the stack immediately before invoking call_once, so the flag would always be reinitialized to BOOST_ONCE_INIT and call_once could just offer me it's atomic guarantees, but apparently that won't work. I guess I'll just have to find some place to put my own mutex where it can be constructed safely. -Jason

Jason Hise <chaos@ezequal.com> writes:
John Maddock wrote:
The mutex is unique to both the calling process and the call_once flag, but not the function pointer (however there should always be a one to one correspondence between the once-flag and the function pointer).
I should probably elaborate on my problem a little bit more. I wanted to create a function that makes a given call atomic, but doesn't enforce that the function be called only once. I thought I could do so by using a once_flag allocated on the stack immediately before invoking call_once, so the flag would always be reinitialized to BOOST_ONCE_INIT and call_once could just offer me it's atomic guarantees, but apparently that won't work. I guess I'll just have to find some place to put my own mutex where it can be constructed safely.
Calls through call_once are only atomic with respect to other calls that reference the same once_flag. If you want mutual exclusion, you need a shared mutex --- why not just create a function-local static mutex, lock it before your function call, and unlock it afterwards? Anthony -- Anthony Williams Software Developer Just Software Solutions Ltd http://www.justsoftwaresolutions.co.uk

Anthony Williams wrote:
Calls through call_once are only atomic with respect to other calls that reference the same once_flag. If you want mutual exclusion, you need a shared mutex --- why not just create a function-local static mutex, lock it before your function call, and unlock it afterwards?
Anthony
Because static variables are not initialized in a thread safe manner (except on some versions of gcc which lock the operation). Multiple threads could end up calling the mutex's constructor simultaneously, resulting in a corrupted mutex object. If I have a global mutex, I need to find a way to initialize it before the program starts spawning threads. -Jason

Jason Hise <chaos@ezequal.com> writes:
Anthony Williams wrote:
Calls through call_once are only atomic with respect to other calls that reference the same once_flag. If you want mutual exclusion, you need a shared mutex --- why not just create a function-local static mutex, lock it before your function call, and unlock it afterwards?
Anthony
Because static variables are not initialized in a thread safe manner (except on some versions of gcc which lock the operation). Multiple threads could end up calling the mutex's constructor simultaneously, resulting in a corrupted mutex object. If I have a global mutex, I need to find a way to initialize it before the program starts spawning threads.
You can use call_once to initialize the mutex, or you can use a mutex type that supports static initialization (unlike the current boost ones, but like a pthread_mutex) Anthony -- Anthony Williams Software Developer Just Software Solutions Ltd http://www.justsoftwaresolutions.co.uk

Calls through call_once are only atomic with respect to other calls that reference the same once_flag. If you want mutual exclusion, you need a shared mutex --- why not just create a function-local static mutex, lock it before your function call, and unlock it afterwards?
I hope not! There will be a race condition in the construction of the function-scope static mutex. John.

I think it would be safe to create the mutex as a global variable, with the documented requirement that the program not spawn any threads until after it enters main. Quick question though: Are global variables destroyed only when the last thread exits (ensuring that the point of destruction of all globals is atomic), or do they die when the main thread exits? -Jason

Jason Hise wrote:
Quick question though: Are global variables destroyed only when the last thread exits (ensuring that the point of destruction of all globals is atomic), or do they die when the main thread exits?
There is no single answer to this as this will depend on platform. On windows e.g. it is easily possible to create a thread by the native Win32 API call CreateThread that even will survive when your main has exited and all global cleanup handlers are done (including global destructor calls). When it comes to process exit of course threads still running are simply extinguished (Without any chance to run cleanup handlers, not even the thread exit callback will help there). So it is entirely up to the application programmer when the last thread finishes. If e.g. someone starts a thread from a global constructor (which currently is deemed bad practice), and ended from its destrutor, the time when this thread will actually die is simply unspecified. Regards, Roland

I should probably elaborate on my problem a little bit more. I wanted to create a function that makes a given call atomic, but doesn't enforce that the function be called only once. I thought I could do so by using a once_flag allocated on the stack immediately before invoking call_once, so the flag would always be reinitialized to BOOST_ONCE_INIT and call_once could just offer me it's atomic guarantees, but apparently that won't work. I guess I'll just have to find some place to put my own mutex where it can be constructed safely.
I forgot to mention: 1) I had the same issue in Boost.Regex and wrote a statically initialised mutex as a solution (see boost/regex/pending/static_mutex.hpp), it might make a useful addition to the threading lib, however, I really should rewrite the Win32 implementation to use a mutex so you don't get a priority inversion and busy waiting (see http://www.netrino.com/Publications/Glossary/PriorityInversion.html). 2) It's possible to do what you want with call_once, but it's one of those things you might think is impossible until someone shows you how, and then it's embarrassingly easy, see http://lists.boost.org/Archives/boost/2005/07/90756.php for an explanation. HTH, John.

John Maddock wrote:
It's possible to do what you want with call_once, but it's one of those things you might think is impossible until someone shows you how, and then it's embarrassingly easy, see http://lists.boost.org/Archives/boost/2005/07/90756.php for an explanation.
In that example, where is the once_flag located? I would think that a similar race condition problem would exist when constructing and/or initializing that once_flag with BOOST_ONCE_INIT. -Jason

Jason Hise <chaos@ezequal.com> writes:
John Maddock wrote:
It's possible to do what you want with call_once, but it's one of those things you might think is impossible until someone shows you how, and then it's embarrassingly easy, see http://lists.boost.org/Archives/boost/2005/07/90756.php for an explanation.
In that example, where is the once_flag located? I would think that a similar race condition problem would exist when constructing and/or initializing that once_flag with BOOST_ONCE_INIT.
static objects undergo two-phase init. Firstly, there is static initialization, which is to all-zero, or to a constant initializer. This happens at or before program startup, before any code is run. Then there is dynamic initialization, which is where things like constructors are run, or initialization to the result of a function call. Dynamic initialization is where the race conditions can happen. once_flag is carefully designed so BOOST_ONCE_INIT is suitable for static initialization. Typically it initializes the once_flag to all-zeros. It is possible to design a mutex this way. pthreads mutexes have a static initializer, for example. boost::mutex is not currently designed this way. Anthony -- Anthony Williams Software Developer Just Software Solutions Ltd http://www.justsoftwaresolutions.co.uk
participants (4)
-
Anthony Williams
-
Jason Hise
-
John Maddock
-
Roland Schwarz