100.00% Lines (67/67) 100.00% Functions (12/12)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com) 2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3   // Copyright (c) 2026 Michael Vandeberg 3   // Copyright (c) 2026 Michael Vandeberg
4   // 4   //
5   // Distributed under the Boost Software License, Version 1.0. (See accompanying 5   // Distributed under the Boost Software License, Version 1.0. (See accompanying
6   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7   // 7   //
8   // Official repository: https://github.com/cppalliance/capy 8   // Official repository: https://github.com/cppalliance/capy
9   // 9   //
10   10  
11   #ifndef BOOST_CAPY_ASYNC_EVENT_HPP 11   #ifndef BOOST_CAPY_ASYNC_EVENT_HPP
12   #define BOOST_CAPY_ASYNC_EVENT_HPP 12   #define BOOST_CAPY_ASYNC_EVENT_HPP
13   13  
14   #include <boost/capy/detail/config.hpp> 14   #include <boost/capy/detail/config.hpp>
15   #include <boost/capy/detail/intrusive.hpp> 15   #include <boost/capy/detail/intrusive.hpp>
16   #include <boost/capy/continuation.hpp> 16   #include <boost/capy/continuation.hpp>
17   #include <boost/capy/concept/executor.hpp> 17   #include <boost/capy/concept/executor.hpp>
18   #include <boost/capy/error.hpp> 18   #include <boost/capy/error.hpp>
19   #include <boost/capy/ex/io_env.hpp> 19   #include <boost/capy/ex/io_env.hpp>
20   #include <boost/capy/io_result.hpp> 20   #include <boost/capy/io_result.hpp>
21   21  
22   #include <stop_token> 22   #include <stop_token>
23   23  
24   #include <atomic> 24   #include <atomic>
25   #include <coroutine> 25   #include <coroutine>
26   #include <new> 26   #include <new>
27   #include <utility> 27   #include <utility>
28   28  
29   /* async_event implementation notes 29   /* async_event implementation notes
30   ================================= 30   =================================
31   31  
32   Same cancellation pattern as async_mutex (see that file for the 32   Same cancellation pattern as async_mutex (see that file for the
33   full discussion on claimed_, stop_cb lifetime, member ordering, 33   full discussion on claimed_, stop_cb lifetime, member ordering,
34   and threading assumptions). 34   and threading assumptions).
35   35  
36   Key difference: set() wakes ALL waiters (broadcast), not one. 36   Key difference: set() wakes ALL waiters (broadcast), not one.
37   It pops every waiter from the list and posts the ones it 37   It pops every waiter from the list and posts the ones it
38   claims. Waiters already claimed by a stop callback are skipped. 38   claims. Waiters already claimed by a stop callback are skipped.
39   39  
40   Because set() pops all waiters, a canceled waiter may have been 40   Because set() pops all waiters, a canceled waiter may have been
41   removed from the list by set() before its await_resume runs. 41   removed from the list by set() before its await_resume runs.
42   This requires a separate in_list_ flag (unlike async_mutex where 42   This requires a separate in_list_ flag (unlike async_mutex where
43   active_ served double duty). await_resume only calls remove() 43   active_ served double duty). await_resume only calls remove()
44   when in_list_ is true. 44   when in_list_ is true.
45   */ 45   */
46   46  
47   namespace boost { 47   namespace boost {
48   namespace capy { 48   namespace capy {
49   49  
50   /** Queues coroutines in `wait()` and resumes all of them when `set()` is called. 50   /** Queues coroutines in `wait()` and resumes all of them when `set()` is called.
51   51  
52   This event provides a way to notify multiple coroutines that some 52   This event provides a way to notify multiple coroutines that some
53   condition has occurred. When a coroutine awaits an unset event, it 53   condition has occurred. When a coroutine awaits an unset event, it
54   suspends and is added to a wait queue. When the event is set, all 54   suspends and is added to a wait queue. When the event is set, all
55   waiting coroutines are resumed. 55   waiting coroutines are resumed.
56   56  
57   @par Cancellation 57   @par Cancellation
58   58  
59   When a coroutine is suspended waiting for the event and its stop 59   When a coroutine is suspended waiting for the event and its stop
60   token is triggered, the waiter completes with `error::canceled` 60   token is triggered, the waiter completes with `error::canceled`
61   instead of waiting for `set()`. 61   instead of waiting for `set()`.
62   62  
63   Cancellation only applies while the coroutine is suspended in the 63   Cancellation only applies while the coroutine is suspended in the
64   wait queue. If the event is already set when `wait()` is called, 64   wait queue. If the event is already set when `wait()` is called,
65   the wait completes immediately even if the stop token is already 65   the wait completes immediately even if the stop token is already
66   signaled. 66   signaled.
67   67  
68   @par Zero Allocation 68   @par Zero Allocation
69   69  
70   No heap allocation occurs for wait operations. 70   No heap allocation occurs for wait operations.
71   71  
72   @par Thread Safety 72   @par Thread Safety
73   73  
74   Distinct objects: Safe.@n 74   Distinct objects: Safe.@n
75   Shared objects: Unsafe. 75   Shared objects: Unsafe.
76   76  
77   The event operations are designed for single-threaded use on one 77   The event operations are designed for single-threaded use on one
78   executor. The stop callback may fire from any thread. 78   executor. The stop callback may fire from any thread.
79   79  
80   This type is non-copyable and non-movable because suspended 80   This type is non-copyable and non-movable because suspended
81   waiters hold intrusive pointers into the event's internal list. 81   waiters hold intrusive pointers into the event's internal list.
82   82  
83   @par Example 83   @par Example
84 - @code 84 + @par !example example
85 - async_event event;  
86 -  
87 - task<> waiter() {  
88 - auto [ec] = co_await event.wait();  
89 - if(ec)  
90 - co_return;  
91 - // ... event was set ...  
92 - }  
93 - task<> notifier() {  
94 - // ... do some work ...  
95 - event.set(); // Wake all waiters  
96 - }  
97 - @endcode  
98   85  
99   */ 86   */
100   class async_event 87   class async_event
101   { 88   {
102   public: 89   public:
103   class wait_awaiter; 90   class wait_awaiter;
104   91  
105   private: 92   private:
106   bool set_ = false; 93   bool set_ = false;
107   detail::intrusive_list<wait_awaiter> waiters_; 94   detail::intrusive_list<wait_awaiter> waiters_;
108   95  
109   public: 96   public:
110   /** Suspends the caller until `set()` runs, or resumes it with `error::canceled` on a stop request. 97   /** Suspends the caller until `set()` runs, or resumes it with `error::canceled` on a stop request.
111   */ 98   */
112   class wait_awaiter 99   class wait_awaiter
113   : public detail::intrusive_list<wait_awaiter>::node 100   : public detail::intrusive_list<wait_awaiter>::node
114   { 101   {
115   friend class async_event; 102   friend class async_event;
116   103  
117   async_event* e_; 104   async_event* e_;
118   continuation cont_; 105   continuation cont_;
119   executor_ref ex_; 106   executor_ref ex_;
120   107  
121   // Declared before stop_cb_buf_: the callback 108   // Declared before stop_cb_buf_: the callback
122   // accesses these members, so they must still be 109   // accesses these members, so they must still be
123   // alive if the stop_cb_ destructor blocks. 110   // alive if the stop_cb_ destructor blocks.
124   std::atomic<bool> claimed_{false}; 111   std::atomic<bool> claimed_{false};
125   bool canceled_ = false; 112   bool canceled_ = false;
126   bool active_ = false; 113   bool active_ = false;
127   bool in_list_ = false; 114   bool in_list_ = false;
128   115  
129   struct cancel_fn 116   struct cancel_fn
130   { 117   {
131   wait_awaiter* self_; 118   wait_awaiter* self_;
132   119  
HITCBC 133   4 void operator()() const noexcept 120   4 void operator()() const noexcept
134   { 121   {
HITCBC 135   4 if(!self_->claimed_.exchange( 122   4 if(!self_->claimed_.exchange(
136   true, std::memory_order_acq_rel)) 123   true, std::memory_order_acq_rel))
137   { 124   {
HITCBC 138   3 self_->canceled_ = true; 125   3 self_->canceled_ = true;
HITCBC 139   3 self_->ex_.post(self_->cont_); 126   3 self_->ex_.post(self_->cont_);
140   } 127   }
HITCBC 141   4 } 128   4 }
142   }; 129   };
143   130  
144   using stop_cb_t = 131   using stop_cb_t =
145   std::stop_callback<cancel_fn>; 132   std::stop_callback<cancel_fn>;
146   133  
147   // Aligned storage for stop_cb_t. Declared last: 134   // Aligned storage for stop_cb_t. Declared last:
148   // its destructor may block while the callback 135   // its destructor may block while the callback
149   // accesses the members above. 136   // accesses the members above.
150   BOOST_CAPY_MSVC_WARNING_PUSH 137   BOOST_CAPY_MSVC_WARNING_PUSH
151   BOOST_CAPY_MSVC_WARNING_DISABLE(4324) // padded due to alignas 138   BOOST_CAPY_MSVC_WARNING_DISABLE(4324) // padded due to alignas
152   alignas(stop_cb_t) 139   alignas(stop_cb_t)
153   unsigned char stop_cb_buf_[sizeof(stop_cb_t)]; 140   unsigned char stop_cb_buf_[sizeof(stop_cb_t)];
154   BOOST_CAPY_MSVC_WARNING_POP 141   BOOST_CAPY_MSVC_WARNING_POP
155   142  
HITCBC 156   20 stop_cb_t& stop_cb_() noexcept 143   20 stop_cb_t& stop_cb_() noexcept
157   { 144   {
158   return *reinterpret_cast<stop_cb_t*>( 145   return *reinterpret_cast<stop_cb_t*>(
HITCBC 159   20 stop_cb_buf_); 146   20 stop_cb_buf_);
160   } 147   }
161   148  
162   public: 149   public:
163   /** Destroy the awaiter, leaving the event unable to reach it. 150   /** Destroy the awaiter, leaving the event unable to reach it.
164   151  
165   Destroys the stop callback if one is registered, and unlinks 152   Destroys the stop callback if one is registered, and unlinks
166   the awaiter from the event's wait queue if it is still linked. 153   the awaiter from the event's wait queue if it is still linked.
167   Both are necessary when the coroutine frame is torn down while 154   Both are necessary when the coroutine frame is torn down while
168   suspended, so that neither `set()` nor the stop callback can 155   suspended, so that neither `set()` nor the stop callback can
169   reach a destroyed awaiter. 156   reach a destroyed awaiter.
170   */ 157   */
HITCBC 171   52 ~wait_awaiter() 158   52 ~wait_awaiter()
172   { 159   {
HITCBC 173   52 if(active_) 160   52 if(active_)
HITCBC 174   1 stop_cb_().~stop_cb_t(); 161   1 stop_cb_().~stop_cb_t();
HITCBC 175   52 if(in_list_) 162   52 if(in_list_)
HITCBC 176   1 e_->waiters_.remove(this); 163   1 e_->waiters_.remove(this);
HITCBC 177   52 } 164   52 }
178   165  
179   /** Construct an awaiter for the given event. 166   /** Construct an awaiter for the given event.
180   167  
181   @param e The event to wait on. It must outlive the awaiter. 168   @param e The event to wait on. It must outlive the awaiter.
182   */ 169   */
HITCBC 183   25 explicit wait_awaiter(async_event* e) noexcept 170   25 explicit wait_awaiter(async_event* e) noexcept
HITCBC 184   25 : e_(e) 171   25 : e_(e)
185   { 172   {
HITCBC 186   25 } 173   25 }
187   174  
188   /** Construct by moving. 175   /** Construct by moving.
189   176  
190   The moved-from awaiter is left inert: its destructor no longer 177   The moved-from awaiter is left inert: its destructor no longer
191   destroys the stop callback and no longer unlinks from the 178   destroys the stop callback and no longer unlinks from the
192   event's wait queue. 179   event's wait queue.
193   180  
194   @param o The awaiter to move from. 181   @param o The awaiter to move from.
195   */ 182   */
HITCBC 196   27 wait_awaiter(wait_awaiter&& o) noexcept 183   27 wait_awaiter(wait_awaiter&& o) noexcept
HITCBC 197   54 : e_(o.e_) 184   54 : e_(o.e_)
HITCBC 198   27 , cont_(o.cont_) 185   27 , cont_(o.cont_)
HITCBC 199   27 , ex_(o.ex_) 186   27 , ex_(o.ex_)
HITCBC 200   27 , claimed_(o.claimed_.load( 187   27 , claimed_(o.claimed_.load(
201   std::memory_order_relaxed)) 188   std::memory_order_relaxed))
HITCBC 202   27 , canceled_(o.canceled_) 189   27 , canceled_(o.canceled_)
HITCBC 203   27 , active_(std::exchange(o.active_, false)) 190   27 , active_(std::exchange(o.active_, false))
HITCBC 204   54 , in_list_(std::exchange(o.in_list_, false)) 191   54 , in_list_(std::exchange(o.in_list_, false))
205   { 192   {
HITCBC 206   27 } 193   27 }
207   194  
208   /** Copy construction is disabled; a waiter is linked into the 195   /** Copy construction is disabled; a waiter is linked into the
209   event's wait queue by address. 196   event's wait queue by address.
210   197  
211   @param other The awaiter that would be copied. 198   @param other The awaiter that would be copied.
212   */ 199   */
213   wait_awaiter(wait_awaiter const& other) = delete; 200   wait_awaiter(wait_awaiter const& other) = delete;
214   201  
215   /** Copy assignment is disabled; a waiter is linked into the 202   /** Copy assignment is disabled; a waiter is linked into the
216   event's wait queue by address. 203   event's wait queue by address.
217   204  
218   @param other The awaiter that would be assigned from. 205   @param other The awaiter that would be assigned from.
219   206  
220   @return A reference to `*this`. 207   @return A reference to `*this`.
221   */ 208   */
222   wait_awaiter& operator=(wait_awaiter const& other) = delete; 209   wait_awaiter& operator=(wait_awaiter const& other) = delete;
223   210  
224   /** Move assignment is disabled; a waiter is linked into the 211   /** Move assignment is disabled; a waiter is linked into the
225   event's wait queue by address. 212   event's wait queue by address.
226   213  
227   @param other The awaiter that would be moved from. 214   @param other The awaiter that would be moved from.
228   215  
229   @return A reference to `*this`. 216   @return A reference to `*this`.
230   */ 217   */
231   wait_awaiter& operator=(wait_awaiter&& other) = delete; 218   wait_awaiter& operator=(wait_awaiter&& other) = delete;
232   219  
233   /** Report whether the event is already set. 220   /** Report whether the event is already set.
234   221  
235   @return `true` if the event is set, in which case the awaiting 222   @return `true` if the event is set, in which case the awaiting
236   coroutine does not suspend; otherwise `false`. 223   coroutine does not suspend; otherwise `false`.
237   */ 224   */
HITCBC 238   25 bool await_ready() const noexcept 225   25 bool await_ready() const noexcept
239   { 226   {
HITCBC 240   25 return e_->set_; 227   25 return e_->set_;
241   } 228   }
242   229  
243   /** Enqueue the awaiting coroutine until the event is set. 230   /** Enqueue the awaiting coroutine until the event is set.
244   231  
245   This is the @ref IoAwaitable overload of `await_suspend`. 232   This is the @ref IoAwaitable overload of `await_suspend`.
246   233  
247   If a stop request is already pending on `env->stop_token`, the 234   If a stop request is already pending on `env->stop_token`, the
248   awaiter records the cancellation and does not enqueue. 235   awaiter records the cancellation and does not enqueue.
249   236  
250   Otherwise it stores `h` and `env->executor`, links itself into 237   Otherwise it stores `h` and `env->executor`, links itself into
251   the event's wait queue, and registers a stop callback on 238   the event's wait queue, and registers a stop callback on
252   `env->stop_token`. Exactly one of `set()` and that callback posts 239   `env->stop_token`. Exactly one of `set()` and that callback posts
253   `h` through the stored executor, whichever claims the waiter 240   `h` through the stored executor, whichever claims the waiter
254   first. Only the post is subject to that race. A losing stop 241   first. Only the post is subject to that race. A losing stop
255   callback does nothing at all, but `set()` unlinks every waiter it 242   callback does nothing at all, but `set()` unlinks every waiter it
256   pops whether it claims it or not. That is why @ref await_resume 243   pops whether it claims it or not. That is why @ref await_resume
257   unlinks a canceled waiter only when it is still linked. 244   unlinks a canceled waiter only when it is still linked.
258   245  
259   @param h The awaiting coroutine, resumed when the event is set 246   @param h The awaiting coroutine, resumed when the event is set
260   or the wait is canceled. 247   or the wait is canceled.
261   248  
262   @param env The execution environment. Its executor posts the 249   @param env The execution environment. Its executor posts the
263   resumption and its stop token is watched for the duration of 250   resumption and its stop token is watched for the duration of
264   the wait. It must outlive the wait. 251   the wait. It must outlive the wait.
265   252  
266   @return `h` if a stop request was already pending, which 253   @return `h` if a stop request was already pending, which
267   resumes the awaiting coroutine immediately without enqueuing 254   resumes the awaiting coroutine immediately without enqueuing
268   it. Otherwise `std::noop_coroutine()`, which leaves the 255   it. Otherwise `std::noop_coroutine()`, which leaves the
269   coroutine suspended and returns control to the resumer. 256   coroutine suspended and returns control to the resumer.
270   */ 257   */
271   std::coroutine_handle<> 258   std::coroutine_handle<>
HITCBC 272   21 await_suspend( 259   21 await_suspend(
273   std::coroutine_handle<> h, 260   std::coroutine_handle<> h,
274   io_env const* env) noexcept 261   io_env const* env) noexcept
275   { 262   {
HITCBC 276   21 if(env->stop_token.stop_requested()) 263   21 if(env->stop_token.stop_requested())
277   { 264   {
HITCBC 278   1 canceled_ = true; 265   1 canceled_ = true;
HITCBC 279   1 return h; 266   1 return h;
280   } 267   }
HITCBC 281   20 cont_.h = h; 268   20 cont_.h = h;
HITCBC 282   20 ex_ = env->executor; 269   20 ex_ = env->executor;
HITCBC 283   20 e_->waiters_.push_back(this); 270   20 e_->waiters_.push_back(this);
HITCBC 284   20 in_list_ = true; 271   20 in_list_ = true;
HITCBC 285   60 ::new(stop_cb_buf_) stop_cb_t( 272   60 ::new(stop_cb_buf_) stop_cb_t(
HITCBC 286   20 env->stop_token, cancel_fn{this}); 273   20 env->stop_token, cancel_fn{this});
HITCBC 287   20 active_ = true; 274   20 active_ = true;
HITCBC 288   20 return std::noop_coroutine(); 275   20 return std::noop_coroutine();
289   } 276   }
290   277  
291   /** Complete the wait and report the outcome. 278   /** Complete the wait and report the outcome.
292   279  
293   Destroys the stop callback if one is registered. If the wait 280   Destroys the stop callback if one is registered. If the wait
294   was canceled while still linked into the event's wait queue, 281   was canceled while still linked into the event's wait queue,
295   unlinks it. `set()` pops every waiter, so a canceled waiter may 282   unlinks it. `set()` pops every waiter, so a canceled waiter may
296   or may not still be linked when it resumes. 283   or may not still be linked when it resumes.
297   284  
298   @return An empty `io_result<>` if the event was set, or one 285   @return An empty `io_result<>` if the event was set, or one
299   holding `error::canceled` if the stop token fired first. 286   holding `error::canceled` if the stop token fired first.
300   */ 287   */
HITCBC 301   22 [[nodiscard]] io_result<> await_resume() noexcept 288   22 [[nodiscard]] io_result<> await_resume() noexcept
302   { 289   {
HITCBC 303   22 if(active_) 290   22 if(active_)
304   { 291   {
HITCBC 305   19 stop_cb_().~stop_cb_t(); 292   19 stop_cb_().~stop_cb_t();
HITCBC 306   19 active_ = false; 293   19 active_ = false;
307   } 294   }
HITCBC 308   22 if(canceled_) 295   22 if(canceled_)
309   { 296   {
HITCBC 310   4 if(in_list_) 297   4 if(in_list_)
311   { 298   {
HITCBC 312   3 e_->waiters_.remove(this); 299   3 e_->waiters_.remove(this);
HITCBC 313   3 in_list_ = false; 300   3 in_list_ = false;
314   } 301   }
HITCBC 315   8 return {make_error_code( 302   8 return {make_error_code(
HITCBC 316   4 error::canceled)}; 303   4 error::canceled)};
317   } 304   }
HITCBC 318   18 return {{}}; 305   18 return {{}};
319   } 306   }
320   }; 307   };
321   308  
322   /// Construct an unset event. 309   /// Construct an unset event.
323   async_event() = default; 310   async_event() = default;
324   311  
325   /** Copy construction is disabled; suspended waiters point into the 312   /** Copy construction is disabled; suspended waiters point into the
326   event's wait queue. 313   event's wait queue.
327   314  
328   @param other The event that would be copied. 315   @param other The event that would be copied.
329   */ 316   */
330   async_event(async_event const& other) = delete; 317   async_event(async_event const& other) = delete;
331   318  
332   /** Copy assignment is disabled; suspended waiters point into the 319   /** Copy assignment is disabled; suspended waiters point into the
333   event's wait queue. 320   event's wait queue.
334   321  
335   @param other The event that would be assigned from. 322   @param other The event that would be assigned from.
336   323  
337   @return A reference to `*this`. 324   @return A reference to `*this`.
338   */ 325   */
339   async_event& operator=(async_event const& other) = delete; 326   async_event& operator=(async_event const& other) = delete;
340   327  
341   /** Move construction is disabled; suspended waiters point into the 328   /** Move construction is disabled; suspended waiters point into the
342   event's wait queue. 329   event's wait queue.
343   330  
344   @param other The event that would be moved from. 331   @param other The event that would be moved from.
345   */ 332   */
346   async_event(async_event&& other) = delete; 333   async_event(async_event&& other) = delete;
347   334  
348   /** Move assignment is disabled; suspended waiters point into the 335   /** Move assignment is disabled; suspended waiters point into the
349   event's wait queue. 336   event's wait queue.
350   337  
351   @param other The event that would be moved from. 338   @param other The event that would be moved from.
352   339  
353   @return A reference to `*this`. 340   @return A reference to `*this`.
354   */ 341   */
355   async_event& operator=(async_event&& other) = delete; 342   async_event& operator=(async_event&& other) = delete;
356   343  
357   /** Returns an awaiter that waits until the event is set. 344   /** Returns an awaiter that waits until the event is set.
358   345  
359   If the event is already set, completes immediately. 346   If the event is already set, completes immediately.
360   347  
361   @return An awaitable that await-returns `(error_code)`. 348   @return An awaitable that await-returns `(error_code)`.
362   */ 349   */
HITCBC 363   25 wait_awaiter wait() noexcept 350   25 wait_awaiter wait() noexcept
364   { 351   {
HITCBC 365   25 return wait_awaiter{this}; 352   25 return wait_awaiter{this};
366   } 353   }
367   354  
368   /** Resumes every waiting coroutine and marks the event set for later `wait()` calls. 355   /** Resumes every waiting coroutine and marks the event set for later `wait()` calls.
369   356  
370   All waiting coroutines are resumed. Canceled waiters 357   All waiting coroutines are resumed. Canceled waiters
371   are skipped. Subsequent calls to wait() complete 358   are skipped. Subsequent calls to wait() complete
372   immediately until clear() is called. 359   immediately until clear() is called.
373   */ 360   */
HITCBC 374   17 void set() 361   17 void set()
375   { 362   {
HITCBC 376   17 set_ = true; 363   17 set_ = true;
377   for(;;) 364   for(;;)
378   { 365   {
HITCBC 379   33 auto* w = waiters_.pop_front(); 366   33 auto* w = waiters_.pop_front();
HITCBC 380   33 if(!w) 367   33 if(!w)
HITCBC 381   17 break; 368   17 break;
HITCBC 382   16 w->in_list_ = false; 369   16 w->in_list_ = false;
HITCBC 383   16 if(!w->claimed_.exchange( 370   16 if(!w->claimed_.exchange(
384   true, std::memory_order_acq_rel)) 371   true, std::memory_order_acq_rel))
385   { 372   {
HITCBC 386   16 w->ex_.post(w->cont_); 373   16 w->ex_.post(w->cont_);
387   } 374   }
HITCBC 388   16 } 375   16 }
HITCBC 389   17 } 376   17 }
390   377  
391   /** Clears the event. 378   /** Clears the event.
392   379  
393   Subsequent calls to wait() suspend until 380   Subsequent calls to wait() suspend until
394   set() is called again. 381   set() is called again.
395   */ 382   */
HITCBC 396   2 void clear() noexcept 383   2 void clear() noexcept
397   { 384   {
HITCBC 398   2 set_ = false; 385   2 set_ = false;
HITCBC 399   2 } 386   2 }
400   387  
401   /** Returns true if the event is currently set. 388   /** Returns true if the event is currently set.
402   389  
403   @return `true` if the event is set; otherwise `false`. 390   @return `true` if the event is set; otherwise `false`.
404   */ 391   */
HITCBC 405   9 bool is_set() const noexcept 392   9 bool is_set() const noexcept
406   { 393   {
HITCBC 407   9 return set_; 394   9 return set_;
408   } 395   }
409   }; 396   };
410   397  
411   } // namespace capy 398   } // namespace capy
412   } // namespace boost 399   } // namespace boost
413   400  
414   #endif 401   #endif