I’m learning about C++11 concurrency, where my only prior experience with concurrency primitives was in Operating Systems class six years ago, so be gentle, if you can.
In C++11, we can write
std::mutex m;
std::condition_variable cv;
std::queue<int> q;
void producer_thread() {
std::unique_lock<std::mutex> lock(m);
q.push(42);
cv.notify_one();
}
void consumer_thread() {
std::unique_lock<std::mutex> lock(m);
while (q.empty()) {
cv.wait(lock);
}
q.pop();
}
This works fine, but I’m offended by the need to wrap cv.wait in a loop. The reason we need the loop is clear to me:
Consumer (inside wait()) Producer Vulture
release the lock
sleep until notified
acquire the lock
I MADE YOU A COOKIE
notify Consumer
release the lock
acquire the lock
NOM NOM NOM
release the lock
acquire the lock
return from wait()
HEY WHERE'S MY COOKIE I EATED IT
Now, I believe one of the cool things about unique_lock is that we can pass it around, right? So it would be really elegant if we could do this instead:
Consumer (inside wait()) Producer
release the lock
sleep until notified
acquire the lock
I MADE YOU A COOKIE
notify and yield(passing the lock)
wake(receiving the lock)
return from wait()
YUM
release the lock
Now there’s no way for the Vulture thread to swoop in, because the mutex remains locked all the way from I MADE YOU A COOKIE to YUM. Plus, if notify() requires that you pass a lock, that’s a good way to ensure that people actually lock the mutex before calling notify() (see Signalling a condition variable (pthreads)).
I’m pretty sure that C++11 doesn’t have any standard implementation of this idiom. What’s the historical reason for that (is it just that pthreads didn’t do it? and then why is that)? Is there a technical reason that an adventurous C++ coder couldn’t implement this idiom in standard C++11, calling it perhaps my_better_condition_variable?
I also have a vague feeling that maybe I’m reinventing semaphores, but I don’t remember enough from school to know if that’s accurate or not.
The ultimate answer is because pthreads didn’t do it. C++ is a language that encapsulates operating system functionality. C++ is not an operating system or platform. And so it encapsulates the existing functionality of operating systems such as linux, unix and windows.
However pthreads also has a good rationale for this behavior as well. From the Open Group Base Specifications:
So basically the claim is that you can build
my_better_condition_variableon top of a pthreads condition variable (orstd::condition_variable) fairly easily and without performance penalty. However if we putmy_better_condition_variableat the base level, then those clients who did not need the functionality ofmy_better_condition_variablewould have to pay for it anyway.This philosophy of putting the fastest, most primitive design at the bottom of the stack, with the intent that better/slower things can be built on top of them, runs throughout the C++ lib. And where the C++ lib fails to follow this philosophy, clients are often (and rightly) irritated.