| Home | Libraries | People | FAQ | More

### Condition Variables

##### Synopsis

    enum class cv_status; {
        no_timeout,
        timeout
    };

    class condition_variable;
    class condition_variable_any;

The class `condition_variable` provides a mechanism for a fiber to wait for notification from another fiber. When the fiber awakens from the wait, then it checks to see if the appropriate condition is now true, and continues if so. If the condition is not true, then the fiber calls `wait` again to resume waiting. In the simplest case, this condition is just a boolean variable:

    boost::fibers::condition_variable cond;
    boost::fibers::mutex mtx;
    bool data_ready = false;

    void process_data();

    void wait_for_data_to_process() {
        {
            std::unique_lock< boost::fibers::mutex > lk( mtx);
            while ( ! data_ready) {
                cond.wait( lk);
            }
        }   // release lk
        process_data();
    }

Notice that the `lk` is passed to `condition_variable::wait()`: `wait()` will atomically add the fiber to the set of fibers waiting on the condition variable, and unlock the `mutex`. When the fiber is awakened, the `mutex` will be locked again before the call to `wait()` returns. This allows other fibers to acquire the `mutex` in order to update the shared data, and ensures that the data associated with the condition is correctly synchronized.

`wait_for_data_to_process()` could equivalently be written:

    void wait_for_data_to_process() {
        {
            std::unique_lock< boost::fibers::mutex > lk( mtx);
            // make condition_variable::wait() perform the loop
            cond.wait( lk, [](){ return data_ready; });
        }   // release lk
        process_data();
    }

In the meantime, another fiber sets `data_ready` to `true`, and then calls either `condition_variable::notify_one()` or `condition_variable::notify_all()` on the `condition_variable` `cond` to wake one waiting fiber or all the waiting fibers respectively.

    void retrieve_data();
    void prepare_data();

    void prepare_data_for_processing() {
        retrieve_data();
        prepare_data();
        {
            std::unique_lock< boost::fibers::mutex > lk( mtx);
            data_ready = true;
        }
        cond.notify_one();
    }

Note that the same `mutex` is locked before the shared data is updated, but that the `mutex` does not have to be locked across the call to `condition_variable::notify_one()`.

Locking is important because the synchronization objects provided by Boost.Fiber can be used to synchronize fibers running on different threads.

Boost.Fiber provides both `condition_variable` and `condition_variable_any`. `boost::fibers::condition_variable` can only wait on `std::unique_lock` `< boost::fibers::` `mutex` `>` while `boost::fibers::condition_variable_any` can wait on user-defined lock types.

##### No Spurious Wakeups

Neither `condition_variable` nor `condition_variable_any` are subject to spurious wakeup: `condition_variable::wait()` can only wake up when `condition_variable::notify_one()` or `condition_variable::notify_all()` is called. Even so, it is prudent to use one of the `wait( lock, predicate )` overloads.

Consider a set of consumer fibers processing items from a `std::queue`. The queue is continually populated by a set of producer fibers.

The consumer fibers might reasonably wait on a `condition_variable` as long as the queue remains `empty()`.

Because producer fibers might `push()` items to the queue in bursts, they call `condition_variable::notify_all()` rather than `condition_variable::notify_one()`.

But a given consumer fiber might well wake up from `condition_variable::wait()` and find the queue `empty()`, because other consumer fibers might already have processed all pending items.

(See also spurious wakeup.)

##### Enumeration `cv_status`

A timed wait operation might return because of timeout or not.

    enum class cv_status {
        no_timeout,
        timeout
    };

##### `no_timeout`

Effects:

  The condition variable was awakened with `notify_one` or `notify_all`.

##### `timeout`

Effects:

  The condition variable was awakened by timeout.

##### Class `condition_variable_any`

    #include <boost/fiber/condition_variable.hpp>

    namespace boost {
    namespace fibers {

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

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

        void notify_one() noexcept;
        void notify_all() noexcept;

        template< typename LockType >
        void wait( LockType &);

        template< typename LockType, typename Pred >
        void wait( LockType &, Pred);

        template< typename LockType, typename Clock, typename Duration >
        cv_status wait_until( LockType &,
                              std::chrono::time_point< Clock, Duration > const&);

        template< typename LockType, typename Clock, typename Duration, typename Pred >
        bool wait_until( LockType &,
                         std::chrono::time_point< Clock, Duration > const&,
                         Pred);

        template< typename LockType, typename Rep, typename Period >
        cv_status wait_for( LockType &,
                            std::chrono::duration< Rep, Period > const&);

        template< typename LockType, typename Rep, typename Period, typename Pred >
        bool wait_for( LockType &,
                       std::chrono::duration< Rep, Period > const&,
                       Pred);
    };

    }}

##### Constructor

    condition_variable_any()

Effects:

  Creates the object.

Throws:

  Nothing.

