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

According to POSIX,

It is safe to destroy an initialized condition variable under which nothing is currently blocked.

Further, operations with the signal and broadcasting are set to unblock one / all flows blocked in the condition variable.

Thus, it seems to me that the following forms of self-synchronized destruction should be valid, i.e. call pthread_cond_destroy :

  • Immediately after a successful signal, either in the waiting or in the signal stream, when exactly one stream is blocked on cond var.
  • Immediately after a successful broadcast in any pending stream or broadcast stream.

Of course, this assumes that further waiters will not arrive, and after that subsequent signals will not be executed, that the application is responsible for ensuring the use of pthread_cond_destroy .

Is extermination valid in these situations? Are there other self-synchronized destruction scenarios that you need to know with state variables?

Finally, for cond vars shared by processes, where it might make sense to untie collaborative matching without breaking, is it reasonable to expect unmapping to be valid in the same contexts that the kill will be valid, or further synchronization is required if multiple threads are in the same process (address space) uses the same mapping and wants to cancel it in one of the above contexts?

+6
source share
3 answers

No, I don’t think most of your assumptions are true. Returning from pthread_cond_signal or pthread_cond_broadcast does not mean that none of the threads has yet been "unlocked" from the condition variable, i.e. That the threads to be unlocked no longer need access to this variable. The standard says only "unlock", and not "if you successfully return from this conversation, they will be unlocked." Later it will be very restrictive for implementations, so there is probably a good reason that it is worded as it is.

Therefore, I think that of the scenarios that you describe, only one is valid, namely, the case was only a blocked thread or the process destroys the condition after it was woken up.

+2
source

Although I agree with your interpretation (and the Open POSIX version) of this language, the use will be implementation dependent. So here is a summary of some of the main implementations:

Safe

  • nptl - pthread_cond_destroy() will return EBUSY if there are threads in this state. Responsibility for destruction will be transferred to flows that are signaled not unlocked.
  • pthread-win32 MSVC - pthread_cond_destroy() will return EBUSY if there are threads in this state. Threads that are signaled but not unlocked 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 in this state. No provisions are made for blocked but signal flows.
  • dietlibc 0.27 - pthread_cond_destroy() will return EBUSY if there are threads in this state. No provisions are made for blocked but signal flows.

Maybe not safe

  • Android. Depends on the synchronization of the __futex_wake_ex system __futex_wake_ex . Thus, pthread_cond_broadcast() should be blocked until all threads are awake (but not released by their mutex).
  • various implementations of win32 . Many rely on the PulseEvent() function to implement pthread_cond_broadcast() . It has a famous race condition.

Oddballs

  • OSKit 0.9 - safe, but returns EINVAL if pthread_cond_destroy() is called in a condition variable that is still referenced

Edit

The main problem, if I read your comments correctly, is whether pthread_cond_wait() can access the condition variable before it returns, but after it is unlocked. And the answer is yes . The subprogram assumes that its arguments will remain valid.

This means that after the broadcast, your thread cannot assume that the condition variable is not used by other threads.

When you call pthread_cond_broadcast() , you do not get the associated mutex lock. Although threads waiting for a variable of your condition will execute sequentially, each of them will receive the associated mutex sequentially. Since your waiters can block each other, your broadcast stream can continue to run as long as waiters still in pthread_cond_wait() blocked in the mutex (but do not expect conditions).


Edit 2

[...] is it reasonable to expect that unmapping will be valid in the same contexts that destruction will be valid?

I do not think this is a reasonable expectation based on the reasoning in Edit 1. Additional synchronization will certainly be required if you are not allowed to use pthread_cond_destroy()

0
source

Comment (not an answer):

Is that what you mean?

Global:

  // protected by m:
 pthread_mutex_t m;
 pthread_cond_t c;
 bool about_to_pthread_cond_wait = false;
 bool condition_waited_on = false;

Topic 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);

Theme 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 track the signals in the CV, then unlock the mutexes, then go into sleep mode.

If the CV has an internal mutex that __pthread_mutex_cond_wait_then_unlock and pthread_cond_signal should block internally, then I assume that __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 is called only after __pthread_mutex_cond_wait_then_unlock completed using c.int_state .

0
source

Source: https://habr.com/ru/post/898292/


All Articles