CppCoreGuidelines icon indicating copy to clipboard operation
CppCoreGuidelines copied to clipboard

CP.42: Don’t wait without a condition

Open Quuxplusone opened this issue 5 years ago • 4 comments
trafficstars

More specific than #554 so I figured I'd open a new issue. "CP.42: Don’t wait without a condition" gives this "example, bad":

void thread1() {
    while (true) {
        // do some work ... [for the sake of argument let's say q.push(42)]
        std::unique_lock<std::mutex> lock(mx);
        cv.notify_one();    // wake other thread
    }
}

void thread2() {
    while (true) {
        std::unique_lock<std::mutex> lock(mx);
        cv.wait(lock);    // might block forever
        // do work ... [for the sake of argument let's say q.pop()]
    }
}

Here, if some other thread consumes thread1’s notification, thread2 can wait forever.

That's true, but adding a condition to the wait doesn't actually fix that problem. If the cv.notify_one() can be consumed by "some other thread," then there's really nothing thread2 will ever be able to do about that.

void thread2() {
    while (true) {
        std::unique_lock<std::mutex> lock(mx);
        cv.wait(lock, [](){ return !q.empty(); });    // STILL might block forever
        // do work ... [for the sake of argument let's say q.pop()]
    }
}

I think the original "example, bad" needs to be a complete example; i.e., replace the "do work..." comments with actual code that can be evaluated by the reader. I don't know if it's possible to create an example where

  • cv.wait(lock, some_condition) is correct,
  • cv.wait(lock) is buggy, and
  • cv.wait(lock) isn't just obviously wrong and out of step with the programmer's intent.

Or, maybe the appropriate solution is to replace the words "if some other thread consumes thread1’s notification" with the words "if thread1's notification arrives before thread2 has begun waiting on cv"?

Quuxplusone avatar May 02 '20 03:05 Quuxplusone

Could just weasel-word it out as "thread2 could miss the notification" thread2 is also buggy in that it does work on every spurious wakeup (which aren't theoretical: changing system time fires them on linux)

cubbimew avatar May 05 '20 12:05 cubbimew

@cubbimew: I really think "thread2 could miss the notification" implies the wrong intuition. I want to avoid the implication that a notification could somehow "fail to arrive" thread2, as if thread2 isn't being fully attentive or as if there's some bad guy out there eating the notifications meant for thread2. The problem is specifically that "thread1's notification arrives before thread2 has begun waiting on cv."

[The "bad" version of] thread2 is also buggy in that it does work on every spurious wakeup

Agreed. The current text does actually nod to this problem by saying "...or wake up simply to find that there is no work to do," but it would be a huge improvement for it to actually explain what that means (i.e., define and use the term "spurious wakeup").

FWIW, I frequently use this mailbox/flag/cymbals metaphor for explaining the mutex/cv pattern: https://www.youtube.com/watch?v=jfDRgnxDe7o&t=4m15s (Spurious wakeup is described @6:14.)

Quuxplusone avatar May 05 '20 14:05 Quuxplusone

Editors call: Thanks, we should improve this example.

hsutter avatar Sep 03 '20 18:09 hsutter