##### Destructor

    ~condition_variable_any()

Precondition:

  All fibers waiting on `*this` have been notified by a call to `notify_one` or `notify_all` (though the respective calls to `wait`, `wait_for` or `wait_until` need not have returned).

Effects:

  Destroys the object.

##### Member function `notify_one`()

    void notify_one() noexcept;

Effects:

  If any fibers are currently blocked waiting on `*this` in a call to `wait`, `wait_for` or `wait_until`, unblocks one of those fibers.

Throws:

  Nothing.

Note:

  It is arbitrary which waiting fiber is resumed.

##### Member function `notify_all`()

    void notify_all() noexcept;

Effects:

  If any fibers are currently blocked waiting on `*this` in a call to `wait`, `wait_for` or `wait_until`, unblocks all of those fibers.

Throws:

  Nothing.

Note:

  This is why a waiting fiber must also check for the desired program state using a mechanism external to the `condition_variable_any`, and retry the wait until that state is reached. A fiber waiting on a `condition_variable_any` might well wake up a number of times before the desired state is reached.

##### Templated member function `wait`()

    template< typename LockType >
        void wait( LockType & lk);

    template< typename LockType, typename Pred >
    void wait( LockType & lk, Pred pred);

Precondition:

  `lk` is locked by the current fiber, and either no other fiber is currently waiting on `*this`, or the execution of the `mutex()` member function on the `lk` objects supplied in the calls to `wait` in all the fibers currently waiting on `*this` would return the same value as `lk->mutex()` for this call to `wait`.

Effects:

  Atomically call `lk.unlock()` and blocks the current fiber. The fiber will unblock when notified by a call to `this->notify_one()` or `this->notify_all()`. When the fiber is unblocked (for whatever reason), the lock is reacquired by invoking `lk.lock()` before the call to `wait` returns. The lock is also reacquired by invoking `lk.lock()` if the function exits with an exception. The member function accepting `pred` is shorthand for:

      while ( ! pred() ) {
          wait( lk);
      }

Postcondition:

  `lk` is locked by the current fiber.

Throws:

  `fiber_error` if an error occurs.

Note:

  The Precondition is a bit dense. It merely states that all the fibers concurrently calling `wait` on `*this` must wait on `lk` objects governing the same `mutex`. Three distinct objects are involved in any `condition_variable_any::wait()` call: the `condition_variable_any` itself, the `mutex` coordinating access between fibers and a local lock object (e.g. `std::unique_lock`). In general, you can partition the lifespan of a given `condition_variable_any` instance into periods with one or more fibers waiting on it, separated by periods when no fibers are waiting on it. When more than one fiber is waiting on that `condition_variable_any`, all must pass lock objects referencing the same `mutex` instance.

##### Templated member function `wait_until`()

    template< typename LockType, typename Clock, typename Duration >
    cv_status wait_until( LockType & lk,
                          std::chrono::time_point< Clock, Duration > const& abs_time);

    template< typename LockType, typename Clock, typename Duration, typename Pred >
    bool wait_until( LockType & lk,
                     std::chrono::time_point< Clock, Duration > const& abs_time,
                     Pred pred);

Precondition:

  `lk` is locked by the current fiber, and either no other fiber is currently waiting on `*this`, or the execution of the `mutex()` member function on the `lk` objects supplied in the calls to `wait`, `wait_for` or `wait_until` in all the fibers currently waiting on `*this` would return the same value as `lk.mutex()` for this call to `wait_until`.

Effects:

  Atomically call `lk.unlock()` and blocks the current fiber. The fiber will unblock when notified by a call to `this->notify_one()` or `this->notify_all()`, when the system time would be equal to or later than the specified `abs_time`. When the fiber is unblocked (for whatever reason), the lock is reacquired by invoking `lk.lock()` before the call to `wait_until` returns. The lock is also reacquired by invoking `lk.lock()` if the function exits with an exception. The member function accepting `pred` is shorthand for:

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

  That is, even if `wait_until()` times out, it can still return `true` if `pred()` returns `true` at that time.

Postcondition:

  `lk` is locked by the current fiber.

Throws:

  `fiber_error` if an error occurs or timeout-related exceptions.

Returns:

  The overload without `pred` returns `cv_status::no_timeout` if awakened by `notify_one()` or `notify_all()`, or `cv_status::timeout` if awakened because the system time is past `abs_time`.

Returns:

  The overload accepting `pred` returns `false` if the call is returning because the time specified by `abs_time` was reached and the predicate returns `false`, `true` otherwise.

Note:

  See Note for `condition_variable_any::wait()`.

##### Templated member function `wait_for`()

    template< typename LockType, typename Rep, typename Period >
    cv_status wait_for( LockType & lk,
                        std::chrono::duration< Rep, Period > const& rel_time);

    template< typename LockType, typename Rep, typename Period, typename Pred >
    bool wait_for( LockType & lk,
                   std::chrono::duration< Rep, Period > const& rel_time,
                   Pred pred);

