- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hello,
If I have a function with a static mutex:
void func()
{
if (some condition)
{
static tbb:spin_mutex mutex;
tbb::spin_mutex::scoped_lock lock(mutex);
// the rest of the if clause should now be protected
}
}
is the initialisation of the static mutex above thread-safe?
Is it also consistent, ie. would a thread be able to enter the clause while another thread is still busy initialising the mutex for the first time?
Thank you,
manuel
If I have a function with a static mutex:
void func()
{
if (some condition)
{
static tbb:spin_mutex mutex;
tbb::spin_mutex::scoped_lock lock(mutex);
// the rest of the if clause should now be protected
}
}
is the initialisation of the static mutex above thread-safe?
Is it also consistent, ie. would a thread be able to enter the clause while another thread is still busy initialising the mutex for the first time?
Thank you,
manuel
Link Copied
12 Replies
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I don't see the distinction between thread-safe and consistent, but I'd say yes to both.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
In the case of parallel programs, statics are just plain nasty, try to avoid them.
If this is something you think you need then you may need to look at your design again and alter it so this wouldn't be required.
I'd have thought that a non static, but public mutex in a class that is used by methods in that class would be more suitable.
If this is something you think you need then you may need to look at your design again and alter it so this wouldn't be required.
I'd have thought that a non static, but public mutex in a class that is used by methods in that class would be more suitable.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Unmotivated big absolute statements are just plain nasty, try to avoid them.
(Added) Or in other words: would you care to elaborate?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The initialization of the static object should occur at
a) compile time when the object is POD
b) at program load time when the static class ctor's are called
IOW initialization of the static objects are complete prior to the call to main(), and thus prior to the instantiation of the thread pool.
Note, your func would protect the code in func, but not necessarilyprotect an arbitrary object referenced by the protected code in func.
You have to determine if it is necessary to protect the code, the object, or both. Then place the mutex accordingly.
For example, should you have
void YourLinkedList::InsertIntoList(Node* node);
void YourLinkedList::DeleteFromList(Node* node);
And should each function have a private (static)mutex then...
While you could not have multiple concurrent insert's OR multiple concurrent delete's
You could have a concurrent insert and delete (which could break your list).
Look carefully at your mutex requirements to assure what you are protecting is what you need protecting.
Jim Dempsey
a) compile time when the object is POD
b) at program load time when the static class ctor's are called
IOW initialization of the static objects are complete prior to the call to main(), and thus prior to the instantiation of the thread pool.
Note, your func would protect the code in func, but not necessarilyprotect an arbitrary object referenced by the protected code in func.
You have to determine if it is necessary to protect the code, the object, or both. Then place the mutex accordingly.
For example, should you have
void YourLinkedList::InsertIntoList(Node* node);
void YourLinkedList::DeleteFromList(Node* node);
And should each function have a private (static)mutex then...
While you could not have multiple concurrent insert's OR multiple concurrent delete's
You could have a concurrent insert and delete (which could break your list).
Look carefully at your mutex requirements to assure what you are protecting is what you need protecting.
Jim Dempsey
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
A correction is in order...Things are different for local static objects (6.7 Declaration statement [stmt.dcl]) than for non-local objects (3.6.2 Initialization of non-local objects [basic.start.init]): these objects are not (seen to be) initialised before main(). In a multithreaded environment it's a matter of what the ABI provides (until C++0x becomes effective), and what it does islock a mutexrelated to the initialisation of the object, because this initialisation can indeed occur after the thread pool has been created.
(I did not want to assume anything about the propriety or not of a function-local mutex without specific knowledge about its purpose, but of course I agree with Jim's predicated recommendations in that regard.)
(I did not want to assume anything about the propriety or not of a function-local mutex without specific knowledge about its purpose, but of course I agree with Jim's predicated recommendations in that regard.)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I concur with Jim's and Raf's comments.In the rarecase that afunction-local mutex is approprate, and you do not have C++0x guarantees on initialization of thread-local objects, there is a TBB hack that should work.Be warned that it relies onthe undocumenteddetail that the internal implementation of spin_mutex's constructorzero-initializes it. Though this detail could change in the future, I'd be amazed if it did.
Here is thecode:
The aligned_space object will be zero-initialized at compile time, thus avoiding a race to initialize it. This trick also works with the current implementation of spin_rw_mutex.cpp. Indeed we use the hack in src/tbb/observer_proxy.cpp.
Ordinarily, I do not like to use hacks that depend on undocumented features. However, the stated problem is a tough nut to crack without thread-safe initialization of function-local static variables.
A mutex that is safe to use in these circumstances and uses only documented TBB features is possible to build. Use a tbb::atomic<:BOOL> and compare_and_swap. To reduce busy-waiting contention, a thread should execute std::this_thread::yield() [defined in "/tbb/compat/thread"] after each failed compare_and_swap.
Here is thecode:
[cpp]#include "tbb/spin_mutex.h" #include "tbb/aligned_space.h" void func() { if( some_condition ) { static tbb::aligned_space<:SPIN_MUTEX> mutex; tbb::spin_mutex::scoped_lock( *mutex.begin() ); // the rest of the if clause is now protected } } [/cpp]
The aligned_space object will be zero-initialized at compile time, thus avoiding a race to initialize it. This trick also works with the current implementation of spin_rw_mutex.cpp. Indeed we use the hack in src/tbb/observer_proxy.cpp.
Ordinarily, I do not like to use hacks that depend on undocumented features. However, the stated problem is a tough nut to crack without thread-safe initialization of function-local static variables.
A mutex that is safe to use in these circumstances and uses only documented TBB features is possible to build. Use a tbb::atomic<:BOOL> and compare_and_swap. To reduce busy-waiting contention, a thread should execute std::this_thread::yield() [defined in "/tbb/compat/thread"] after each failed compare_and_swap.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I concur with Jim's and Raf's comments.In the rarecase that afunction-local mutex is approprate, and you do not have C++0x guarantees on initialization of thread-local objects, there is a TBB hack that should work.Be warned that it relies onthe undocumenteddetail that the internal implementation of spin_mutex's constructorzero-initializes it. Though this detail could change in the future, I'd be amazed if it did.
Here is thecode:
The aligned_space object will be zero-initialized at compile time, thus avoiding a race to initialize it. This trick also works with the current implementation of spin_rw_mutex.cpp. Indeed we use the hack in src/tbb/observer_proxy.cpp.
Ordinarily, I do not like to use hacks that depend on undocumented features. However, the stated problem is a tough nut to crack without thread-safe initialization of function-local static variables.
A mutex that is safe to use in these circumstances and uses only documented TBB features is possible to build. Use a tbb::atomic<:BOOL> and compare_and_swap. To reduce busy-waiting contention, a thread should execute std::this_thread::yield() [defined in "/tbb/compat/thread"] after each failed compare_and_swap.
Here is thecode:
[cpp]#include "tbb/spin_mutex.h" #include "tbb/aligned_space.h" void func() { if( some_condition ) { static tbb::aligned_space<:SPIN_MUTEX> mutex; tbb::spin_mutex::scoped_lock( *mutex.begin() ); // the rest of the if clause is now protected } } [/cpp]
The aligned_space object will be zero-initialized at compile time, thus avoiding a race to initialize it. This trick also works with the current implementation of spin_rw_mutex.cpp. Indeed we use the hack in src/tbb/observer_proxy.cpp.
Ordinarily, I do not like to use hacks that depend on undocumented features. However, the stated problem is a tough nut to crack without thread-safe initialization of function-local static variables.
A mutex that is safe to use in these circumstances and uses only documented TBB features is possible to build. Use a tbb::atomic<:BOOL> and compare_and_swap. To reduce busy-waiting contention, a thread should execute std::this_thread::yield() [defined in "/tbb/compat/thread"] after each failed compare_and_swap.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I think that aligned_space is superfluous in this case, as any static object is guaranteed to be statically zero-initialized.
Actually most of the modern compilers ensure correct one-time initialization of local static objects, but this obviously comes with cost, which certainly makes their usage less efficient in comparison with global static ones.
Actually most of the modern compilers ensure correct one-time initialization of local static objects, but this obviously comes with cost, which certainly makes their usage less efficient in comparison with global static ones.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The point of the aligned_space is to prevent the constructor of spin_mutex from running after some thread has already acquired a lock on the spin_mutex, in which case the zeroing would cause the lock to be spuriously released. Of course this is only an issue for compilers that do not do thread-safe initialization of local-static objects.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Is it really more than just a test of a status field on x86, which implicitly orders loads and stores, with initialisation-time cost amortised to zero? Other architectures may notice a bump in the road each time they have to explicitly load-acquire the status field. Or did I overlook something?
And what modern compilers do not support it?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
There is a way to avoid the load-acquire on the common path. See appendix in http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2382.html .
I can't speak for current compilers. I remember implementing thread-safe function-local static initializations in the KAI C++ compiler back in the 1990s :-)
I can't speak for current compilers. I remember implementing thread-safe function-local static initializations in the KAI C++ compiler back in the 1990s :-)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thanks, some light bed-time reading... :-)
Althoughwith that declaration I'm a bit surprised that you were also the one who proposed a solution that is only needed with compilers obviously at leastsomething like 15years behind the state of the art (wasn't there an ice age somewhere between then and now in IT time?).
Althoughwith that declaration I'm a bit surprised that you were also the one who proposed a solution that is only needed with compilers obviously at leastsomething like 15years behind the state of the art (wasn't there an ice age somewhere between then and now in IT time?).

Reply
Topic Options
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page