2 (C) Copyright 2007-8 Anthony Williams.
3 (C) Copyright 2013 Oliver Kowalke.
4 Distributed under the Boost Software License, Version 1.0.
5 (See accompanying file LICENSE_1_0.txt or copy at
6 http://www.boost.org/LICENSE_1_0.txt).
9 [section:conditions Condition Variables]
13 enum class cv_status; {
18 class condition_variable;
19 class condition_variable_any;
21 The class [class_link condition_variable] provides a mechanism for a fiber to
22 wait for notification from another fiber. When the fiber awakens from the
23 wait, then it checks to see if the appropriate condition is now true, and
24 continues if so. If the condition is not true, then the fiber calls `wait`
25 again to resume waiting. In the simplest case, this condition is just a
28 boost::fibers::condition_variable cond;
29 boost::fibers::mutex mtx;
30 bool data_ready = false;
34 void wait_for_data_to_process() {
36 std::unique_lock< boost::fibers::mutex > lk( mtx);
37 while ( ! data_ready) {
44 Notice that the `lk` is passed to [member_link condition_variable..wait]:
45 `wait()` will atomically add the fiber to the set of fibers waiting on the
46 condition variable, and unlock the [class_link mutex]. When the fiber is
47 awakened, the `mutex` will be locked again before the call to `wait()`
48 returns. This allows other fibers to acquire the `mutex` in order to update
49 the shared data, and ensures that the data associated with the condition is
50 correctly synchronized.
52 `wait_for_data_to_process()` could equivalently be written:
54 void wait_for_data_to_process() {
56 std::unique_lock< boost::fibers::mutex > lk( mtx);
57 // make condition_variable::wait() perform the loop
58 cond.wait( lk, [](){ return data_ready; });
63 In the meantime, another fiber sets `data_ready` to `true`, and then calls
64 either [member_link condition_variable..notify_one] or [member_link
65 condition_variable..notify_all] on the [class_link condition_variable] `cond`
66 to wake one waiting fiber or all the waiting fibers respectively.
71 void prepare_data_for_processing() {
75 std::unique_lock< boost::fibers::mutex > lk( mtx);
81 Note that the same [class_link mutex] is locked before the shared data is
82 updated, but that the `mutex` does not have to be locked across the call to
83 [member_link condition_variable..notify_one].
85 Locking is important because the synchronization objects provided by
86 __boost_fiber__ can be used to synchronize fibers running on different
89 __boost_fiber__ provides both [class_link condition_variable] and [class_link
90 condition_variable_any]. `boost::fibers::condition_variable` can only wait on
91 __unique_lock__`< boost::fibers::`[class_link mutex]` >` while
92 `boost::fibers::condition_variable_any` can wait on user-defined lock types.
94 [#condition_variable_spurious_wakeups]
95 [heading No Spurious Wakeups]
97 Neither [class_link condition_variable] nor [class_link
98 condition_variable_any] are subject to spurious wakeup:
99 [member_link condition_variable..wait] can only wake up when
100 [member_link condition_variable..notify_one] or
101 [member_link condition_variable..notify_all] is called. Even so, it is prudent
102 to use one of the `wait( lock, predicate )` overloads.
104 Consider a set of consumer fibers processing items from a
105 [@http://en.cppreference.com/w/cpp/container/queue `std::queue`]. The queue is
106 continually populated by a set of producer fibers.
108 The consumer fibers might reasonably wait on a `condition_variable` as long as
109 the queue remains [@http://en.cppreference.com/w/cpp/container/queue/empty
112 Because producer fibers might
113 [@http://en.cppreference.com/w/cpp/container/queue/push `push()`] items to the
114 queue in bursts, they call [member_link condition_variable..notify_all] rather
115 than [member_link condition_variable..notify_one].
117 But a given consumer fiber might well wake up from [member_link
118 condition_variable..wait] and find the queue `empty()`, because other consumer
119 fibers might already have processed all pending items.
121 (See also [link spurious_wakeup spurious wakeup].)
124 [heading Enumeration `cv_status`]
126 A timed wait operation might return because of timeout or not.
128 enum class cv_status {
133 [heading `no_timeout`]
135 [[Effects:] [The condition variable was awakened with `notify_one` or `notify_all`.]]
140 [[Effects:] [The condition variable was awakened by timeout.]]
143 [/ The documentation for condition_variable_any and condition_variable is so
144 nearly identical that we define a QuickBook template to capture its
145 essentials. Differences are:
146 the classname (of course),
147 the locktype (a LockType template param vs. std::unique_lock<mutex>),
148 the template parameter 'typename LockType',
149 and -- for when it's the only template parameter -- the return type plus
150 the template prefix 'template <typename LockType> void'.
151 The last two really want to just vanish for condition_variable, but since
152 you can't pass empty parameters to a QuickBook template (why??), the
153 template_rtype param must supply the return type as well as the template <>
154 clause, and the template_arg parameter must supply the 'typename' for the
155 next template parameter as well as 'typename LockType'.]
156 [template condition_variable_x[classname locktype template_rtype template_arg]
157 [class_heading [classname]]
159 #include <boost/fiber/condition_variable.hpp>
164 class ``[classname]`` {
169 ``[classname]``( ``[classname]`` const&) = delete;
170 ``[classname]`` & operator=( ``[classname]`` const&) = delete;
172 void notify_one() noexcept;
173 void notify_all() noexcept;
175 ``[template_rtype]`` wait( ``[locktype]`` &);
177 template< ``[template_arg]`` Pred >
178 void wait( ``[locktype]`` &, Pred);
180 template< ``[template_arg]`` Clock, typename Duration >
181 cv_status wait_until( ``[locktype]`` &,
182 std::chrono::time_point< Clock, Duration > const&);
184 template< ``[template_arg]`` Clock, typename Duration, typename Pred >
185 bool wait_until( ``[locktype]`` &,
186 std::chrono::time_point< Clock, Duration > const&,
189 template< ``[template_arg]`` Rep, typename Period >
190 cv_status wait_for( ``[locktype]`` &,
191 std::chrono::duration< Rep, Period > const&);
193 template< ``[template_arg]`` Rep, typename Period, typename Pred >
194 bool wait_for( ``[locktype]`` &,
195 std::chrono::duration< Rep, Period > const&,
201 [heading Constructor]
206 [[Effects:] [Creates the object.]]
207 [[Throws:] [Nothing.]]
215 [[Precondition:] [All fibers waiting on `*this` have been notified by a call to
216 `notify_one` or `notify_all` (though the respective calls to `wait`, `wait_for` or
217 `wait_until` need not have returned).]]
218 [[Effects:] [Destroys the object.]]
221 [member_heading [classname]..notify_one]
223 void notify_one() noexcept;
226 [[Effects:] [If any fibers are currently __blocked__ waiting on `*this` in a
227 call to `wait`, `wait_for` or `wait_until`, unblocks one of those fibers.]]
228 [[Throws:] [Nothing.]]
229 [[Note:] [It is arbitrary which waiting fiber is resumed.]]
232 [member_heading [classname]..notify_all]
234 void notify_all() noexcept;
237 [[Effects:] [If any fibers are currently __blocked__ waiting on `*this` in a
238 call to `wait`, `wait_for` or `wait_until`, unblocks all of those fibers.]]
239 [[Throws:] [Nothing.]]
240 [[Note:] [This is why a waiting fiber must ['also] check for the desired
241 program state using a mechanism external to the [`[classname]], and
242 retry the wait until that state is reached. A fiber waiting on a
243 [`[classname]] might well wake up a number of times before the desired
247 [template_member_heading [classname]..wait]
249 ``[template_rtype]`` wait( ``[locktype]`` & lk);
251 template< ``[template_arg]`` Pred >
252 void wait( ``[locktype]`` & lk, Pred pred);
255 [[Precondition:] [`lk` is locked by the current fiber, and either no other
256 fiber is currently waiting on `*this`, or the execution of the
257 [@http://en.cppreference.com/w/cpp/thread/unique_lock/mutex `mutex()`]
258 member function on the `lk` objects supplied in the calls to `wait` in all the
259 fibers currently waiting on `*this` would return the same value as
260 `lk->mutex()` for this call to `wait`.]]
261 [[Effects:] [Atomically call `lk.unlock()` and blocks the current fiber. The
262 fiber will unblock when notified by a call to `this->notify_one()` or
263 `this->notify_all()`. When the fiber is unblocked (for whatever
264 reason), the lock is reacquired by invoking `lk.lock()` before the call to
265 `wait` returns. The lock is also reacquired by invoking `lk.lock()` if the
266 function exits with an exception.
267 The member function accepting `pred` is shorthand for: ``
273 [[Postcondition:] [`lk` is locked by the current fiber.]]
274 [[Throws:] [__fiber_error__ if an error occurs.]]
275 [[Note:] [The Precondition is a bit dense. It merely states that all the
276 fibers concurrently calling `wait` on `*this` must wait on `lk` objects
277 governing the ['same] [class_link mutex]. Three distinct objects are involved
278 in any [`[classname]::wait()] call: the [`[classname]] itself, the `mutex`
279 coordinating access between fibers and a local lock object (e.g.
280 __unique_lock__). In general, you can partition the lifespan of a given
281 [`[classname]] instance into periods with one or more fibers waiting on it,
282 separated by periods when no fibers are waiting on it. When more than one
283 fiber is waiting on that [`[classname]], all must pass lock objects
284 referencing the ['same] `mutex` instance.]]
287 [template_member_heading [classname]..wait_until]
289 template< ``[template_arg]`` Clock, typename Duration >
290 cv_status wait_until( ``[locktype]`` & lk,
291 std::chrono::time_point< Clock, Duration > const& abs_time);
293 template< ``[template_arg]`` Clock, typename Duration, typename Pred >
294 bool wait_until( ``[locktype]`` & lk,
295 std::chrono::time_point< Clock, Duration > const& abs_time,
299 [[Precondition:] [`lk` is locked by the current fiber, and either no other
300 fiber is currently waiting on `*this`, or the execution of the `mutex()` member
301 function on the `lk` objects supplied in the calls to `wait`, `wait_for` or
302 `wait_until` in all the fibers currently waiting on `*this` would return the
303 same value as `lk.mutex()` for this call to `wait_until`.]]
304 [[Effects:] [Atomically call `lk.unlock()` and blocks the current fiber. The
305 fiber will unblock when notified by a call to `this->notify_one()` or
306 `this->notify_all()`, when the system time
307 would be equal to or later than the specified `abs_time`.
308 When the fiber is unblocked (for whatever reason), the lock is reacquired by
309 invoking `lk.lock()` before the call to `wait_until` returns. The lock is also
310 reacquired by invoking `lk.lock()` if the function exits with an exception.
311 The member function accepting `pred` is shorthand for: ``
314 if ( cv_status::timeout == wait_until( lk, abs_time) )
319 `` That is, even if `wait_until()` times out, it can still return `true` if
320 `pred()` returns `true` at that time.]]
321 [[Postcondition:] [`lk` is locked by the current fiber.]]
322 [[Throws:] [__fiber_error__ if an error
323 occurs or timeout-related exceptions.]]
324 [[Returns:] [The overload without `pred` returns `cv_status::no_timeout` if
325 awakened by `notify_one()` or `notify_all()`, or `cv_status::timeout` if
326 awakened because the system time is past `abs_time`.]]
327 [[Returns:] [The overload accepting `pred` returns `false` if the call is
328 returning because the time specified by `abs_time` was reached and the
329 predicate returns `false`, `true` otherwise.]]
330 [[Note:] [See [*Note] for [member_link [classname]..wait].]]
333 [template_member_heading [classname]..wait_for]
335 template< ``[template_arg]`` Rep, typename Period >
336 cv_status wait_for( ``[locktype]`` & lk,
337 std::chrono::duration< Rep, Period > const& rel_time);
339 template< ``[template_arg]`` Rep, typename Period, typename Pred >
340 bool wait_for( ``[locktype]`` & lk,
341 std::chrono::duration< Rep, Period > const& rel_time,
345 [[Precondition:] [`lk` is locked by the current fiber, and either no other
346 fiber is currently waiting on `*this`, or the execution of the `mutex()` member
347 function on the `lk` objects supplied in the calls to `wait`, `wait_for` or
348 `wait_until` in all the fibers currently waiting on `*this` would return the
349 same value as `lk.mutex()` for this call to `wait_for`.]]
350 [[Effects:] [Atomically call `lk.unlock()` and blocks the current fiber. The
351 fiber will unblock when notified by a call to `this->notify_one()` or
352 `this->notify_all()`, when a time interval equal to or greater than the
353 specified `rel_time` has elapsed. When the fiber is
354 unblocked (for whatever reason), the lock is reacquired by invoking
355 `lk.lock()` before the call to `wait` returns. The lock is also reacquired by
356 invoking `lk.lock()` if the function exits with an exception.
357 The `wait_for()` member function accepting `pred` is shorthand for: ``
360 if ( cv_status::timeout == wait_for( lk, rel_time) ) {
366 `` (except of course that `rel_time` is adjusted for each iteration).
367 The point is that, even if `wait_for()` times out, it can still return `true`
368 if `pred()` returns `true` at that time.]]
369 [[Postcondition:] [`lk` is locked by the current fiber.]]
370 [[Throws:] [__fiber_error__ if an error
371 occurs or timeout-related exceptions.]]
372 [[Returns:] [The overload without `pred` returns `cv_status::no_timeout` if
373 awakened by `notify_one()` or `notify_all()`, or `cv_status::timeout` if
374 awakened because at least `rel_time` has elapsed.]]
375 [[Returns:] [The overload accepting `pred` returns `false` if the call is
376 returning because at least `rel_time` has elapsed and the predicate
377 returns `false`, `true` otherwise.]]
378 [[Note:] [See [*Note] for [member_link [classname]..wait].]]
381 [/ The above is the template describing both condition_variable_any and
382 condition_variable. We must invoke it to generate those descriptions. First
383 condition_variable_any, which accepts any LockType. Because a QuickBook
384 template argument cannot be empty, the template<> prefix must also supply
385 the 'void' return type for wait(); likewise the 'typename LockType'
386 template argument must also supply the following 'typename'.]
387 [condition_variable_x condition_variable_any..LockType..template< typename LockType >
388 void..typename LockType, typename]
389 [/ Now condition_variable, which accepts specifically std::unique_lock<mutex>.
390 Make the template<> prefix for wait() go away by supplying only its return
391 type 'void'; make the 'typename LockType' template argument go away by
392 supplying only the following 'typename'.]
393 [condition_variable_x condition_variable..std::unique_lock< mutex >..void..typename]