Precondition:

  `lk` is locked by the current fiber, and either no other fiber is currently waiting on `*this`, or the execution of the `mutex()` member function on the `lk` objects supplied in the calls to `wait`, `wait_for` or `wait_until` in all the fibers currently waiting on `*this` would return the same value as `lk.mutex()` for this call to `wait_for`.

Effects:

  Atomically call `lk.unlock()` and blocks the current fiber. The fiber will unblock when notified by a call to `this->notify_one()` or `this->notify_all()`, when a time interval equal to or greater than the specified `rel_time` has elapsed. When the fiber is unblocked (for whatever reason), the lock is reacquired by invoking `lk.lock()` before the call to `wait` returns. The lock is also reacquired by invoking `lk.lock()` if the function exits with an exception. The `wait_for()` member function accepting `pred` is shorthand for:

      while ( ! pred() ) {
          if ( cv_status::timeout == wait_for( lk, rel_time) ) {
              return pred();
          }
      }
      return true;

  (except of course that `rel_time` is adjusted for each iteration). The point is that, even if `wait_for()` times out, it can still return `true` if `pred()` returns `true` at that time.

Postcondition:

  `lk` is locked by the current fiber.

Throws:

  `fiber_error` if an error occurs or timeout-related exceptions.

Returns:

  The overload without `pred` returns `cv_status::no_timeout` if awakened by `notify_one()` or `notify_all()`, or `cv_status::timeout` if awakened because at least `rel_time` has elapsed.

Returns:

  The overload accepting `pred` returns `false` if the call is returning because at least `rel_time` has elapsed and the predicate returns `false`, `true` otherwise.

Note:

  See Note for `condition_variable_any::wait()`.

##### Class `condition_variable`

    #include <boost/fiber/condition_variable.hpp>

    namespace boost {
    namespace fibers {

    class condition_variable {
    public:
        condition_variable();
        ~condition_variable();

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

        void notify_one() noexcept;
        void notify_all() noexcept;

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

        template< typename Pred >
        void wait( std::unique_lock< mutex > &, Pred);

        template< typename Clock, typename Duration >
        cv_status wait_until( std::unique_lock< mutex > &,
                              std::chrono::time_point< Clock, Duration > const&);

        template< typename Clock, typename Duration, typename Pred >
        bool wait_until( std::unique_lock< mutex > &,
                         std::chrono::time_point< Clock, Duration > const&,
                         Pred);

        template< typename Rep, typename Period >
        cv_status wait_for( std::unique_lock< mutex > &,
                            std::chrono::duration< Rep, Period > const&);

        template< typename Rep, typename Period, typename Pred >
        bool wait_for( std::unique_lock< mutex > &,
                       std::chrono::duration< Rep, Period > const&,
                       Pred);
    };

    }}

##### Constructor

    condition_variable()

Effects:

  Creates the object.

Throws:

  Nothing.

##### Destructor

    ~condition_variable()

Precondition:

  All fibers waiting on `*this` have been notified by a call to `notify_one` or `notify_all` (though the respective calls to `wait`, `wait_for` or `wait_until` need not have returned).

Effects:

  Destroys the object.

##### Member function `notify_one`()

    void notify_one() noexcept;

Effects:

  If any fibers are currently blocked waiting on `*this` in a call to `wait`, `wait_for` or `wait_until`, unblocks one of those fibers.

Throws:

  Nothing.

Note:

  It is arbitrary which waiting fiber is resumed.

##### Member function `notify_all`()

    void notify_all() noexcept;

Effects:

  If any fibers are currently blocked waiting on `*this` in a call to `wait`, `wait_for` or `wait_until`, unblocks all of those fibers.

Throws:

  Nothing.

Note:

  This is why a waiting fiber must also check for the desired program state using a mechanism external to the `condition_variable`, and retry the wait until that state is reached. A fiber waiting on a `condition_variable` might well wake up a number of times before the desired state is reached.

##### Templated member function `wait`()

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

    template< typename Pred >
    void wait( std::unique_lock< mutex > & lk, Pred pred);

Precondition:

  `lk` is locked by the current fiber, and either no other fiber is currently waiting on `*this`, or the execution of the `mutex()` member function on the `lk` objects supplied in the calls to `wait` in all the fibers currently waiting on `*this` would return the same value as `lk->mutex()` for this call to `wait`.

Effects:

  Atomically call `lk.unlock()` and blocks the current fiber. The fiber will unblock when notified by a call to `this->notify_one()` or `this->notify_all()`. When the fiber is unblocked (for whatever reason), the lock is reacquired by invoking `lk.lock()` before the call to `wait` returns. The lock is also reacquired by invoking `lk.lock()` if the function exits with an exception. The member function accepting `pred` is shorthand for:

      while ( ! pred() ) {
          wait( lk);
      }

