Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

This is the documentation for an old version of Boost. Click here to view this page for the latest version.
PrevUpHomeNext

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().


PrevUpHomeNext