33 Thread support library [thread]

33.5 Condition variables [thread.condition]

Condition variables provide synchronization primitives used to block a thread until notified by some other thread that some condition is met or until a system time is reached. Class condition_­variable provides a condition variable that can only wait on an object of type unique_­lock<mutex>, allowing maximum efficiency on some platforms. Class condition_­variable_­any provides a general condition variable that can wait on objects of user-supplied lock types.

Condition variables permit concurrent invocation of the wait, wait_­for, wait_­until, notify_­one and notify_­all member functions.

The execution of notify_­one and notify_­all shall be atomic. The execution of wait, wait_­for, and wait_­until shall be performed in three atomic parts:

  1. 1.the release of the mutex and entry into the waiting state;

  2. 2.the unblocking of the wait; and

  3. 3.the reacquisition of the lock.

The implementation shall behave as if all executions of notify_­one, notify_­all, and each part of the wait, wait_­for, and wait_­until executions are executed in a single unspecified total order consistent with the "happens before" order.

Condition variable construction and destruction need not be synchronized.

33.5.1 Header <condition_­variable> synopsis [condition_variable.syn]

namespace std {
  class condition_variable;
  class condition_variable_any;

  void notify_all_at_thread_exit(condition_variable& cond, unique_lock<mutex> lk);

  enum class cv_status { no_timeout, timeout };
}

33.5.2 Non-member functions [thread.condition.nonmember]

void notify_all_at_thread_exit(condition_variable& cond, unique_lock<mutex> lk);

Requires: lk is locked by the calling thread and either

  • no other thread is waiting on cond, or

  • lk.mutex() returns the same value for each of the lock arguments supplied by all concurrently waiting (via wait, wait_­for, or wait_­until) threads.

Effects: Transfers ownership of the lock associated with lk into internal storage and schedules cond to be notified when the current thread exits, after all objects of thread storage duration associated with the current thread have been destroyed. This notification shall be as if:

lk.unlock();
cond.notify_all();

Synchronization: The implied lk.unlock() call is sequenced after the destruction of all objects with thread storage duration associated with the current thread.

[Note: The supplied lock will be held until the thread exits, and care must be taken to ensure that this does not cause deadlock due to lock ordering issues. After calling notify_­all_­at_­thread_­exit it is recommended that the thread should be exited as soon as possible, and that no blocking or time-consuming tasks are run on that thread. end note]

