2
0
mirror of https://github.com/boostorg/fiber.git synced 2026-02-17 01:32:32 +00:00
Files
fiber/doc/condition_variables.qbk
Nat Goodspeed d653cbdcc6 Finish proofreading pass.
Add a note to the condition_variable::wait_for(..., pred) overload.

fiber_specific_ptr::reset() has no default argument.

Remove mention of launch policy deferred, since no API accepts a launch
policy argument.

Copy construction or copy assignment of a shared_future leaves other.valid()
unchanged. It won't be 'true' unless it was 'true' before.

Mention that [shared_]future::get_exception_ptr() does not invalidate.

Note that 'blocks' and 'suspends' are used interchangeably.

Add some cross-references; add link to std::allocator_arg_t. Clarify the
cross-reference to the paragraph describing BOOST_FIBERS_NO_ATOMICS.

Reformat some overly-long source lines.
2015-09-03 09:16:09 -04:00

322 lines
13 KiB
Plaintext

[/
(C) Copyright 2007-8 Anthony Williams.
(C) Copyright 2013 Oliver Kowalke.
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or copy at
http://www.boost.org/LICENSE_1_0.txt).
]
[section:conditions Condition Variables]
[heading 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 `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.
In the meantime, another fiber sets `data_ready` to `true`, and then calls
either `notify_one` or `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 `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` because __boost_thread__ provides both.
(__boost_fiber__ also provides the name `condition`, which has been deprecated
in __boost_thread__.) However, `boost::fiber::condition_variable` and
`boost::fiber::condition_variable_any` are the same class; like
`boost::thread::condition_variable_any`, its wait() method will accept any
form of lock. `boost::fiber::condition_variable_any` has no need to further
optimize as described for `boost::thread::condition_variable`.
[class_heading condition_variable]
#include <boost/fiber/condition.hpp>
enum cv_status {
no_timeout,
timeout
};
class condition_variable {
public:
condition_variable();
~condition_variable();
condition_variable( condition_variable const&) = delete;
condition_variable & operator=( condition_variable const&) = delete;
void notify_one();
void notify_all();
template< typename LockType >
void wait( LockType & lk);
template< typename LockType, typename Pred >
void wait( LockType & lk, Pred predicate);
template< typename LockType, typename Clock, typename Duration >
cv_status wait_until( LockType & lk,
std::chrono::time_point< Clock, Duration > const& timeout_time);
template< typename LockType, typename Clock, typename Duration, typename Pred >
bool wait_until( LockType & lk,
std::chrono::time_point< Clock, Duration > const& timeout_time,
Pred pred);
template< typename LockType, typename Rep, typename Period >
cv_status wait_for( LockType & lk,
std::chrono::duration< Rep, Period > const& timeout_duration);
template< typename LockType, typename Rep, typename Period, typename Pred >
bool wait_for( LockType & lk,
std::chrono::duration< Rep, Period > const& timeout_duration,
Pred pred);
};
typedef condition_variable condition_variable_any;
[heading Constructor]
condition_variable()
[variablelist
[[Effects:] [Creates the object.]]
[[Throws:] [Nothing.]]
]
[heading Destructor]
~condition_variable()
[variablelist
[[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.]]
[[Throws:] [Nothing.]]
]
[member_heading condition_variable..notify_one]
void notify_one();
[variablelist
[[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_heading condition_variable..notify_all]
void notify_all();
[variablelist
[[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.]]
]
[template_member_heading condition_variable..wait]
template< typename LockType >
void wait( LockType & lk);
template< typename LockType, typename Pred >
void wait( LockType & lk, Pred pred);
[variablelist
[[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()`, or spuriously. 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_exception__ if an error
occurs. __fiber_interrupted__ if the wait was interrupted by a call to
__interrupt__ on the __fiber__ object associated with the current fiber of
execution.]]
[[Note:] [The Precondition is a bit dense. It merely states that all the
fibers 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 lock object (e.g.
`std::unique_lock`). In some sense it would be nice if the
`condition_variable`'s constructor could accept the related `mutex` object,
enforcing agreement across all `wait()` calls; but the existing APIs prevent
that. Instead we must require the `wait()` call to accept a reference to the
local lock object. It is an error to reuse a given `condition_variable`
instance with lock objects that reference ['different] underlying `mutex`
objects. It would be like a road intersection with traffic lights independent
of one another: sooner or later a collision will result.]]
]
[template_member_heading condition_variable..wait_until]
template< typename LockType, typename Clock, typename Duration >
cv_status wait_until( LockType & lk,
std::chrono::time_point< Clock, Duration > const& timeout_time);
template< typename LockType, typename Clock, typename Duration, typename Pred >
bool wait_until( LockType & lk,
std::chrono::time_point< Clock, Duration > const& timeout_time,
Pred pred);
[variablelist
[[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 `timeout_time`, or spuriously.
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, timeout_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_exception__ if an error
occurs. __fiber_interrupted__ if the wait was interrupted by a call to
__interrupt__ on the __fiber__ object associated with the current fiber of
execution.]]
[[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 `timeout_time`.]]
[[Returns:] [The overload accepting `pred` returns `false` if the call is
returning because the time specified by `timeout_time` was reached and the
predicate returns `false`, `true` otherwise.]]
[[Note:] [See [*Note] for [member_link condition_variable..wait].]]
]
[template_member_heading condition_variable..wait_for]
template< typename LockType, typename Rep, typename Period >
cv_status wait_for( LockType & lk,
std::chrono::duration< Rep, Period > const& timeout_duration);
template< typename LockType, typename Rep, typename Period, typename Pred >
bool wait_for( LockType & lk,
std::chrono::duration< Rep, Period > const& timeout_duration,
Pred pred);
[variablelist
[[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 `timeout_duration` has elapsed, or spuriously. 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, timeout_duration) ) {
return pred();
}
}
return true;
`` (except of course that `timeout_duration` 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_exception__ if an error
occurs. __fiber_interrupted__ if the wait was interrupted by a call to
__interrupt__ on the __fiber__ object associated with the current fiber of
execution.]]
[[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 `timeout_duration` has elapsed.]]
[[Returns:] [The overload accepting `pred` returns `false` if the call is
returning because at least `timeout_duration` has elapsed and the predicate
returns `false`, `true` otherwise.]]
[[Note:] [See [*Note] for [member_link condition_variable..wait].]]
]
[endsect]