]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/fiber/doc/condition_variables.qbk
bump version to 12.2.2-pve1
[ceph.git] / ceph / src / boost / libs / fiber / doc / condition_variables.qbk
1 [/
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).
7 ]
8
9 [section:conditions Condition Variables]
10
11 [heading Synopsis]
12
13 enum class cv_status; {
14 no_timeout,
15 timeout
16 };
17
18 class condition_variable;
19 class condition_variable_any;
20
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
26 boolean variable:
27
28 boost::fibers::condition_variable cond;
29 boost::fibers::mutex mtx;
30 bool data_ready = false;
31
32 void process_data();
33
34 void wait_for_data_to_process() {
35 {
36 std::unique_lock< boost::fibers::mutex > lk( mtx);
37 while ( ! data_ready) {
38 cond.wait( lk);
39 }
40 } // release lk
41 process_data();
42 }
43
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.
51
52 `wait_for_data_to_process()` could equivalently be written:
53
54 void wait_for_data_to_process() {
55 {
56 std::unique_lock< boost::fibers::mutex > lk( mtx);
57 // make condition_variable::wait() perform the loop
58 cond.wait( lk, [](){ return data_ready; });
59 } // release lk
60 process_data();
61 }
62
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.
67
68 void retrieve_data();
69 void prepare_data();
70
71 void prepare_data_for_processing() {
72 retrieve_data();
73 prepare_data();
74 {
75 std::unique_lock< boost::fibers::mutex > lk( mtx);
76 data_ready = true;
77 }
78 cond.notify_one();
79 }
80
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].
84
85 Locking is important because the synchronization objects provided by
86 __boost_fiber__ can be used to synchronize fibers running on different
87 threads.
88
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.
93
94 [#condition_variable_spurious_wakeups]
95 [heading No Spurious Wakeups]
96
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.
103
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.
107
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
110 `empty()`].
111
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].
116
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.
120
121 (See also [link spurious_wakeup spurious wakeup].)
122
123 [#class_cv_status]
124 [heading Enumeration `cv_status`]
125
126 A timed wait operation might return because of timeout or not.
127
128 enum class cv_status {
129 no_timeout,
130 timeout
131 };
132
133 [heading `no_timeout`]
134 [variablelist
135 [[Effects:] [The condition variable was awakened with `notify_one` or `notify_all`.]]
136 ]
137
138 [heading `timeout`]
139 [variablelist
140 [[Effects:] [The condition variable was awakened by timeout.]]
141 ]
142
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]]
158
159 #include <boost/fiber/condition_variable.hpp>
160
161 namespace boost {
162 namespace fibers {
163
164 class ``[classname]`` {
165 public:
166 ``[classname]``();
167 ~``[classname]``();
168
169 ``[classname]``( ``[classname]`` const&) = delete;
170 ``[classname]`` & operator=( ``[classname]`` const&) = delete;
171
172 void notify_one() noexcept;
173 void notify_all() noexcept;
174
175 ``[template_rtype]`` wait( ``[locktype]`` &);
176
177 template< ``[template_arg]`` Pred >
178 void wait( ``[locktype]`` &, Pred);
179
180 template< ``[template_arg]`` Clock, typename Duration >
181 cv_status wait_until( ``[locktype]`` &,
182 std::chrono::time_point< Clock, Duration > const&);
183
184 template< ``[template_arg]`` Clock, typename Duration, typename Pred >
185 bool wait_until( ``[locktype]`` &,
186 std::chrono::time_point< Clock, Duration > const&,
187 Pred);
188
189 template< ``[template_arg]`` Rep, typename Period >
190 cv_status wait_for( ``[locktype]`` &,
191 std::chrono::duration< Rep, Period > const&);
192
193 template< ``[template_arg]`` Rep, typename Period, typename Pred >
194 bool wait_for( ``[locktype]`` &,
195 std::chrono::duration< Rep, Period > const&,
196 Pred);
197 };
198
199 }}
200
201 [heading Constructor]
202
203 ``[classname]``()
204
205 [variablelist
206 [[Effects:] [Creates the object.]]
207 [[Throws:] [Nothing.]]
208 ]
209
210 [heading Destructor]
211
212 ~``[classname]``()
213
214 [variablelist
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.]]
219 ]
220
221 [member_heading [classname]..notify_one]
222
223 void notify_one() noexcept;
224
225 [variablelist
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.]]
230 ]
231
232 [member_heading [classname]..notify_all]
233
234 void notify_all() noexcept;
235
236 [variablelist
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
244 state is reached.]]
245 ]
246
247 [template_member_heading [classname]..wait]
248
249 ``[template_rtype]`` wait( ``[locktype]`` & lk);
250
251 template< ``[template_arg]`` Pred >
252 void wait( ``[locktype]`` & lk, Pred pred);
253
254 [variablelist
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: ``
268
269 while ( ! pred() ) {
270 wait( lk);
271 }
272 ``]]
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.]]
285 ]
286
287 [template_member_heading [classname]..wait_until]
288
289 template< ``[template_arg]`` Clock, typename Duration >
290 cv_status wait_until( ``[locktype]`` & lk,
291 std::chrono::time_point< Clock, Duration > const& abs_time);
292
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,
296 Pred pred);
297
298 [variablelist
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: ``
312
313 while ( ! pred() ) {
314 if ( cv_status::timeout == wait_until( lk, abs_time) )
315 return pred();
316 }
317 return true;
318
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].]]
331 ]
332
333 [template_member_heading [classname]..wait_for]
334
335 template< ``[template_arg]`` Rep, typename Period >
336 cv_status wait_for( ``[locktype]`` & lk,
337 std::chrono::duration< Rep, Period > const& rel_time);
338
339 template< ``[template_arg]`` Rep, typename Period, typename Pred >
340 bool wait_for( ``[locktype]`` & lk,
341 std::chrono::duration< Rep, Period > const& rel_time,
342 Pred pred);
343
344 [variablelist
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: ``
358
359 while ( ! pred() ) {
360 if ( cv_status::timeout == wait_for( lk, rel_time) ) {
361 return pred();
362 }
363 }
364 return true;
365
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].]]
379 ]
380 ]
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]
394
395 [endsect]