Hacker News new | ask | show | jobs
by no-bugs 3665 days ago
> Aren't most mutex/condition variable implementations optimized to avoid this case by deferring the condition signal to the mutex unlock?

Yes, _some_ but not _all_ implementations are doing it; however, as it is not really guaranteed (and actually is a workaround for poorly written programs) - standing recommendation is still to notify after the lock (which can be better, can be the same, but won't be worse than doing it within), see for example http://en.cppreference.com/w/cpp/thread/condition_variable/n...:

"The notifying thread does not need to hold the lock on the same mutex as the one held by the waiting thread(s); in fact doing so is a pessimization, since the notified thread would immediately block again, waiting for the notifying thread to release the lock. However, some implementations (in particular many implementations of pthreads) recognize this situation and avoid this "hurry up and wait" scenario by transferring the waiting thread from the condition variable's queue directly to the queue of the mutex within the notify call, without waking it up."

> Additionally, in the implementation of kill() in the latter examples, there should be notify_all() instead of notify_one().

In MOST cases, it didn't matter (as there was only one blocking thread - the one reading), but yes, there was one case when it was indeed important. Fixed now, THANKS!

2 comments

Pthreads on Linux encourage holding the lock during a signal or broadcast (http://linux.die.net/man/3/pthread_cond_signal):

  The pthread_cond_broadcast() or pthread_cond_signal()
  functions may be called by a thread whether or not it
  currently owns the mutex that threads calling
  pthread_cond_wait() or pthread_cond_timedwait() have
  associated with the condition variable during their waits;
  however, if predictable scheduling behavior is required,
  then that mutex shall be locked by the thread calling
  pthread_cond_broadcast() or pthread_cond_signal().
They don't define what exactly "predictable scheduling is", but the message that most programmers will probably come away with is, grabbing the lock is probably the better thing to do.
Since most platforms defer condition signalling to mutex unlocks, the way the code is now written will cause more spurious wakeups and context switching than necessary.

The pathological case in scheduling goes like this:

    1. Reader A enters pop_front on an empty queue, goes to wait on the condition variable
    2. Writer W enters push_back, adds an element to list and releases mutex and get pre-empted (on line 25 first example)
    3. Reader B enters pop_front, sees queue not empty, pops element and leaves
    4. Writer W signals condition, wakes up Reader A
    5. Reader A wakes up but the queue is empty, goes back to sleep causing unnecessary context switch and trashing the TLB
The code is "correct" either way, but it's now optimized for the rare platforms that have a badly implemented condition variable.

I would say it's usually better to signal your condition variables with mutexes locked unless you know you're running on a platform with flaky condition variables. Easier to guarantee correctness and avoid spurious wakeups that way.

related note: cppreference.com is a terrible resource. It was worse 10 years ago, but it's not improved much. I would not trust the weasel wording ("some implementations" etc) in the link you posted, what it says about pthreads contradicts the pthread man pages (that encourage signal while holding lock)

From what I've seen (YMMV), chances of it happening are MUCH smaller than chances of getting context switch under the lock (with lots of threads running into this lock and having their own context switches), because of spending more time under the lock than it is really necessary (and ANY call, especially kernel call at 300+ clocks, is a LOT of time). Strictly speaking, it needs to be measured, but until that point - I'm keeping my mutex locks as small as possible.