condition_variables.qbk 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  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. [section:conditions Condition Variables]
  9. [heading Synopsis]
  10. enum class cv_status; {
  11. no_timeout,
  12. timeout
  13. };
  14. class condition_variable;
  15. class condition_variable_any;
  16. The class [class_link condition_variable] provides a mechanism for a fiber to
  17. wait for notification from another fiber. When the fiber awakens from the
  18. wait, then it checks to see if the appropriate condition is now true, and
  19. continues if so. If the condition is not true, then the fiber calls `wait`
  20. again to resume waiting. In the simplest case, this condition is just a
  21. boolean variable:
  22. boost::fibers::condition_variable cond;
  23. boost::fibers::mutex mtx;
  24. bool data_ready = false;
  25. void process_data();
  26. void wait_for_data_to_process() {
  27. {
  28. std::unique_lock< boost::fibers::mutex > lk( mtx);
  29. while ( ! data_ready) {
  30. cond.wait( lk);
  31. }
  32. } // release lk
  33. process_data();
  34. }
  35. Notice that the `lk` is passed to [member_link condition_variable..wait]:
  36. `wait()` will atomically add the fiber to the set of fibers waiting on the
  37. condition variable, and unlock the [class_link mutex]. When the fiber is
  38. awakened, the `mutex` will be locked again before the call to `wait()`
  39. returns. This allows other fibers to acquire the `mutex` in order to update
  40. the shared data, and ensures that the data associated with the condition is
  41. correctly synchronized.
  42. `wait_for_data_to_process()` could equivalently be written:
  43. void wait_for_data_to_process() {
  44. {
  45. std::unique_lock< boost::fibers::mutex > lk( mtx);
  46. // make condition_variable::wait() perform the loop
  47. cond.wait( lk, [](){ return data_ready; });
  48. } // release lk
  49. process_data();
  50. }
  51. In the meantime, another fiber sets `data_ready` to `true`, and then calls
  52. either [member_link condition_variable..notify_one] or [member_link
  53. condition_variable..notify_all] on the [class_link condition_variable] `cond`
  54. to wake one waiting fiber or all the waiting fibers respectively.
  55. void retrieve_data();
  56. void prepare_data();
  57. void prepare_data_for_processing() {
  58. retrieve_data();
  59. prepare_data();
  60. {
  61. std::unique_lock< boost::fibers::mutex > lk( mtx);
  62. data_ready = true;
  63. }
  64. cond.notify_one();
  65. }
  66. Note that the same [class_link mutex] is locked before the shared data is
  67. updated, but that the `mutex` does not have to be locked across the call to
  68. [member_link condition_variable..notify_one].
  69. Locking is important because the synchronization objects provided by
  70. __boost_fiber__ can be used to synchronize fibers running on different
  71. threads.
  72. __boost_fiber__ provides both [class_link condition_variable] and [class_link
  73. condition_variable_any]. `boost::fibers::condition_variable` can only wait on
  74. __unique_lock__`< boost::fibers::`[class_link mutex]` >` while
  75. `boost::fibers::condition_variable_any` can wait on user-defined lock types.
  76. [#condition_variable_spurious_wakeups]
  77. [heading No Spurious Wakeups]
  78. Neither [class_link condition_variable] nor [class_link
  79. condition_variable_any] are subject to spurious wakeup:
  80. [member_link condition_variable..wait] can only wake up when
  81. [member_link condition_variable..notify_one] or
  82. [member_link condition_variable..notify_all] is called. Even so, it is prudent
  83. to use one of the `wait( lock, predicate )` overloads.
  84. Consider a set of consumer fibers processing items from a
  85. [@http://en.cppreference.com/w/cpp/container/queue `std::queue`]. The queue is
  86. continually populated by a set of producer fibers.
  87. The consumer fibers might reasonably wait on a `condition_variable` as long as
  88. the queue remains [@http://en.cppreference.com/w/cpp/container/queue/empty
  89. `empty()`].
  90. Because producer fibers might
  91. [@http://en.cppreference.com/w/cpp/container/queue/push `push()`] items to the
  92. queue in bursts, they call [member_link condition_variable..notify_all] rather
  93. than [member_link condition_variable..notify_one].
  94. But a given consumer fiber might well wake up from [member_link
  95. condition_variable..wait] and find the queue `empty()`, because other consumer
  96. fibers might already have processed all pending items.
  97. (See also [link spurious_wakeup spurious wakeup].)
  98. [#class_cv_status]
  99. [heading Enumeration `cv_status`]
  100. A timed wait operation might return because of timeout or not.
  101. enum class cv_status {
  102. no_timeout,
  103. timeout
  104. };
  105. [heading `no_timeout`]
  106. [variablelist
  107. [[Effects:] [The condition variable was awakened with `notify_one` or `notify_all`.]]
  108. ]
  109. [heading `timeout`]
  110. [variablelist
  111. [[Effects:] [The condition variable was awakened by timeout.]]
  112. ]
  113. [/ The documentation for condition_variable_any and condition_variable is so
  114. nearly identical that we define a QuickBook template to capture its
  115. essentials. Differences are:
  116. the classname (of course),
  117. the locktype (a LockType template param vs. std::unique_lock<mutex>),
  118. the template parameter 'typename LockType',
  119. and -- for when it's the only template parameter -- the return type plus
  120. the template prefix 'template <typename LockType> void'.
  121. The last two really want to just vanish for condition_variable, but since
  122. you can't pass empty parameters to a QuickBook template (why??), the
  123. template_rtype param must supply the return type as well as the template <>
  124. clause, and the template_arg parameter must supply the 'typename' for the
  125. next template parameter as well as 'typename LockType'.]
  126. [template condition_variable_x[classname locktype template_rtype template_arg]
  127. [class_heading [classname]]
  128. #include <boost/fiber/condition_variable.hpp>
  129. namespace boost {
  130. namespace fibers {
  131. class ``[classname]`` {
  132. public:
  133. ``[classname]``();
  134. ~``[classname]``();
  135. ``[classname]``( ``[classname]`` const&) = delete;
  136. ``[classname]`` & operator=( ``[classname]`` const&) = delete;
  137. void notify_one() noexcept;
  138. void notify_all() noexcept;
  139. ``[template_rtype]`` wait( ``[locktype]`` &);
  140. template< ``[template_arg]`` Pred >
  141. void wait( ``[locktype]`` &, Pred);
  142. template< ``[template_arg]`` Clock, typename Duration >
  143. cv_status wait_until( ``[locktype]`` &,
  144. std::chrono::time_point< Clock, Duration > const&);
  145. template< ``[template_arg]`` Clock, typename Duration, typename Pred >
  146. bool wait_until( ``[locktype]`` &,
  147. std::chrono::time_point< Clock, Duration > const&,
  148. Pred);
  149. template< ``[template_arg]`` Rep, typename Period >
  150. cv_status wait_for( ``[locktype]`` &,
  151. std::chrono::duration< Rep, Period > const&);
  152. template< ``[template_arg]`` Rep, typename Period, typename Pred >
  153. bool wait_for( ``[locktype]`` &,
  154. std::chrono::duration< Rep, Period > const&,
  155. Pred);
  156. };
  157. }}
  158. [heading Constructor]
  159. ``[classname]``()
  160. [variablelist
  161. [[Effects:] [Creates the object.]]
  162. [[Throws:] [Nothing.]]
  163. ]
  164. [heading Destructor]
  165. ~``[classname]``()
  166. [variablelist
  167. [[Precondition:] [All fibers waiting on `*this` have been notified by a call to
  168. `notify_one` or `notify_all` (though the respective calls to `wait`, `wait_for` or
  169. `wait_until` need not have returned).]]
  170. [[Effects:] [Destroys the object.]]
  171. ]
  172. [member_heading [classname]..notify_one]
  173. void notify_one() noexcept;
  174. [variablelist
  175. [[Effects:] [If any fibers are currently __blocked__ waiting on `*this` in a
  176. call to `wait`, `wait_for` or `wait_until`, unblocks one of those fibers.]]
  177. [[Throws:] [Nothing.]]
  178. [[Note:] [It is arbitrary which waiting fiber is resumed.]]
  179. ]
  180. [member_heading [classname]..notify_all]
  181. void notify_all() noexcept;
  182. [variablelist
  183. [[Effects:] [If any fibers are currently __blocked__ waiting on `*this` in a
  184. call to `wait`, `wait_for` or `wait_until`, unblocks all of those fibers.]]
  185. [[Throws:] [Nothing.]]
  186. [[Note:] [This is why a waiting fiber must ['also] check for the desired
  187. program state using a mechanism external to the [`[classname]], and
  188. retry the wait until that state is reached. A fiber waiting on a
  189. [`[classname]] might well wake up a number of times before the desired
  190. state is reached.]]
  191. ]
  192. [template_member_heading [classname]..wait]
  193. ``[template_rtype]`` wait( ``[locktype]`` & lk);
  194. template< ``[template_arg]`` Pred >
  195. void wait( ``[locktype]`` & lk, Pred pred);
  196. [variablelist
  197. [[Precondition:] [`lk` is locked by the current fiber, and either no other
  198. fiber is currently waiting on `*this`, or the execution of the
  199. [@http://en.cppreference.com/w/cpp/thread/unique_lock/mutex `mutex()`]
  200. member function on the `lk` objects supplied in the calls to `wait` in all the
  201. fibers currently waiting on `*this` would return the same value as
  202. `lk->mutex()` for this call to `wait`.]]
  203. [[Effects:] [Atomically call `lk.unlock()` and blocks the current fiber. The
  204. fiber will unblock when notified by a call to `this->notify_one()` or
  205. `this->notify_all()`. When the fiber is unblocked (for whatever
  206. reason), the lock is reacquired by invoking `lk.lock()` before the call to
  207. `wait` returns. The lock is also reacquired by invoking `lk.lock()` if the
  208. function exits with an exception.
  209. The member function accepting `pred` is shorthand for: ``
  210. while ( ! pred() ) {
  211. wait( lk);
  212. }
  213. ``]]
  214. [[Postcondition:] [`lk` is locked by the current fiber.]]
  215. [[Throws:] [__fiber_error__ if an error occurs.]]
  216. [[Note:] [The Precondition is a bit dense. It merely states that all the
  217. fibers concurrently calling `wait` on `*this` must wait on `lk` objects
  218. governing the ['same] [class_link mutex]. Three distinct objects are involved
  219. in any [`[classname]::wait()] call: the [`[classname]] itself, the `mutex`
  220. coordinating access between fibers and a local lock object (e.g.
  221. __unique_lock__). In general, you can partition the lifespan of a given
  222. [`[classname]] instance into periods with one or more fibers waiting on it,
  223. separated by periods when no fibers are waiting on it. When more than one
  224. fiber is waiting on that [`[classname]], all must pass lock objects
  225. referencing the ['same] `mutex` instance.]]
  226. ]
  227. [template_member_heading [classname]..wait_until]
  228. template< ``[template_arg]`` Clock, typename Duration >
  229. cv_status wait_until( ``[locktype]`` & lk,
  230. std::chrono::time_point< Clock, Duration > const& abs_time);
  231. template< ``[template_arg]`` Clock, typename Duration, typename Pred >
  232. bool wait_until( ``[locktype]`` & lk,
  233. std::chrono::time_point< Clock, Duration > const& abs_time,
  234. Pred pred);
  235. [variablelist
  236. [[Precondition:] [`lk` is locked by the current fiber, and either no other
  237. fiber is currently waiting on `*this`, or the execution of the `mutex()` member
  238. function on the `lk` objects supplied in the calls to `wait`, `wait_for` or
  239. `wait_until` in all the fibers currently waiting on `*this` would return the
  240. same value as `lk.mutex()` for this call to `wait_until`.]]
  241. [[Effects:] [Atomically call `lk.unlock()` and blocks the current fiber. The
  242. fiber will unblock when notified by a call to `this->notify_one()` or
  243. `this->notify_all()`, when the system time
  244. would be equal to or later than the specified `abs_time`.
  245. When the fiber is unblocked (for whatever reason), the lock is reacquired by
  246. invoking `lk.lock()` before the call to `wait_until` returns. The lock is also
  247. reacquired by invoking `lk.lock()` if the function exits with an exception.
  248. The member function accepting `pred` is shorthand for: ``
  249. while ( ! pred() ) {
  250. if ( cv_status::timeout == wait_until( lk, abs_time) )
  251. return pred();
  252. }
  253. return true;
  254. `` That is, even if `wait_until()` times out, it can still return `true` if
  255. `pred()` returns `true` at that time.]]
  256. [[Postcondition:] [`lk` is locked by the current fiber.]]
  257. [[Throws:] [__fiber_error__ if an error
  258. occurs or timeout-related exceptions.]]
  259. [[Returns:] [The overload without `pred` returns `cv_status::no_timeout` if
  260. awakened by `notify_one()` or `notify_all()`, or `cv_status::timeout` if
  261. awakened because the system time is past `abs_time`.]]
  262. [[Returns:] [The overload accepting `pred` returns `false` if the call is
  263. returning because the time specified by `abs_time` was reached and the
  264. predicate returns `false`, `true` otherwise.]]
  265. [[Note:] [See [*Note] for [member_link [classname]..wait].]]
  266. ]
  267. [template_member_heading [classname]..wait_for]
  268. template< ``[template_arg]`` Rep, typename Period >
  269. cv_status wait_for( ``[locktype]`` & lk,
  270. std::chrono::duration< Rep, Period > const& rel_time);
  271. template< ``[template_arg]`` Rep, typename Period, typename Pred >
  272. bool wait_for( ``[locktype]`` & lk,
  273. std::chrono::duration< Rep, Period > const& rel_time,
  274. Pred pred);
  275. [variablelist
  276. [[Precondition:] [`lk` is locked by the current fiber, and either no other
  277. fiber is currently waiting on `*this`, or the execution of the `mutex()` member
  278. function on the `lk` objects supplied in the calls to `wait`, `wait_for` or
  279. `wait_until` in all the fibers currently waiting on `*this` would return the
  280. same value as `lk.mutex()` for this call to `wait_for`.]]
  281. [[Effects:] [Atomically call `lk.unlock()` and blocks the current fiber. The
  282. fiber will unblock when notified by a call to `this->notify_one()` or
  283. `this->notify_all()`, when a time interval equal to or greater than the
  284. specified `rel_time` has elapsed. When the fiber is
  285. unblocked (for whatever reason), the lock is reacquired by invoking
  286. `lk.lock()` before the call to `wait` returns. The lock is also reacquired by
  287. invoking `lk.lock()` if the function exits with an exception.
  288. The `wait_for()` member function accepting `pred` is shorthand for: ``
  289. while ( ! pred() ) {
  290. if ( cv_status::timeout == wait_for( lk, rel_time) ) {
  291. return pred();
  292. }
  293. }
  294. return true;
  295. `` (except of course that `rel_time` is adjusted for each iteration).
  296. The point is that, even if `wait_for()` times out, it can still return `true`
  297. if `pred()` returns `true` at that time.]]
  298. [[Postcondition:] [`lk` is locked by the current fiber.]]
  299. [[Throws:] [__fiber_error__ if an error
  300. occurs or timeout-related exceptions.]]
  301. [[Returns:] [The overload without `pred` returns `cv_status::no_timeout` if
  302. awakened by `notify_one()` or `notify_all()`, or `cv_status::timeout` if
  303. awakened because at least `rel_time` has elapsed.]]
  304. [[Returns:] [The overload accepting `pred` returns `false` if the call is
  305. returning because at least `rel_time` has elapsed and the predicate
  306. returns `false`, `true` otherwise.]]
  307. [[Note:] [See [*Note] for [member_link [classname]..wait].]]
  308. ]
  309. ]
  310. [/ The above is the template describing both condition_variable_any and
  311. condition_variable. We must invoke it to generate those descriptions. First
  312. condition_variable_any, which accepts any LockType. Because a QuickBook
  313. template argument cannot be empty, the template<> prefix must also supply
  314. the 'void' return type for wait(); likewise the 'typename LockType'
  315. template argument must also supply the following 'typename'.]
  316. [condition_variable_x condition_variable_any..LockType..template< typename LockType >
  317. void..typename LockType, typename]
  318. [/ Now condition_variable, which accepts specifically std::unique_lock<mutex>.
  319. Make the template<> prefix for wait() go away by supplying only its return
  320. type 'void'; make the 'typename LockType' template argument go away by
  321. supplying only the following 'typename'.]
  322. [condition_variable_x condition_variable..std::unique_lock< mutex >..void..typename]
  323. [endsect]