When can a cond var be used to synchronize its own destruction/unmapping?
According to POSIX,
It shall be safe to destroy an initialized condition variable upon which no threads are currently blocked.
Further, the signal and broadcast operations are specified to unblock one/all threads blocked on the condition variable.
Thus, it seems to me the following forms of self-synchronized destruction should be valid, i.e. calling pthread_cond_destroy
:
- Immediately after a successful signal, in either the waiting or the signaling thread, when exactly one thread is blocked on the cond var.
- Immediately after a successful broadcast, in either any waiting thread or the broadcasting thread.
Of course this assumes no further waiters will arrive and no further signals shall be performed afterwards, which the application is responsible for guaranteeing if using pthread_cond_destroy
.
Am I correct that destruction is valid in these situations? And are there other self-synchronized destruction scenarios to be aware of with condition variables?
Finally, for process-shared cond vars where unmapping the shared mapping without destruction might make sense, is it reasonable to expect unmapping to be valid in the same contexts destruction would be valid, or must further synchronization be performed if multiple threads in the same process (address space) are using the same mapping开发者_如何学运维 and want to unmap it in one of the above contexts?
No, I don't think that most of your assumptions are correct. Returning from pthread_cond_signal
or pthread_cond_broadcast
does not indicate that any of the threads are yet "unblocked" from the condition variable, i.e that the threads that are to be unblocked don't need access to that variable anymore. The standard only says "shall unblock" and not "on successful return from this call they will be unblocked". The later would be very restrictive for implementations, so there is probably a good reason that this is formulated as it is.
So I think from the scenarios you describe only one is valid, namely the case were the solely blocked thread or process destroys the condition after being woken up.
Comment (not answer):
Is that what you have in mind?
Global:
// protected by m: pthread_mutex_t m; pthread_cond_t c; bool about_to_pthread_cond_wait = false; bool condition_waited_on = false;
Thread A :
pthread_mutex_lock (&m); { // locked region about_to_pthread_cond_wait = true; while (condition_waited_on) { // pthread_cond_wait (&m, &c) is decomposed here: __pthread_mutex_cond_wait_then_unlock (&m, &c); // unlocked region pthread_mutex_lock (&m); } } pthread_mutex_unlock (&m);
Thread B:
for (bool break_loop = false; !break_loop;) { pthread_mutex_lock (&m); { // locked region condition_waited_on = true; if (about_to_pthread_cond_wait) { pthread_cond_signal (&c); pthread_cond_destroy (&c); break_loop = true; } } pthread_mutex_unlock (&m); pthread_yield (); }
EDIT:
I assume pthread_cond_wait (&m, &c);
does:
__pthread_mutex_cond_wait_then_unlock (&m, &c); pthread_mutex_lock (&m);
__pthread_mutex_cond_wait_then_unlock
will being monitoring the signals to the CV, then unlock the mutex, then go to sleep.
If the CV has an internal mutex that __pthread_mutex_cond_wait_then_unlock
and pthread_cond_signal
must lock internaly, then I assume __pthread_mutex_cond_wait_then_unlock
does:
pthread_mutex_lock (&c.int_mutex); { // locked region for c.int_state __register_wakeup_cond (&c.int_state); pthread_mutex_unlock (&m); } pthread_mutex_unlock (&c.int_mutex); // will not touch c.int_state any more __sleep_until_registered_wakeups ();
and pthread_cond_signal
does:
pthread_mutex_lock (&c.int_mutex); { // locked region for CV internal state __wakeup_registered (&c.int_state); } pthread_mutex_unlock (&c.int_mutex);
Then pthread_cond_destroy
will only be called after __pthread_mutex_cond_wait_then_unlock
has finished using c.int_state
.
While I agree with your interpretation (and that of the Open POSIX test suite) of this language, usage will be implementation dependent. As such, here is a quick rundown of some of the major implementations:
Safe
- nptl -
pthread_cond_destroy()
will returnEBUSY
if there are threads still blocked on the condition. Destruction responsibility will pass to the threads that are signaled by not unblocked. - pthread-win32 MSVC -
pthread_cond_destroy()
will returnEBUSY
if there are threads still blocked on the condition. Threads that are signaled but not unblocked will be executed beforepthread_cond_destroy()
returns control to the application.
Safe but blocking
- Darwin libc-391 -
pthread_cond_destroy()
will returnEBUSY
if there are threads still blocked on the condition. No provisions are made for blocked but signaled threads. - dietlibc 0.27 -
pthread_cond_destroy()
will returnEBUSY
if there are threads still blocked on the condition. No provisions are made for blocked but signaled threads.
Possibly not safe
- Android - Depends on system implementation of
__futex_wake_ex
to be synchronous. Thuspthread_cond_broadcast()
must block until all threads have woken (but not released their mutex). - various win32 implementations - Many rely on the
PulseEvent()
function to implementpthread_cond_broadcast()
. This has a known race condition
Oddballs
- OSKit 0.9 - Safe but returns
EINVAL
ifpthread_cond_destroy()
is called on a condition variable that is still referenced
Edit
The major issue, if I read your comments correctly is whether the pthread_cond_wait()
can access the condition variable before it returns but after it is unblocked. And the answer is yes. The routine assumes that its arguments will remain valid.
This means that after broadcasting, your thread cannot assume that the condition variable is unused by other threads.
When you call pthread_cond_broadcast()
, you do not acquire the associated mutex lock. While the threads waiting on your condition variable will execute sequentially, each acquiring the associated mutex in series. Because your waiters may block each other, your broadcasting thread may continue executing while waiters are still in pthread_cond_wait()
blocked on the mutex (but not waiting for the condition).
Edit 2
[...]is it reasonable to expect unmapping to be valid in the same contexts destruction would be valid?
I don't think that this is a reasonable expectation based on the reasoning in Edit 1. Additional synchronization would definitely be required if you are precluded from using pthread_cond_destroy()
精彩评论