Is there a pattern that allows the usage of std::condition_variable::notify_once/all
without holding a mutex? (Note: I'm not talking about the wait
part)
Formally, a thread is not required to hold a mutex when calling notify
on a condition variable (contrary to wait()
), but - to the best of my knowledge - the following is not possible:
void Thread1() {
unique_lock<mutex> ul(mux);
while (!newData) { //*) see below
cv.wait(ul);
}
//readData;
}
void Thread2() {
//write/modify Data;
newData = true;
cv.notify_one();
}
The problem is that Thread1 could be interrupted, just when it enters the loop body, after it has checked newData
, but before it actually starts to wait.
Now if Thread2 runs to completion between those two statements, the notification will never be registered by Thread1 and Thread1 waits forever.
The simple
obvious solution is to put notify into the protected region:
void Thread2() {
//write/modify Data;
newData = true;
{
lock_guard<mutex> lg(mux);
cv.notify_one();
}
}
Why this is necessary by default is further discussed here.
However, the additional overhead bothers me, especially considering that 99% of the time the locking is not necessary. Also, the fact that I need a synchronization mechanism, to protect another synchronization mechanism seems wrong to me.
So I was wondering, whether there is a pattern (e.g. using atomic operations) that makes locking on the notify side unnecessary (only using standard c++14 functions/mechanisms).
Edit:
To elaborate a bit more about the background: I have a lockfree circular buffer with non-blocking reads and writes. Now I'd like to add also blocking reads and writes (that wait if the buffer is full/empty), but I'd like to minimize the impact that addition mechanism has on the performance of the non-blocking ones. Obviously, I can not avoid to e.g. call nonEmpty.notify_all()
in the writes, but I'd really like to avoid to also lock a mutex in that function.
So far I came up with two Ideas, which unfortunately are less then ideal:
Use
wait_for
. That way, even if the notify is missed, the thread will not deadlock forever, but it introduces sporadic delays.Put a lock around the notify, but make the whole block conditional depending on whether another thread could potentially be in the problematic section (similar to this). This should significantly reduce the number of system calls on the notify siede, but it still requires a mutex sometimes, which might limit its scalability (I havn't done any measurements yet)
*) Btw. I'm aware of the predicated version of wait
. Unfortunately - according to cppreference - it is semantically equivalent to my version above and the explicit form makes it easier to reason about the program.