[boost.thread] destruction of condition variables
Hello.
Is it feasible to make CVs' destructors thread-safe? Consider the code
below. It causes a failed assertion in
basic_condition_variable::dispose_entry(...) when cv's destructor kicks in
while notify_all() is still cleaning up.
The problem is easily resolved by adding another mutex, but it would just
look nicer if we could rely on cv's own features for that.
Another alternative would be to make wait() return only after notify_xxx()
has really finished its job.
(Yes, I guess this code is not exactly a Good Practice :) , but it's not
exactly logically inconsistent either. Or is it?)
[
Boost 1.35.0, MSVC2008, Win32 Debug configuration
Actually, it's not easy to make this particular code fail; adding a short
sleep() in the beginning of dispose_entry(...) helps. This problem managed
to appear quite systematically in a more complex program though.
]
----
Vladimir
:::CODE:::
#include
"Vladimir Pozdyaev"
Hello.Is it feasible to make CVs' destructors thread-safe? Consider the code below. It causes a failed assertion in basic_condition_variable::dispose_entry(...) when cv's destructor kicks in while notify_all() is still cleaning up. The problem is easily resolved by adding another mutex, but it would just look nicer if we could rely on cv's own features for that.Another alternative would be to make wait() return only after notify_xxx() has really finished its job. (Yes, I guess this code is not exactly a Good Practice :) , but it's not exactly logically inconsistent either. Or is it?)[Boost 1.35.0, MSVC2008, Win32 Debug configurationActually, it's not easy to make this particular code fail; adding a short sleep() in the beginning of dispose_entry(...) helps. This problem managed to appear quite systematically in a more complex program though.
First off, Boost 1.35.0 condition variables are broken on Windows. Please update from subversion trunk or release branch. However, it is never safe to destroy any object whilst another thread might still be in a member function. If you use your mutex to protect the notify too, everything will be fine. Anthony -- Anthony Williams | Just Software Solutions Ltd Custom Software Development | http://www.justsoftwaresolutions.co.uk Registered in England, Company Number 5478976. Registered Office: 15 Carrallack Mews, St Just, Cornwall, TR19 7UL
Anthony Williams:
However, it is never safe to destroy any object whilst another thread might still be in a member function.
I believe that this is a "POSIX destruction safety" issue. POSIX CVs are supposed to support such code.
If you use your mutex to protect the notify too, everything will be fine.
I think that if the mutex doesn't offer "POSIX destruction safety", the code can still fail. Thread A may be unblocked and destroy the mutex while thread B is still in mutex::unlock. http://lists.boost.org/Archives/boost/2004/07/68477.php
"Peter Dimov"
Anthony Williams:
However, it is never safe to destroy any object whilst another thread might still be in a member function.
I believe that this is a "POSIX destruction safety" issue. POSIX CVs are supposed to support such code.
POSIX CVs are supposed to support broadcast immediately before destroy. What we have here is the waiting thread doing the destruction, which is not guaranteed safe by POSIX, as far as I know.
If you use your mutex to protect the notify too, everything will be fine.
I think that if the mutex doesn't offer "POSIX destruction safety", the code can still fail. Thread A may be unblocked and destroy the mutex while thread B is still in mutex::unlock.
Hmm. You're probably right. If the notify was protected by the mutex this would require that the notifying thread was still in the unlock even when the waiting thread had woken, acquired the lock and released the lock. Though this is unlikely, it could happen. Ouch. Anthony -- Anthony Williams | Just Software Solutions Ltd Custom Software Development | http://www.justsoftwaresolutions.co.uk Registered in England, Company Number 5478976. Registered Office: 15 Carrallack Mews, St Just, Cornwall, TR19 7UL
On Jul 25, 2008, at 12:11 PM, Anthony Williams wrote:
"Peter Dimov"
writes: Anthony Williams:
However, it is never safe to destroy any object whilst another thread might still be in a member function.
I believe that this is a "POSIX destruction safety" issue. POSIX CVs are supposed to support such code.
POSIX CVs are supposed to support broadcast immediately before destroy. What we have here is the waiting thread doing the destruction, which is not guaranteed safe by POSIX, as far as I know.
If you use your mutex to protect the notify too, everything will be fine.
I think that if the mutex doesn't offer "POSIX destruction safety", the code can still fail. Thread A may be unblocked and destroy the mutex while thread B is still in mutex::unlock.
Hmm. You're probably right. If the notify was protected by the mutex this would require that the notifying thread was still in the unlock even when the waiting thread had woken, acquired the lock and released the lock. Though this is unlikely, it could happen. Ouch.
I believe if this were to happen then the mutex would be generally unusable even without considering condition variables. We have this: A B m.lock(); ... ... ... m.unlock(); m.lock(); ... m.unlock(); ... m.~mutex(); If after unlocking a mutex, and knowing by design that no one else is trying to lock it, you're still not able to safely destruct it, then you're really stuck with a bad mutex. The only way you could safely destruct it is by signaling the destructing thread that all other threads had passed a certain point. Such a signal would need a condition variable, and thus another mutex... catch 22! :-) ... One / has/ to be able to safely destruct a mutex after unlocking it. Or have I misunderstood the concern? -Howard
Howard Hinnant:
I believe if this were to happen then the mutex would be generally unusable even without considering condition variables. We have this:
A B m.lock(); ... ... ... m.unlock(); m.lock(); ... m.unlock(); ... m.~mutex();
If after unlocking a mutex, and knowing by design that no one else is trying to lock it, you're still not able to safely destruct it, then you're really stuck with a bad mutex. The only way you could safely destruct it is by signaling the destructing thread that all other threads had passed a certain point. Such a signal would need a condition variable, and thus another mutex... catch 22! :-) ... One / has/ to be able to safely destruct a mutex after unlocking it.
Yep. But this is not easy to get right. Looking at http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2406.html#shared_mu... and using its exclusive part as an example, thread A does: void shared_mutex::unlock() { { scoped_lock<mutex> _(mut_); state_ = 0; } gate1_.notify_all(); } If it's preempted right after mut_.unlock, thread B can go ahead with lock/unlock/destroy, destroying gate1_. Thread A then resumes and crashes. Unless I'm missing something. (FWIW, most of my mutex implementations have the same problem, the above is just the first mutex implementation link I found with a quick Google.)
On Jul 25, 2008, at 1:37 PM, Peter Dimov wrote:
Howard Hinnant:
I believe if this were to happen then the mutex would be generally unusable even without considering condition variables. We have this:
A B m.lock(); ... ... ... m.unlock(); m.lock(); ... m.unlock(); ... m.~mutex();
If after unlocking a mutex, and knowing by design that no one else is trying to lock it, you're still not able to safely destruct it, then you're really stuck with a bad mutex. The only way you could safely destruct it is by signaling the destructing thread that all other threads had passed a certain point. Such a signal would need a condition variable, and thus another mutex... catch 22! :-) ... One / has/ to be able to safely destruct a mutex after unlocking it.
Yep. But this is not easy to get right. Looking at
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2406.html#shared_mu...
and using its exclusive part as an example, thread A does:
void shared_mutex::unlock() { { scoped_lock<mutex> _(mut_); state_ = 0; } gate1_.notify_all(); }
If it's preempted right after mut_.unlock, thread B can go ahead with lock/unlock/destroy, destroying gate1_. Thread A then resumes and crashes. Unless I'm missing something. (FWIW, most of my mutex implementations have the same problem, the above is just the first mutex implementation link I found with a quick Google.)
Ouch! Cut to the quick! :-) Bug report gratefully accepted. :-) -Howard
Anthony Williams wrote:
"Vladimir Pozdyaev"
writes: (...)
First off, Boost 1.35.0 condition variables are broken on Windows. Please update from subversion trunk or release branch.
Hello, I'm using some boost::condition with 1.34.1 on windows - is that also broken? (And what is broken anyways?) thanks! - Martin
Martin Trappel <0xCDCDCDCD@gmx.at> writes:
I'm using some boost::condition with 1.34.1 on windows - is that also broken? (And what is broken anyways?)
To the best of my knowledge, 1.34.1 is not broken. "Broken" in this case means that signals may get lost, so even if there is a thread waiting, notify_one() may not wake it. Anthony -- Anthony Williams | Just Software Solutions Ltd Custom Software Development | http://www.justsoftwaresolutions.co.uk Registered in England, Company Number 5478976. Registered Office: 15 Carrallack Mews, St Just, Cornwall, TR19 7UL
participants (5)
-
Anthony Williams
-
Howard Hinnant
-
Martin Trappel
-
Peter Dimov
-
Vladimir Pozdyaev