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 21The class [class_link condition_variable] provides a mechanism for a fiber to 22wait for notification from another fiber. When the fiber awakens from the 23wait, then it checks to see if the appropriate condition is now true, and 24continues if so. If the condition is not true, then the fiber calls `wait` 25again to resume waiting. In the simplest case, this condition is just a 26boolean 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 44Notice 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 46condition variable, and unlock the [class_link mutex]. When the fiber is 47awakened, the `mutex` will be locked again before the call to `wait()` 48returns. This allows other fibers to acquire the `mutex` in order to update 49the shared data, and ensures that the data associated with the condition is 50correctly 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 63In the meantime, another fiber sets `data_ready` to `true`, and then calls 64either [member_link condition_variable..notify_one] or [member_link 65condition_variable..notify_all] on the [class_link condition_variable] `cond` 66to 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 81Note that the same [class_link mutex] is locked before the shared data is 82updated, but that the `mutex` does not have to be locked across the call to 83[member_link condition_variable..notify_one]. 84 85Locking is important because the synchronization objects provided by 86__boost_fiber__ can be used to synchronize fibers running on different 87threads. 88 89__boost_fiber__ provides both [class_link condition_variable] and [class_link 90condition_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 97Neither [class_link condition_variable] nor [class_link 98condition_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 102to use one of the `wait( lock, predicate )` overloads. 103 104Consider a set of consumer fibers processing items from a 105[@http://en.cppreference.com/w/cpp/container/queue `std::queue`]. The queue is 106continually populated by a set of producer fibers. 107 108The consumer fibers might reasonably wait on a `condition_variable` as long as 109the queue remains [@http://en.cppreference.com/w/cpp/container/queue/empty 110`empty()`]. 111 112Because producer fibers might 113[@http://en.cppreference.com/w/cpp/container/queue/push `push()`] items to the 114queue in bursts, they call [member_link condition_variable..notify_all] rather 115than [member_link condition_variable..notify_one]. 116 117But a given consumer fiber might well wake up from [member_link 118condition_variable..wait] and find the queue `empty()`, because other consumer 119fibers 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 126A 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 227call 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 238call 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 241program state using a mechanism external to the [`[classname]], and 242retry 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 244state 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 256fiber is currently waiting on `*this`, or the execution of the 257[@http://en.cppreference.com/w/cpp/thread/unique_lock/mutex `mutex()`] 258member function on the `lk` objects supplied in the calls to `wait` in all the 259fibers 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 262fiber will unblock when notified by a call to `this->notify_one()` or 263`this->notify_all()`. When the fiber is unblocked (for whatever 264reason), 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 266function exits with an exception. 267The member function accepting `pred` is shorthand for: `` 268 269while ( ! 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 276fibers concurrently calling `wait` on `*this` must wait on `lk` objects 277governing the ['same] [class_link mutex]. Three distinct objects are involved 278in any [`[classname]::wait()] call: the [`[classname]] itself, the `mutex` 279coordinating 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, 282separated by periods when no fibers are waiting on it. When more than one 283fiber is waiting on that [`[classname]], all must pass lock objects 284referencing 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 300fiber is currently waiting on `*this`, or the execution of the `mutex()` member 301function 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 303same value as `lk.mutex()` for this call to `wait_until`.]] 304[[Effects:] [Atomically call `lk.unlock()` and blocks the current fiber. The 305fiber will unblock when notified by a call to `this->notify_one()` or 306`this->notify_all()`, when the system time 307would be equal to or later than the specified `abs_time`. 308When the fiber is unblocked (for whatever reason), the lock is reacquired by 309invoking `lk.lock()` before the call to `wait_until` returns. The lock is also 310reacquired by invoking `lk.lock()` if the function exits with an exception. 311The member function accepting `pred` is shorthand for: `` 312 313while ( ! pred() ) { 314 if ( cv_status::timeout == wait_until( lk, abs_time) ) 315 return pred(); 316} 317return 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 323occurs or timeout-related exceptions.]] 324[[Returns:] [The overload without `pred` returns `cv_status::no_timeout` if 325awakened by `notify_one()` or `notify_all()`, or `cv_status::timeout` if 326awakened because the system time is past `abs_time`.]] 327[[Returns:] [The overload accepting `pred` returns `false` if the call is 328returning because the time specified by `abs_time` was reached and the 329predicate 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 346fiber is currently waiting on `*this`, or the execution of the `mutex()` member 347function 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 349same value as `lk.mutex()` for this call to `wait_for`.]] 350[[Effects:] [Atomically call `lk.unlock()` and blocks the current fiber. The 351fiber 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 353specified `rel_time` has elapsed. When the fiber is 354unblocked (for whatever reason), the lock is reacquired by invoking 355`lk.lock()` before the call to `wait` returns. The lock is also reacquired by 356invoking `lk.lock()` if the function exits with an exception. 357The `wait_for()` member function accepting `pred` is shorthand for: `` 358 359while ( ! pred() ) { 360 if ( cv_status::timeout == wait_for( lk, rel_time) ) { 361 return pred(); 362 } 363} 364return true; 365 366`` (except of course that `rel_time` is adjusted for each iteration). 367The point is that, even if `wait_for()` times out, it can still return `true` 368if `pred()` returns `true` at that time.]] 369[[Postcondition:] [`lk` is locked by the current fiber.]] 370[[Throws:] [__fiber_error__ if an error 371occurs or timeout-related exceptions.]] 372[[Returns:] [The overload without `pred` returns `cv_status::no_timeout` if 373awakened by `notify_one()` or `notify_all()`, or `cv_status::timeout` if 374awakened because at least `rel_time` has elapsed.]] 375[[Returns:] [The overload accepting `pred` returns `false` if the call is 376returning because at least `rel_time` has elapsed and the predicate 377returns `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] 396