3

Why is the behavior undefined when a POSIX condition variable is used with multiple mutexes?

Excerpt from the 2018 edition:

When a thread waits on a condition variable, having specified a particular mutex to either the pthread_cond_timedwait() or the pthread_cond_wait() operation, a dynamic binding is formed between that mutex and condition variable that remains in effect as long as at least one thread is blocked on the condition variable. During this time, the effect of an attempt by any thread to wait on that condition variable using a different mutex is undefined

I suppose it's because of some kind of implementation detail of some specific system, so can someone confirm or refute it?

C11 (and C17) condition variables don't seem to have such limitation, and why?

DannyNiu
  • 1,313
  • 8
  • 27
  • For POSIX, it's explicitly stated in the texts for `pthread_cond_wait`, @Yunnosch – DannyNiu May 05 '21 at 04:52
  • That is helpful information which you should add to your question by [edit]ing. Consider quoting the text you want to discuss. – Yunnosch May 05 '21 at 05:00
  • It can cause all sorts of annoying race conditions. The [Wikpedia entry on condition variables](https://en.wikipedia.org/wiki/Monitor_\(synchronization\)#Condition_variables) touches on it. – Shawn May 05 '21 at 05:01

3 Answers3

1

C11 (and C17) condition variables don't seem to have such limitation,

I agree that C11 and C17 seem not to document any limitation on the mutex(es) used with cnd_wait or cnd_timedwait. However, they do permit these functions to fail, and one plausible reason they might fail is that a different mutex is specified than another thread currently waiting on the same CV specified.

and why?

I can only speculate about the standard committee's motivations and thought process. Possibly members wanted to allow implementations that do not have such a limitation, and they assumed that other implementations would simply exercise their option to fail as needed. Possibly it was simply an oversight.

It is both ordinary for condition variable implementations and appropriate for condition variable usage patterns to expect that multiple threads do not concurrently wait on a CV with different mutexes. POSIX is by no means unique in its restriction:

  • C++ std::condition_variable has a restriction analogous to POSIX's
  • Java objects' monitors and implementations of java.util.concurrent.locks.Condition are inherently associated with specific locks, so one cannot even express trying to use one with different locks at the same time.
  • Python threading.Condition objects are also associated with specific locks.
John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • “and appropriate for condition variable usage patterns to expect ...", do you have some examples where using multiple mutex with a single CV can (and even should) be replaced with using a single mutex? I'm still scratching my head about this. – DannyNiu May 06 '21 at 05:07
  • 1
    @DannyNiu, you are looking at it the wrong way around. The question should be whether there are any examples where a condition should be used with multiple different mutexes in the first place. The combination of CV + (one) mutex is not arbitrary. It is designed for a specific category of usage patterns that involve threads waiting on changes to a particular piece of data. For that, you want all threads to use the same mutex, with that mutex being one that protects access to the data in question. – John Bollinger May 06 '21 at 12:56
0

It is more of a "purpose" thing, std::mutex is more for a one thread signaling to many threads unlike std::atomic that works for most situations. The undefined part is probably trying to say "it may deadlock itself", it may happen anyways, but not because of the library. Some of the can't should be seen as a shouldn't, just test if ti works for you and thats it (mostly).

SrPanda
  • 854
  • 1
  • 5
  • 9
  • The question is about C, not C++, and it is principally about *condition variables*, as specified by C11 and C17, but this answer does not even mention CVs. Also, as long as you're looking at C++, note that `std::condition_variable` has the same limitation as POSIX CVs. – John Bollinger May 05 '21 at 06:24
  • Noticed, but i'm way more familiar with c++, i didn't refer specifically to de cv because i see the dangerous part in the mutex. – SrPanda May 05 '21 at 06:39
0

This answer is a logical one that "argues" me out of my confusion. Thanks goes to John Bollinger for inspiration.

Logically, a mutex, a condition variable object, and an abstract predicate are associated together, such that a wait operation on the condvar returns with the predicate changed compared to before the operation. In particular, the association between the mutex and the predicate is especially significant. This is consistent with the academically agreed on definition of a condition variable.

Logic: Suppose there are 2 threads waiting on a condvar with 2 different mutexes, logically this means that there are 2 independent predicates, thus, the 2 mutexes belong to different condition variables.

Implementation: Often, implementations of condition variables are user-space subroutines operating on kernel-backed semaphores, they are simplified and made more thread-safe when incorporating the above logical assumption.

Reality: What this means, is that signalling the change of 2 predicates using a single condvar is usually much less efficient and much more error-prone than signalling them using 2 different condvar.

DannyNiu
  • 1,313
  • 8
  • 27