开发者

When can a cond var be used to synchronize its own destruction/unmapping?

开发者 https://www.devze.com 2023-04-10 00:12 出处:网络
According to POSIX, It shall be safe to destroy an initialized condition variable upon which no threads are currently blocked.

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:

  1. Immediately after a successful signal, in either the waiting or the signaling thread, when exactly one thread is blocked on the cond var.
  2. 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 return EBUSY 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 return EBUSY if there are threads still blocked on the condition. Threads that are signaled but not unblocked will be executed before pthread_cond_destroy() returns control to the application.

Safe but blocking

  • Darwin libc-391 - pthread_cond_destroy() will return EBUSY 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 return EBUSY 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. Thus pthread_cond_broadcast() must block until all threads have woken (but not released their mutex).
  • various win32 implementations - Many rely on the PulseEvent() function to implement pthread_cond_broadcast(). This has a known race condition

Oddballs

  • OSKit 0.9 - Safe but returns EINVAL if pthread_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()

0

精彩评论

暂无评论...
验证码 换一张
取 消