Search code examples
c++c++11lockingcondition-variablespurious-wakeup

c++11 std::notify_all and spurious wakeup


with c++11. As std::notify_all would cause spurious wakeup, then why std::notify_all is remained but not std::notify_one all the time? And could std::notify_one cause spurious wakeup by the way?


elaborating my doubts:

When I call std::condition_variable.wait/wait_for/wait_until and std::notify_XXX, my purpose is generally to implement thread sychronisation. That is to say, more threads blocked to wait until another thread to notify only one of them to unblock.

Then I can just call notify_one to achieve that, but why there's another notify_all, what is its purpose, or what situation is notify_all suitable for? And in my situation, when I call notify_all, it will wakeup all waiting threads, then only one thread actually unblock and others remains blocking, is it called spurious wakeup? And if notify_one would call spurious wakeup as well?


Solution

  • On void std::condition_variable::wait(std::unique_lock<std::mutex>& lock); from thread.condition/8.3:

    The function will unblock when signaled by a call to notify_­one() or a call to notify_­all(), or spuriously.

    So calling notify_­one() or notify_­all() is not a prerequisite. It can unblock without any of those being called.


    The above quote is from "C++20 first post-publication draft" but has remained the same since it was first written for C++11.


    why there's another notify_all, what is its purpose, or what situation is notify_all suitable for?

    When you want all waiting threads to unblock. A practical situation would be when it's time to shutdown. If you have threads blocked in a wait they will never finish and join()ing them will hang.

    Example with a predicate saying that it should wait until either aborted is true or queue.empty() is false:

    bool pop_from_queue(T& item) {
        std::unique_lock<std::mutex> lock(mtx);
        while(queue.empty() && not aborted) cv.wait(lock);
        if(aborted) return false;        // time to shutdown
        // else pick an item from the queue
        item = std::move(queue.front());
        queue.pop();
        return true;
    }
    

    When it's time to shutdown, another thread would here typically do:

    aborted = true;   // std::atomic<bool>
    cv.notify_all();
    

    when I call nitify_all, it will wakeup all waiting threads, then only one thread actually unblock and others remains blocking, is it called spurious wakeup?

    No. A spurious wakeup is a wakeup that can happen at any time. If you call notify_all, the waiting threads will all wakeup as ordered - not spuriously.

    And if notify_one would call spurious wakeup as well?

    It may cause a spurious wakeup, but that would be an implementation detail. The best thing is to just live with the fact that the threads may wakeup at any time and just check the predicate when they do.

    could I precislly control where to unblock in the waiting thread(who called condition_variable.wait with no Predicate)?

    Without checking the predicate, the only thing the thread knows for sure is that it woke up. It does not now if it's for the right reason or not.