Postcondition:

  `lk` is locked by the current fiber.

Throws:

  `fiber_error` if an error occurs.

Note:

  The Precondition is a bit dense. It merely states that all the fibers concurrently calling `wait` on `*this` must wait on `lk` objects governing the same `mutex`. Three distinct objects are involved in any `condition_variable::wait()` call: the `condition_variable` itself, the `mutex` coordinating access between fibers and a local lock object (e.g. `std::unique_lock`). In general, you can partition the lifespan of a given `condition_variable` instance into periods with one or more fibers waiting on it, separated by periods when no fibers are waiting on it. When more than one fiber is waiting on that `condition_variable`, all must pass lock objects referencing the same `mutex` instance.

##### Templated member function `wait_until`()

    template< typename Clock, typename Duration >
    cv_status wait_until( std::unique_lock< mutex > & lk,
                          std::chrono::time_point< Clock, Duration > const& abs_time);

    template< typename Clock, typename Duration, typename Pred >
    bool wait_until( std::unique_lock< mutex > & lk,
                     std::chrono::time_point< Clock, Duration > const& abs_time,
                     Pred pred);

Precondition:

  `lk` is locked by the current fiber, and either no other fiber is currently waiting on `*this`, or the execution of the `mutex()` member function on the `lk` objects supplied in the calls to `wait`, `wait_for` or `wait_until` in all the fibers currently waiting on `*this` would return the same value as `lk.mutex()` for this call to `wait_until`.

Effects:

  Atomically call `lk.unlock()` and blocks the current fiber. The fiber will unblock when notified by a call to `this->notify_one()` or `this->notify_all()`, when the system time would be equal to or later than the specified `abs_time`. When the fiber is unblocked (for whatever reason), the lock is reacquired by invoking `lk.lock()` before the call to `wait_until` returns. The lock is also reacquired by invoking `lk.lock()` if the function exits with an exception. The member function accepting `pred` is shorthand for:

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

  That is, even if `wait_until()` times out, it can still return `true` if `pred()` returns `true` at that time.

Postcondition:

  `lk` is locked by the current fiber.

Throws:

  `fiber_error` if an error occurs or timeout-related exceptions.

Returns:

  The overload without `pred` returns `cv_status::no_timeout` if awakened by `notify_one()` or `notify_all()`, or `cv_status::timeout` if awakened because the system time is past `abs_time`.

Returns:

  The overload accepting `pred` returns `false` if the call is returning because the time specified by `abs_time` was reached and the predicate returns `false`, `true` otherwise.

Note:

  See Note for `condition_variable::wait()`.

##### Templated member function `wait_for`()

    template< typename Rep, typename Period >
    cv_status wait_for( std::unique_lock< mutex > & lk,
                        std::chrono::duration< Rep, Period > const& rel_time);

    template< typename Rep, typename Period, typename Pred >
    bool wait_for( std::unique_lock< mutex > & lk,
                   std::chrono::duration< Rep, Period > const& rel_time,
                   Pred pred);

Precondition:

  `lk` is locked by the current fiber, and either no other fiber is currently waiting on `*this`, or the execution of the `mutex()` member function on the `lk` objects supplied in the calls to `wait`, `wait_for` or `wait_until` in all the fibers currently waiting on `*this` would return the same value as `lk.mutex()` for this call to `wait_for`.

Effects:

  Atomically call `lk.unlock()` and blocks the current fiber. The fiber will unblock when notified by a call to `this->notify_one()` or `this->notify_all()`, when a time interval equal to or greater than the specified `rel_time` has elapsed. When the fiber is unblocked (for whatever reason), the lock is reacquired by invoking `lk.lock()` before the call to `wait` returns. The lock is also reacquired by invoking `lk.lock()` if the function exits with an exception. The `wait_for()` member function accepting `pred` is shorthand for:

      while ( ! pred() ) {
          if ( cv_status::timeout == wait_for( lk, rel_time) ) {
              return pred();
          }
      }
      return true;

  (except of course that `rel_time` is adjusted for each iteration). The point is that, even if `wait_for()` times out, it can still return `true` if `pred()` returns `true` at that time.

Postcondition:

  `lk` is locked by the current fiber.

Throws:

  `fiber_error` if an error occurs or timeout-related exceptions.

Returns:

  The overload without `pred` returns `cv_status::no_timeout` if awakened by `notify_one()` or `notify_all()`, or `cv_status::timeout` if awakened because at least `rel_time` has elapsed.

Returns:

  The overload accepting `pred` returns `false` if the call is returning because at least `rel_time` has elapsed and the predicate returns `false`, `true` otherwise.

Note:

  See Note for `condition_variable::wait()`.