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