[Note: It is the user's responsibility to ensure that waiting threads do not erroneously assume that the thread has finished if they experience spurious wakeups. This typically requires that the condition being waited for is satisfied while holding the lock on lk, and that this lock is not released and reacquired prior to calling notify_­all_­at_­thread_­exit. end note]

33.5.3 Class condition_­variable [thread.condition.condvar]

namespace std {
  class condition_variable {
  public:

    condition_variable();
    ~condition_variable();

    condition_variable(const condition_variable&) = delete;
    condition_variable& operator=(const condition_variable&) = delete;

    void notify_one() noexcept;
    void notify_all() noexcept;
    void wait(unique_lock<mutex>& lock);
    template <class Predicate>
      void wait(unique_lock<mutex>& lock, Predicate pred);
    template <class Clock, class Duration>
      cv_status wait_until(unique_lock<mutex>& lock,
                           const chrono::time_point<Clock, Duration>& abs_time);
    template <class Clock, class Duration, class Predicate>
      bool wait_until(unique_lock<mutex>& lock,
                      const chrono::time_point<Clock, Duration>& abs_time,
                      Predicate pred);

    template <class Rep, class Period>
      cv_status wait_for(unique_lock<mutex>& lock,
                         const chrono::duration<Rep, Period>& rel_time);
    template <class Rep, class Period, class Predicate>
      bool wait_for(unique_lock<mutex>& lock,
                    const chrono::duration<Rep, Period>& rel_time,
                    Predicate pred);

    using native_handle_type = implementation-defined; // See [thread.req.native]
    native_handle_type native_handle();                // See [thread.req.native]
  };
}

The class condition_­variable shall be a standard-layout class (Clause [class]).

condition_variable();

Effects: Constructs an object of type condition_­variable.

Throws: system_­error when an exception is required ([thread.req.exception]).

Error conditions:

  • resource_­unavailable_­try_­again — if some non-memory resource limitation prevents initialization.

~condition_variable();

Requires: There shall be no thread blocked on *this. [Note: That is, all threads shall have been notified; they may subsequently block on the lock specified in the wait. This relaxes the usual rules, which would have required all wait calls to happen before destruction. Only the notification to unblock the wait must happen before destruction. The user must take care to ensure that no threads wait on *this once the destructor has been started, especially when the waiting threads are calling the wait functions in a loop or using the overloads of wait, wait_­for, or wait_­until that take a predicate. end note]

Effects: Destroys the object.

void notify_one() noexcept;

Effects: If any threads are blocked waiting for *this, unblocks one of those threads.

void notify_all() noexcept;

Effects: Unblocks all threads that are blocked waiting for *this.

void wait(unique_lock<mutex>& lock);

Requires: lock.owns_­lock() is true and lock.mutex() is locked by the calling thread, and either

  • no other thread is waiting on this condition_­variable object or

  • lock.mutex() returns the same value for each of the lock arguments supplied by all concurrently waiting (via wait, wait_­for, or wait_­until) threads.

Effects:

  • Atomically calls lock.unlock() and blocks on *this.

  • When unblocked, calls lock.lock() (possibly blocking on the lock), then returns.

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

Remarks: If the function fails to meet the postcondition, terminate() shall be called ([except.terminate]). [Note: This can happen if the re-locking of the mutex throws an exception. end note]

Postconditions: lock.owns_­lock() is true and lock.mutex() is locked by the calling thread.

Throws: Nothing.

template <class Predicate> void wait(unique_lock<mutex>& lock, Predicate pred);

Requires: lock.owns_­lock() is true and lock.mutex() is locked by the calling thread, and either

  • no other thread is waiting on this condition_­variable object or

  • lock.mutex() returns the same value for each of the lock arguments supplied by all concurrently waiting (via wait, wait_­for, or wait_­until) threads.

Effects: Equivalent to:

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

Remarks: If the function fails to meet the postcondition, terminate() shall be called ([except.terminate]). [Note: This can happen if the re-locking of the mutex throws an exception. end note]

Postconditions: lock.owns_­lock() is true and lock.mutex() is locked by the calling thread.

Throws: Any exception thrown by pred.

template <class Clock, class Duration> cv_status wait_until(unique_lock<mutex>& lock, const chrono::time_point<Clock, Duration>& abs_time);

Requires: lock.owns_­lock() is true and lock.mutex() is locked by the calling thread, and either

  • no other thread is waiting on this condition_­variable object or

  • lock.mutex() returns the same value for each of the lock arguments supplied by all concurrently waiting (via wait, wait_­for, or wait_­until) threads.

Effects:

  • Atomically calls lock.unlock() and blocks on *this.

  • When unblocked, calls lock.lock() (possibly blocking on the lock), then returns.

  • The function will unblock when signaled by a call to notify_­one(), a call to notify_­all(), expiration of the absolute timeout ([thread.req.timing]) specified by abs_­time, or spuriously.

  • If the function exits via an exception, lock.lock() shall be called prior to exiting the function.

Remarks: If the function fails to meet the postcondition, terminate() shall be called ([except.terminate]). [Note: This can happen if the re-locking of the mutex throws an exception. end note]

Postconditions: lock.owns_­lock() is true and lock.mutex() is locked by the calling thread.

Returns: cv_­status​::​timeout if the absolute timeout ([thread.req.timing]) specified by abs_­time expired, otherwise cv_­status​::​no_­timeout.

Throws: Timeout-related exceptions ([thread.req.timing]).

template <class Rep, class Period> cv_status wait_for(unique_lock<mutex>& lock, const chrono::duration<Rep, Period>& rel_time);

Requires: lock.owns_­lock() is true and lock.mutex() is locked by the calling thread, and either

  • no other thread is waiting on this condition_­variable object or

  • lock.mutex() returns the same value for each of the lock arguments supplied by all concurrently waiting (via wait, wait_­for, or wait_­until) threads.

Effects: Equivalent to:

return wait_until(lock, chrono::steady_clock::now() + rel_time);

Returns: cv_­status​::​timeout if the relative timeout ([thread.req.timing]) specified by rel_­time expired, otherwise cv_­status​::​no_­timeout.

Remarks: If the function fails to meet the postcondition, terminate() shall be called ([except.terminate]). [Note: This can happen if the re-locking of the mutex throws an exception. end note]

Postconditions: lock.owns_­lock() is true and lock.mutex() is locked by the calling thread.

Throws: Timeout-related exceptions ([thread.req.timing]).

template <class Clock, class Duration, class Predicate> bool wait_until(unique_lock<mutex>& lock, const chrono::time_point<Clock, Duration>& abs_time, Predicate pred);

Requires: lock.owns_­lock() is true and lock.mutex() is locked by the calling thread, and either

  • no other thread is waiting on this condition_­variable object or

  • lock.mutex() returns the same value for each of the lock arguments supplied by all concurrently waiting (via wait, wait_­for, or wait_­until) threads.

Effects: Equivalent to:

while (!pred())
  if (wait_until(lock, abs_time) == cv_status::timeout)
    return pred();
return true;

Remarks: If the function fails to meet the postcondition, terminate() shall be called ([except.terminate]). [Note: This can happen if the re-locking of the mutex throws an exception. end note]

Postconditions: lock.owns_­lock() is true and lock.mutex() is locked by the calling thread.

[Note: The returned value indicates whether the predicate evaluated to true regardless of whether the timeout was triggered. end note]

Throws: Timeout-related exceptions ([thread.req.timing]) or any exception thrown by pred.

template <class Rep, class Period, class Predicate> bool wait_for(unique_lock<mutex>& lock, const chrono::duration<Rep, Period>& rel_time, Predicate pred);

Requires: lock.owns_­lock() is true and lock.mutex() is locked by the calling thread, and either

  • no other thread is waiting on this condition_­variable object or

  • lock.mutex() returns the same value for each of the lock arguments supplied by all concurrently waiting (via wait, wait_­for, or wait_­until) threads.

Effects: Equivalent to:

return wait_until(lock, chrono::steady_clock::now() + rel_time, std::move(pred));

[Note: There is no blocking if pred() is initially true, even if the timeout has already expired. end note]

Remarks: If the function fails to meet the postcondition, terminate() shall be called ([except.terminate]). [Note: This can happen if the re-locking of the mutex throws an exception. end note]

Postconditions: lock.owns_­lock() is true and lock.mutex() is locked by the calling thread.

[Note: The returned value indicates whether the predicate evaluates to true regardless of whether the timeout was triggered. end note]

Throws: Timeout-related exceptions ([thread.req.timing]) or any exception thrown by pred.

33.5.4 Class condition_­variable_­any [thread.condition.condvarany]

A Lock type shall meet the BasicLockable requirements. [Note: All of the standard mutex types meet this requirement. If a Lock type other than one of the standard mutex types or a unique_­lock wrapper for a standard mutex type is used with condition_­variable_­any, the user must ensure that any necessary synchronization is in place with respect to the predicate associated with the condition_­variable_­any instance. end note]

namespace std {
  class condition_variable_any {
  public:
    condition_variable_any();
    ~condition_variable_any();

    condition_variable_any(const condition_variable_any&) = delete;
    condition_variable_any& operator=(const condition_variable_any&) = delete;

    void notify_one() noexcept;
    void notify_all() noexcept;
    template <class Lock>
      void wait(Lock& lock);
    template <class Lock, class Predicate>
      void wait(Lock& lock, Predicate pred);

    template <class Lock, class Clock, class Duration>
      cv_status wait_until(Lock& lock, const chrono::time_point<Clock, Duration>& abs_time);
    template <class Lock, class Clock, class Duration, class Predicate>
      bool wait_until(Lock& lock, const chrono::time_point<Clock, Duration>& abs_time,
        Predicate pred);
    template <class Lock, class Rep, class Period>
      cv_status wait_for(Lock& lock, const chrono::duration<Rep, Period>& rel_time);
    template <class Lock, class Rep, class Period, class Predicate>
      bool wait_for(Lock& lock, const chrono::duration<Rep, Period>& rel_time,
        Predicate pred);
  };
}

condition_variable_any();

Effects: Constructs an object of type condition_­variable_­any.

Throws: bad_­alloc or system_­error when an exception is required ([thread.req.exception]).

Error conditions:

  • resource_­unavailable_­try_­again — if some non-memory resource limitation prevents initialization.

  • operation_­not_­permitted — if the thread does not have the privilege to perform the operation.

~condition_variable_any();

Requires: There shall be no thread blocked on *this. [Note: That is, all threads shall have been notified; they may subsequently block on the lock specified in the wait. This relaxes the usual rules, which would have required all wait calls to happen before destruction. Only the notification to unblock the wait must happen before destruction. The user must take care to ensure that no threads wait on *this once the destructor has been started, especially when the waiting threads are calling the wait functions in a loop or using the overloads of wait, wait_­for, or wait_­until that take a predicate. end note]

Effects: Destroys the object.

void notify_one() noexcept;

Effects: If any threads are blocked waiting for *this, unblocks one of those threads.

void notify_all() noexcept;

Effects: Unblocks all threads that are blocked waiting for *this.

template <class Lock> void wait(Lock& lock);

[Note: If any of the wait functions exits via an exception, it is unspecified whether the Lock is held. One can use a Lock type that allows to query that, such as the unique_­lock wrapper. end note]

Effects:

  • Atomically calls lock.unlock() and blocks on *this.

  • When unblocked, calls lock.lock() (possibly blocking on the lock) and returns.

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

Remarks: If the function fails to meet the postcondition, terminate() shall be called ([except.terminate]). [Note: This can happen if the re-locking of the mutex throws an exception. end note]

Postconditions: lock is locked by the calling thread.

Throws: Nothing.

template <class Lock, class Predicate> void wait(Lock& lock, Predicate pred);

Effects: Equivalent to:

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

template <class Lock, class Clock, class Duration> cv_status wait_until(Lock& lock, const chrono::time_point<Clock, Duration>& abs_time);

Effects:

  • Atomically calls lock.unlock() and blocks on *this.

  • When unblocked, calls lock.lock() (possibly blocking on the lock) and returns.

  • The function will unblock when signaled by a call to notify_­one(), a call to notify_­all(), expiration of the absolute timeout ([thread.req.timing]) specified by abs_­time, or spuriously.

  • If the function exits via an exception, lock.lock() shall be called prior to exiting the function.

Remarks: If the function fails to meet the postcondition, terminate() shall be called ([except.terminate]). [Note: This can happen if the re-locking of the mutex throws an exception. end note]

Postconditions: lock is locked by the calling thread.

Returns: cv_­status​::​timeout if the absolute timeout ([thread.req.timing]) specified by abs_­time expired, otherwise cv_­status​::​no_­timeout.

Throws: Timeout-related exceptions ([thread.req.timing]).

template <class Lock, class Rep, class Period> cv_status wait_for(Lock& lock, const chrono::duration<Rep, Period>& rel_time);

Effects: Equivalent to:

return wait_until(lock, chrono::steady_clock::now() + rel_time);

Returns: cv_­status​::​timeout if the relative timeout ([thread.req.timing]) specified by rel_­time expired, otherwise cv_­status​::​no_­timeout.

Remarks: If the function fails to meet the postcondition, terminate() shall be called ([except.terminate]). [Note: This can happen if the re-locking of the mutex throws an exception. end note]

Postconditions: lock is locked by the calling thread.

Throws: Timeout-related exceptions ([thread.req.timing]).

template <class Lock, class Clock, class Duration, class Predicate> bool wait_until(Lock& lock, const chrono::time_point<Clock, Duration>& abs_time, Predicate pred);

Effects: Equivalent to:

while (!pred())
  if (wait_until(lock, abs_time) == cv_status::timeout)
    return pred();
return true;

[Note: There is no blocking if pred() is initially true, or if the timeout has already expired. end note]

[Note: The returned value indicates whether the predicate evaluates to true regardless of whether the timeout was triggered. end note]

template <class Lock, class Rep, class Period, class Predicate> bool wait_for(Lock& lock, const chrono::duration<Rep, Period>& rel_time, Predicate pred);

Effects: Equivalent to:

return wait_until(lock, chrono::steady_clock::now() + rel_time, std::move(pred));