I know this would cause problems with boost, not sure about std::.
I know this would cause problems with boost, not sure about std::.
That's not a bug. The notifier can either signal the condition variable with or without the corresponding lock held and both cases are race-free. In some implementations, it may be more performant to signal the condvar with the lock still held, while in others it is more performant to signal the condvar after releasing the lock.
In this discussion "implementations" isn't referring to boost, libstdc++, or LLVM's glue library, "implementations" is referring to the underlying system libraries.
Alas, most of the implementations aren't willing to tell you which one is better.
What would the case where it's more performant to notify while holding the lock look like? Is the underlying implementation somehow able to transfer ownership of the mutex?
> There is at least one implementation that takes advantage of the requirement that a condition variable be associated with a unique lock to make fewer futex(2) calls, such that the signaling and woken thread make exactly one system call each. NPTL does this.
However, I cannot find that Q&A pairing any more. It is certainly the case on the RTOS I'm using right now that it is more optimal to signal outside the lock and rely on the fact that uncontended locking and unlocking operations don't make passes through the scheduler.
IIRC it is so hard to get it right in practice (the number of races and corner cases is staggering) that recent libc versions might have stopped doing it. I might be misremembering though.
[1] https://en.cppreference.com/w/cpp/thread/condition_variable
As a rule of thumb: its usually a bug to not hold the lock before notify(), unless you synchronize it with another condition
But yeah, in general I agree with the advice.. I'm pretty sure I've made that mistake before.
"Even if the shared variable is atomic, it must be modified under the mutex in order to correctly publish the modification to the waiting thread."
That's not normative but I can't be bothered to check the c++ standard for exact wordings. Also SuSv4 doesn't have an explicit prohibition on modifying the predicate outside a critical section. But if you do, you are truly on your own.
So simply using an atomic (even with a full release-acquire memory barrier) is not necessarily sufficient as that would only enforce the coherence but not prevent the scheduler from interrupting at the wrong moment.
All correct non-realtime uses uses of condition variables should work fine with notify called outside of the critical section.
The only case it matters is when using strict realtime scheduling (SCHED_FIFO and SCHED_RR) and it is important that higher priority threads are woken up before lower priority ones.