std::condit...riable::wait(3)

πŸ“¦ version 2024.07.05, in nixpkgs unstable, opensuse-leap 16.0

🌸 NAME 🌸

std::condition_variable::wait β€” std::condition_variable::wait

okay so this is from the C++ Standard Libary manual (yes, the header really spells it Libary 😭), and the header title is the truncated std::condit...riable::wait(3). We're talking about the condition-variable wait call, bestie β€” it's giving "block until notified" energy. πŸ’…

✨ Synopsis ✨

void wait( std::unique_lock<std::mutex>& lock );                 (1)

(since C++11)

template< class Predicate >                                      (2)

(since C++11)

void wait( std::unique_lock<std::mutex>& lock, Predicate pred );

wait causes the current thread to block until the condition variable is notified or a spurious wakeup occurs. pred can be optionally provided to detect spurious wakeup.

Overload (1): atomically calls lock.unlock() and blocks on *this. The thread will be unblocked when notify_all() or notify_one() is executed. It may also be unblocked spuriously. When unblocked, calls lock.lock() (possibly blocking on the lock), then returns. πŸ¦‹

Overload (2): equivalent to

while (!pred())
    wait(lock);

This overload may be used to ignore spurious awakenings while waiting for a specific condition to become true. If pred() is ill-formed, or the return value of pred() is not convertible to bool (until C++20) or decltype(pred()) does not model boolean-testable (since C++20), the program is ill-formed. πŸ’€

Right after wait returns, lock.owns_lock() is true, and lock.mutex() is locked by the calling thread. If these postconditions cannot be satisfied^[1], it calls std::terminate.

If any of the following conditions is satisfied, the behavior is undefined:

  • lock.owns_lock() is false.
  • lock.mutex() is not locked by the calling thread.
  • If some other threads are also waiting on *this, lock.mutex() is different from the mutex unlocked by the waiting functions (wait, wait_for and wait_until) called on *this by those threads.
  1. ↑ This can happen if the re-locking of the mutex throws an exception.

πŸŽ€ Parameters πŸŽ€

  • lock β€” an lock which must be locked by the calling thread
  • pred β€” the predicate to check whether the waiting can be completed

πŸ¦‹ Type requirements πŸ¦‹

  • Predicate must meet the requirements of FunctionObject.

πŸ’… Exceptions πŸ’…

  1. Throws nothing.
  2. Any exception thrown by pred.

🌈 Notes 🌈

The effects of notify_one()/notify_all() and each of the three atomic parts of wait()/wait_for()/wait_until() (unlock+wait, wakeup, and lock) take place in a single total order that can be viewed as modification order of an atomic variable: the order is specific to this individual condition variable. This makes it impossible for notify_one() to, for example, be delayed and unblock a thread that started waiting just after the call to notify_one() was made. periodt ✨

🌟 Example 🌟

there's a // Run this code marker on the original page; here's the code, copied exactly:

        #include <chrono>
        #include <condition_variable>
        #include <iostream>
        #include <thread>

        std::condition_variable cv;
        std::mutex cv_m; // This mutex is used for three purposes:
                         // 1) to synchronize accesses to i
                         // 2) to synchronize accesses to std::cerr
                         // 3) for the condition variable cv
        int i = 0;

        void waits()
        {
            std::unique_lock<std::mutex> lk(cv_m);
            std::cerr << "Waiting... \n";
            cv.wait(lk, []{ return i == 1; });
            std::cerr << "...finished waiting. i == 1\n";
        }

        void signals()
        {
            std::this_thread::sleep_for(std::chrono::seconds(1));
            {
                std::lock_guard<std::mutex> lk(cv_m);
                std::cerr << "Notifying...\n";
            }
            cv.notify_all();

            std::this_thread::sleep_for(std::chrono::seconds(1));

            {
                std::lock_guard<std::mutex> lk(cv_m);
                i = 1;
                std::cerr << "Notifying again...\n";
            }
            cv.notify_all();
        }

        int main()
        {
            std::thread t1(waits), t2(waits), t3(waits), t4(signals);
            t1.join();
            t2.join();
            t3.join();
            t4.join();
        }

πŸ“ Possible output πŸ“

        Waiting...
        Waiting...
        Waiting...
        Notifying...
        Notifying again...
        ...finished waiting. i == 1
        ...finished waiting. i == 1
        ...finished waiting. i == 1

πŸ’€ Defect reports πŸ’€

The following behavior-changing defect reports were applied retroactively to previously published C++ standards.

  • DR: LWG 2135 β€” Applied to: C++11. Behavior as published: the behavior was unclear if lock.lock() throws an exception. Correct behavior: calls std::terminate in this case.

πŸ‘‘ See also πŸ‘‘

  • wait_for β€” blocks the current thread until the condition variable is awakened or after the specified timeout duration (public member function).
  • wait_until β€” blocks the current thread until the condition variable is awakened or until specified time point has been reached (public member function).
  • C documentation for cnd_wait.
  1. The Old New Thing article: Spurious wake-ups in Win32 condition variables.

πŸ™ˆ Hidden category πŸ™ˆ

  • Pages with unreviewed LWG DR marker

okay that's the whole page, bestie πŸ’– source/version/date sign-off: http://cppreference.com, 2024.06.10, std::condit...riable::wait(3) β€” go forth and wait on your condition variables responsibly 🫢

πŸ“¦ where this page comes from