100.00% Lines (95/95) 100.00% Functions (30/30)
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_TASK_HPP 11   #ifndef BOOST_CAPY_TASK_HPP
12   #define BOOST_CAPY_TASK_HPP 12   #define BOOST_CAPY_TASK_HPP
13   13  
14   #include <boost/capy/detail/config.hpp> 14   #include <boost/capy/detail/config.hpp>
15   #include <boost/capy/concept/executor.hpp> 15   #include <boost/capy/concept/executor.hpp>
16   #include <boost/capy/concept/context_awaitable.hpp> 16   #include <boost/capy/concept/context_awaitable.hpp>
17   #include <boost/capy/ex/context_awaitable_promise_base.hpp> 17   #include <boost/capy/ex/context_awaitable_promise_base.hpp>
18   #include <boost/capy/ex/await_context.hpp> 18   #include <boost/capy/ex/await_context.hpp>
19   #include <boost/capy/ex/frame_allocator.hpp> 19   #include <boost/capy/ex/frame_allocator.hpp>
20   #include <boost/capy/detail/await_suspend_helper.hpp> 20   #include <boost/capy/detail/await_suspend_helper.hpp>
21   #include <boost/capy/results.hpp> 21   #include <boost/capy/results.hpp>
22   22  
23   #include <exception> 23   #include <exception>
24   #include <optional> 24   #include <optional>
25   #include <type_traits> 25   #include <type_traits>
26   #include <utility> 26   #include <utility>
27   #include <variant> 27   #include <variant>
28   28  
29   namespace boost { 29   namespace boost {
30   namespace capy { 30   namespace capy {
31   31  
32   namespace detail { 32   namespace detail {
33   33  
34   // Helper base for result storage and return_void/return_value 34   // Helper base for result storage and return_void/return_value
35   template<typename T> 35   template<typename T>
36   struct task_return_base 36   struct task_return_base
37   { 37   {
38   std::optional<T> result_; 38   std::optional<T> result_;
39   39  
HITCBC 40   901 void return_value(T value) 40   899 void return_value(T value)
41   { 41   {
HITCBC 42   901 result_ = std::move(value); 42   899 result_ = std::move(value);
HITCBC 43   901 } 43   899 }
44   44  
HITCBC 45   285 T&& result() noexcept 45   284 T&& result() noexcept
46   { 46   {
HITCBC 47   285 return std::move(*result_); 47   284 return std::move(*result_);
48   } 48   }
49   }; 49   };
50   50  
51   template<> 51   template<>
52   struct task_return_base<void> 52   struct task_return_base<void>
53   { 53   {
HITCBC 54   4174 void return_void() 54   4159 void return_void()
55   { 55   {
HITCBC 56   4174 } 56   4159 }
57   }; 57   };
58   58  
59   } // namespace detail 59   } // namespace detail
60   60  
61   /** Defers a coroutine body until awaited, then runs it inline on the caller's thread. 61   /** Defers a coroutine body until awaited, then runs it inline on the caller's thread.
62   62  
63   Use `task<T>` as the return type for coroutines that perform I/O 63   Use `task<T>` as the return type for coroutines that perform I/O
64   and return a value of type `T`. The coroutine body does not start 64   and return a value of type `T`. The coroutine body does not start
65   executing until the task is awaited, enabling efficient composition 65   executing until the task is awaited, enabling efficient composition
66   without unnecessary eager execution. 66   without unnecessary eager execution.
67   67  
68   The task participates in the I/O awaitable protocol: when awaited, 68   The task participates in the I/O awaitable protocol: when awaited,
69   it receives the caller's executor and stop token, propagating them 69   it receives the caller's executor and stop token, propagating them
70   to nested `co_await` expressions. This enables cancellation and 70   to nested `co_await` expressions. This enables cancellation and
71   proper completion dispatch across executor boundaries. 71   proper completion dispatch across executor boundaries.
72   72  
73   @par Await-effects 73   @par Await-effects
74   74  
75   Let `t` be a `task<T>`. `co_await t` always suspends the awaiting 75   Let `t` be a `task<T>`. `co_await t` always suspends the awaiting
76   coroutine, then transfers control directly into the task's coroutine 76   coroutine, then transfers control directly into the task's coroutine
77   body on the current thread; no executor operation is posted. The task 77   body on the current thread; no executor operation is posted. The task
78   records the caller's environment (executor, stop token, and frame 78   records the caller's environment (executor, stop token, and frame
79   allocator) by pointer rather than copying it. It propagates that 79   allocator) by pointer rather than copying it. It propagates that
80   environment to every `co_await` inside the body. 80   environment to every `co_await` inside the body.
81   81  
82   The body runs until it returns or exits via an exception. Control 82   The body runs until it returns or exits via an exception. Control
83   then transfers directly back to the awaiting coroutine, again 83   then transfers directly back to the awaiting coroutine, again
84   without an executor operation. 84   without an executor operation.
85   85  
86   `task` never inspects the stop token; it only propagates it. A task 86   `task` never inspects the stop token; it only propagates it. A task
87   body observes a stop request through the results of the operations it 87   body observes a stop request through the results of the operations it
88 - awaits, or by reading the token itself. See @ref quitter for a task 88 + awaits, or by reading the token itself.
89 - that stops its own body.  
90   89  
91   @par Await-returns 90   @par Await-returns
92   The value the body passed to `co_return`, moved out of the task, or 91   The value the body passed to `co_return`, moved out of the task, or
93   nothing when `T` is `void`. 92   nothing when `T` is `void`.
94   93  
95   If the body exits via an unhandled exception, that exception is 94   If the body exits via an unhandled exception, that exception is
96   rethrown instead. 95   rethrown instead.
97   96  
98   @par Await-postcondition 97   @par Await-postcondition
99   The task's coroutine has run to completion and is suspended at its 98   The task's coroutine has run to completion and is suspended at its
100   final suspend point. The task still owns the frame, but not the 99   final suspend point. The task still owns the frame, but not the
101   result: the await moves it out, so a task must not be awaited twice. 100   result: the await moves it out, so a task must not be awaited twice.
102   101  
103   @par Thread Safety 102   @par Thread Safety
104   Distinct objects: Safe. 103   Distinct objects: Safe.
105   Shared objects: Unsafe. 104   Shared objects: Unsafe.
106   105  
107   @par Example 106   @par Example
108   107  
109   @par !example example 108   @par !example example
110   109  
111   110  
112   @tparam T The result type. Use `task<>` for `task<void>`. 111   @tparam T The result type. Use `task<>` for `task<void>`.
113   112  
114   @see @ref ContextRunnable, @ref ContextAwaitable, @ref run, @ref run_async 113   @see @ref ContextRunnable, @ref ContextAwaitable, @ref run, @ref run_async
115   */ 114   */
116   template<typename T = void> 115   template<typename T = void>
117   struct [[nodiscard]] BOOST_CAPY_CORO_AWAIT_ELIDABLE 116   struct [[nodiscard]] BOOST_CAPY_CORO_AWAIT_ELIDABLE
118   task 117   task
119   { 118   {
120   /** Stores `task<T>`'s result and joins the I/O awaitable protocol via `context_awaitable_promise_base`. 119   /** Stores `task<T>`'s result and joins the I/O awaitable protocol via `context_awaitable_promise_base`.
121   120  
122   This is the promise object the compiler associates with a 121   This is the promise object the compiler associates with a
123   `task<T>` coroutine. It satisfies the coroutine promise 122   `task<T>` coroutine. It satisfies the coroutine promise
124   requirements and participates in the I/O awaitable protocol via 123   requirements and participates in the I/O awaitable protocol via
125   @ref context_awaitable_promise_base. It is part of the coroutine 124   @ref context_awaitable_promise_base. It is part of the coroutine
126   machinery and is not intended to be used directly by callers. 125   machinery and is not intended to be used directly by callers.
127   126  
128   Result storage and `return_value`/`return_void` are provided by 127   Result storage and `return_value`/`return_void` are provided by
129   `detail::task_return_base<T>`. 128   `detail::task_return_base<T>`.
130   129  
131   @see @ref context_awaitable_promise_base, @ref ContextRunnable 130   @see @ref context_awaitable_promise_base, @ref ContextRunnable
132   */ 131   */
133   struct promise_type 132   struct promise_type
134   : context_awaitable_promise_base<promise_type> 133   : context_awaitable_promise_base<promise_type>
135   , detail::task_return_base<T> 134   , detail::task_return_base<T>
136   { 135   {
137   private: 136   private:
138   friend task; 137   friend task;
139   union { std::exception_ptr ep_; }; 138   union { std::exception_ptr ep_; };
140   bool has_ep_; 139   bool has_ep_;
141   140  
142   public: 141   public:
143   /// Construct the promise with no stored exception. 142   /// Construct the promise with no stored exception.
HITCBC 144   109265 promise_type() noexcept 143   109243 promise_type() noexcept
HITCBC 145   109265 : has_ep_(false) 144   109243 : has_ep_(false)
146   { 145   {
HITCBC 147   109265 } 146   109243 }
148   147  
149   /// Destroy the promise, releasing any stored exception. 148   /// Destroy the promise, releasing any stored exception.
HITCBC 150   109265 ~promise_type() 149   109243 ~promise_type()
151   { 150   {
HITCBC 152   109265 if(has_ep_) 151   109243 if(has_ep_)
HITCBC 153   490 ep_.~exception_ptr(); 152   485 ep_.~exception_ptr();
HITCBC 154   109265 } 153   109243 }
155   154  
156   /** Return the exception captured by the coroutine body, if any. 155   /** Return the exception captured by the coroutine body, if any.
157   156  
158   @return The stored exception, or a null `std::exception_ptr` 157   @return The stored exception, or a null `std::exception_ptr`
159   if the coroutine did not exit via an unhandled exception. 158   if the coroutine did not exit via an unhandled exception.
160   */ 159   */
HITCBC 161   5056 std::exception_ptr exception() const noexcept 160   5034 std::exception_ptr exception() const noexcept
162   { 161   {
HITCBC 163   5056 if(has_ep_) 162   5034 if(has_ep_)
HITCBC 164   732 return ep_; 163   726 return ep_;
HITCBC 165   4324 return {}; 164   4308 return {};
166   } 165   }
167   166  
168   /** Return the owning `task` for this coroutine. 167   /** Return the owning `task` for this coroutine.
169   168  
170   Called by the compiler to produce the object returned to the 169   Called by the compiler to produce the object returned to the
171   caller when the coroutine is created. 170   caller when the coroutine is created.
172   171  
173   @return A `task` owning the coroutine frame. 172   @return A `task` owning the coroutine frame.
174   */ 173   */
HITCBC 175   109265 task get_return_object() 174   109243 task get_return_object()
176   { 175   {
HITCBC 177   109265 return task{std::coroutine_handle<promise_type>::from_promise(*this)}; 176   109243 return task{std::coroutine_handle<promise_type>::from_promise(*this)};
178   } 177   }
179   178  
180   /** Return the initial-suspend awaiter. 179   /** Return the initial-suspend awaiter.
181   180  
182   The coroutine always suspends at the initial suspend point, 181   The coroutine always suspends at the initial suspend point,
183   so the body does not start until the task is awaited. When the 182   so the body does not start until the task is awaited. When the
184   body is resumed, the awaiter restores the thread-local frame 183   body is resumed, the awaiter restores the thread-local frame
185   allocator from the stored environment. 184   allocator from the stored environment.
186   185  
187   @return An awaiter that suspends unconditionally. 186   @return An awaiter that suspends unconditionally.
188   */ 187   */
HITCBC 189   109265 auto initial_suspend() noexcept 188   109243 auto initial_suspend() noexcept
190   { 189   {
191   struct awaiter 190   struct awaiter
192   { 191   {
193   promise_type* p_; 192   promise_type* p_;
194   193  
HITCBC 195   109265 bool await_ready() const noexcept 194   109243 bool await_ready() const noexcept
196   { 195   {
HITCBC 197   109265 return false; 196   109243 return false;
198   } 197   }
199   198  
HITCBC 200   109265 void await_suspend(std::coroutine_handle<>) const noexcept 199   109243 void await_suspend(std::coroutine_handle<>) const noexcept
201   { 200   {
HITCBC 202   109265 } 201   109243 }
203   202  
HITCBC 204   108182 void await_resume() const noexcept 203   108160 void await_resume() const noexcept
205   { 204   {
206   // Restore TLS when body starts executing 205   // Restore TLS when body starts executing
HITCBC 207   108182 set_current_frame_allocator(p_->environment()->frame_allocator); 206   108160 set_current_frame_allocator(p_->environment()->frame_allocator);
HITCBC 208   108182 } 207   108160 }
209   }; 208   };
HITCBC 210   109265 return awaiter{this}; 209   109243 return awaiter{this};
211   } 210   }
212   211  
213   /** Return the final-suspend awaiter. 212   /** Return the final-suspend awaiter.
214   213  
215   The coroutine always suspends at the final suspend point. The 214   The coroutine always suspends at the final suspend point. The
216   awaiter's `await_suspend` performs symmetric transfer to the 215   awaiter's `await_suspend` performs symmetric transfer to the
217   stored continuation (consuming it), resuming the awaiting 216   stored continuation (consuming it), resuming the awaiting
218   coroutine. 217   coroutine.
219   218  
220   @return An awaiter that suspends and transfers to the 219   @return An awaiter that suspends and transfers to the
221   continuation. 220   continuation.
222   */ 221   */
HITCBC 223   5565 auto final_suspend() noexcept 222   5543 auto final_suspend() noexcept
224   { 223   {
225   struct awaiter 224   struct awaiter
226   { 225   {
227   promise_type* p_; 226   promise_type* p_;
228   227  
HITCBC 229   5565 bool await_ready() const noexcept 228   5543 bool await_ready() const noexcept
230   { 229   {
HITCBC 231   5565 return false; 230   5543 return false;
232   } 231   }
233   232  
HITCBC 234   5565 std::coroutine_handle<> await_suspend(std::coroutine_handle<>) const noexcept 233   5543 std::coroutine_handle<> await_suspend(std::coroutine_handle<>) const noexcept
235   { 234   {
HITCBC 236   5565 return p_->continuation(); 235   5543 return p_->continuation();
237   } 236   }
238   237  
239   void await_resume() const noexcept {} // LCOV_EXCL_LINE final_suspend awaiter, never resumed 238   void await_resume() const noexcept {} // LCOV_EXCL_LINE final_suspend awaiter, never resumed
240   }; 239   };
HITCBC 241   5565 return awaiter{this}; 240   5543 return awaiter{this};
242   } 241   }
243   242  
244   /** Capture the in-flight exception from the coroutine body. 243   /** Capture the in-flight exception from the coroutine body.
245   244  
246   Called by the compiler when the coroutine body exits via an 245   Called by the compiler when the coroutine body exits via an
247   unhandled exception. The captured exception is rethrown when 246   unhandled exception. The captured exception is rethrown when
248   the task is awaited. 247   the task is awaited.
249   */ 248   */
HITCBC 250   490 void unhandled_exception() noexcept 249   485 void unhandled_exception() noexcept
251   { 250   {
HITCBC 252   490 new (&ep_) std::exception_ptr(std::current_exception()); 251   485 new (&ep_) std::exception_ptr(std::current_exception());
HITCBC 253   490 has_ep_ = true; 252   485 has_ep_ = true;
HITCBC 254   490 } 253   485 }
255   254  
256   /** Awaiter wrapping a nested `co_await` of an @ref ContextAwaitable. 255   /** Awaiter wrapping a nested `co_await` of an @ref ContextAwaitable.
257   256  
258   Forwards the environment to the inner awaitable's 257   Forwards the environment to the inner awaitable's
259   environment-taking `await_suspend` and restores the 258   environment-taking `await_suspend` and restores the
260   thread-local frame allocator before the body resumes. 259   thread-local frame allocator before the body resumes.
261   260  
262   @tparam Awaitable The awaitable being transformed. 261   @tparam Awaitable The awaitable being transformed.
263   */ 262   */
264   template<class Awaitable> 263   template<class Awaitable>
265   struct transform_awaiter 264   struct transform_awaiter
266   { 265   {
267   /// The wrapped awaitable, decayed and stored by value. 266   /// The wrapped awaitable, decayed and stored by value.
268   std::decay_t<Awaitable> a_; 267   std::decay_t<Awaitable> a_;
269   268  
270   /// The promise of the coroutine performing the `co_await`. 269   /// The promise of the coroutine performing the `co_await`.
271   promise_type* p_; 270   promise_type* p_;
272   271  
273   /** Report whether the wrapped awaitable is already complete. 272   /** Report whether the wrapped awaitable is already complete.
274   273  
275   Where `BOOST_CAPY_SYMMETRIC_TRANSFER_WORKAROUND` is on, 274   Where `BOOST_CAPY_SYMMETRIC_TRANSFER_WORKAROUND` is on,
276   this always returns `false`, and @ref await_suspend asks 275   this always returns `false`, and @ref await_suspend asks
277   the wrapped awaitable instead. See 276   the wrapped awaitable instead. See
278   `detail::await_ready_then_suspend`. 277   `detail::await_ready_then_suspend`.
279   278  
280   @return The wrapped awaitable's own `await_ready` result: 279   @return The wrapped awaitable's own `await_ready` result:
281   `true` if no suspension is needed. Always `false` where 280   `true` if no suspension is needed. Always `false` where
282   the workaround is on. 281   the workaround is on.
283   */ 282   */
HITCBC 284   108248 bool await_ready() noexcept 283   108226 bool await_ready() noexcept
285   { 284   {
286   #if BOOST_CAPY_SYMMETRIC_TRANSFER_WORKAROUND 285   #if BOOST_CAPY_SYMMETRIC_TRANSFER_WORKAROUND
287   return false; 286   return false;
288   #else 287   #else
HITCBC 289   108248 return a_.await_ready(); 288   108226 return a_.await_ready();
290   #endif 289   #endif
291   } 290   }
292   291  
293   /** Restore the frame allocator, then resume the wrapped 292   /** Restore the frame allocator, then resume the wrapped
294   awaitable. 293   awaitable.
295   294  
296   Reinstalls the thread-local frame allocator from the stored 295   Reinstalls the thread-local frame allocator from the stored
297   environment before the body continues. This is needed 296   environment before the body continues. This is needed
298   because the resumption may arrive on a different thread 297   because the resumption may arrive on a different thread
299   than the one that suspended. 298   than the one that suspended.
300   299  
301   @return The wrapped awaitable's await-result, forwarded 300   @return The wrapped awaitable's await-result, forwarded
302   unchanged. 301   unchanged.
303   */ 302   */
HITCBC 304   5631 decltype(auto) await_resume() 303   5609 decltype(auto) await_resume()
305   { 304   {
306   // Restore TLS before body resumes 305   // Restore TLS before body resumes
HITCBC 307   5631 set_current_frame_allocator(p_->environment()->frame_allocator); 306   5609 set_current_frame_allocator(p_->environment()->frame_allocator);
HITCBC 308   5631 return a_.await_resume(); 307   5609 return a_.await_resume();
309   } 308   }
310   309  
311   /** Suspend by calling the wrapped awaitable with the 310   /** Suspend by calling the wrapped awaitable with the
312   environment. 311   environment.
313   312  
314   This is the plain `await_suspend` the compiler calls for the 313   This is the plain `await_suspend` the compiler calls for the
315   nested `co_await`. It forwards to the wrapped awaitable's 314   nested `co_await`. It forwards to the wrapped awaitable's
316   @ref ContextAwaitable overload, supplying the promise's stored 315   @ref ContextAwaitable overload, supplying the promise's stored
317   environment as the second argument. It then hands back 316   environment as the second argument. It then hands back
318   that call's result unchanged, so the wrapped awaitable's 317   that call's result unchanged, so the wrapped awaitable's
319   suspension decision, whatever form it takes, is preserved. 318   suspension decision, whatever form it takes, is preserved.
320   319  
321   @param h The coroutine performing the `co_await`. 320   @param h The coroutine performing the `co_await`.
322   321  
323   @return Whatever the wrapped awaitable's `await_suspend` 322   @return Whatever the wrapped awaitable's `await_suspend`
324   returns. When that is a `std::coroutine_handle<>`, the 323   returns. When that is a `std::coroutine_handle<>`, the
325   handle is routed through `detail::symmetric_transfer`. 324   handle is routed through `detail::symmetric_transfer`.
326   Where `BOOST_CAPY_SYMMETRIC_TRANSFER_WORKAROUND` is on, 325   Where `BOOST_CAPY_SYMMETRIC_TRANSFER_WORKAROUND` is on,
327   this function instead returns `bool` from 326   this function instead returns `bool` from
328   `detail::await_ready_then_suspend`. It first asks the 327   `detail::await_ready_then_suspend`. It first asks the
329   wrapped awaitable's `await_ready`. When that reports 328   wrapped awaitable's `await_ready`. When that reports
330   ready, it returns `false` to continue without 329   ready, it returns `false` to continue without
331   suspending. Otherwise it resumes any returned handle on 330   suspending. Otherwise it resumes any returned handle on
332   the current stack and returns `true`. Where the 331   the current stack and returns `true`. Where the
333   workaround is off, a returned handle is passed back 332   workaround is off, a returned handle is passed back
334   unchanged for symmetric transfer. 333   unchanged for symmetric transfer.
335   */ 334   */
336   template<class Promise> 335   template<class Promise>
HITCBC 337   107619 auto await_suspend(std::coroutine_handle<Promise> h) noexcept 336   107601 auto await_suspend(std::coroutine_handle<Promise> h) noexcept
338   { 337   {
339   #if BOOST_CAPY_SYMMETRIC_TRANSFER_WORKAROUND 338   #if BOOST_CAPY_SYMMETRIC_TRANSFER_WORKAROUND
340   return detail::await_ready_then_suspend(a_, h, p_->environment()); 339   return detail::await_ready_then_suspend(a_, h, p_->environment());
341   #else 340   #else
342   using R = decltype(a_.await_suspend(h, p_->environment())); 341   using R = decltype(a_.await_suspend(h, p_->environment()));
343   if constexpr (std::is_same_v<R, std::coroutine_handle<>>) 342   if constexpr (std::is_same_v<R, std::coroutine_handle<>>)
HITCBC 344   106581 return detail::symmetric_transfer(a_.await_suspend(h, p_->environment())); 343   106563 return detail::symmetric_transfer(a_.await_suspend(h, p_->environment()));
345   else 344   else
HITCBC 346   1038 return a_.await_suspend(h, p_->environment()); 345   1038 return a_.await_suspend(h, p_->environment());
347   #endif 346   #endif
348   } 347   }
349   }; 348   };
350   349  
351   /** Transform a nested awaitable before `co_await`. 350   /** Transform a nested awaitable before `co_await`.
352   351  
353   Wraps an @ref ContextAwaitable in a @ref transform_awaiter so the 352   Wraps an @ref ContextAwaitable in a @ref transform_awaiter so the
354   coroutine's environment is propagated into it. A diagnostic 353   coroutine's environment is propagated into it. A diagnostic
355   is emitted if the awaitable does not satisfy @ref ContextAwaitable. 354   is emitted if the awaitable does not satisfy @ref ContextAwaitable.
356   355  
357   @param a The awaitable expression from `co_await a`. 356   @param a The awaitable expression from `co_await a`.
358   357  
359   @return A @ref transform_awaiter wrapping `a`. 358   @return A @ref transform_awaiter wrapping `a`.
360   */ 359   */
361   template<class Awaitable> 360   template<class Awaitable>
HITCBC 362   108248 auto transform_awaitable(Awaitable&& a) 361   108226 auto transform_awaitable(Awaitable&& a)
363   { 362   {
364   using A = std::decay_t<Awaitable>; 363   using A = std::decay_t<Awaitable>;
365   if constexpr (ContextAwaitable<A>) 364   if constexpr (ContextAwaitable<A>)
366   { 365   {
367   return transform_awaiter<Awaitable>{ 366   return transform_awaiter<Awaitable>{
HITCBC 368   213975 std::forward<Awaitable>(a), this}; 367   213935 std::forward<Awaitable>(a), this};
369   } 368   }
370   else 369   else
371   { 370   {
372   static_assert(ContextAwaitable<A>, "requires ContextAwaitable"); 371   static_assert(ContextAwaitable<A>, "requires ContextAwaitable");
373   } 372   }
HITCBC 374   105727 } 373   105709 }
375   }; 374   };
376   375  
377   /** Handle to the owned coroutine frame. 376   /** Handle to the owned coroutine frame.
378   377  
379   Null when the task is empty (for example after a move or after 378   Null when the task is empty (for example after a move or after
380   @ref release). Prefer @ref handle to read this; the member is 379   @ref release). Prefer @ref handle to read this; the member is
381   public for use by the coroutine machinery. 380   public for use by the coroutine machinery.
382   */ 381   */
383   std::coroutine_handle<promise_type> h_; 382   std::coroutine_handle<promise_type> h_;
384   383  
385   /** Destroy the task and its coroutine frame if owned. 384   /** Destroy the task and its coroutine frame if owned.
386   385  
387   @par Preconditions 386   @par Preconditions
388   The frame is not lent: no parent is suspended awaiting it, 387   The frame is not lent: no parent is suspended awaiting it,
389   unless its chain is being unwound after an @ref abandon. 388   unless its chain is being unwound after an @ref abandon.
390   Destroying a lent frame otherwise calls `std::terminate`; end 389   Destroying a lent frame otherwise calls `std::terminate`; end
391   a running chain by requesting stop, or by destroying its 390   a running chain by requesting stop, or by destroying its
392   execution context. 391   execution context.
393   */ 392   */
HITCBC 394   113344 ~task() 393   113317 ~task()
395   { 394   {
HITCBC 396   113344 destroy_frame(); 395   113317 destroy_frame();
HITCBC 397   113344 } 396   113317 }
398   397  
399   /** Report whether the awaited task is already complete. 398   /** Report whether the awaited task is already complete.
400   399  
401   Always returns `false`; a task is lazy and has not started when 400   Always returns `false`; a task is lazy and has not started when
402   it is awaited, so the awaiting coroutine always suspends. 401   it is awaited, so the awaiting coroutine always suspends.
403   402  
404   @return `false`. 403   @return `false`.
405   */ 404   */
HITCBC 406   2112 bool await_ready() const noexcept 405   2108 bool await_ready() const noexcept
407   { 406   {
HITCBC 408   2112 return false; 407   2108 return false;
409   } 408   }
410   409  
411   /** Return the task's result, rethrowing any captured exception. 410   /** Return the task's result, rethrowing any captured exception.
412   411  
413   If the coroutine body exited via an unhandled exception, that 412   If the coroutine body exited via an unhandled exception, that
414   exception is rethrown here. Otherwise the result is returned by 413   exception is rethrown here. Otherwise the result is returned by
415   move (for `task<T>`) or nothing is returned (for `task<void>`). 414   move (for `task<T>`) or nothing is returned (for `task<void>`).
416   415  
417   @return The result value for non-void `T`; otherwise `void`. 416   @return The result value for non-void `T`; otherwise `void`.
418   417  
419   @throws The exception captured by the coroutine body, if any. 418   @throws The exception captured by the coroutine body, if any.
420   419  
421   @note Discarding a `results` silently drops the error 420   @note Discarding a `results` silently drops the error
422   code, so that overload is marked `[[nodiscard]]`. 421   code, so that overload is marked `[[nodiscard]]`.
423   */ 422   */
HITCBC 424   567 [[nodiscard]] auto await_resume() 423   566 [[nodiscard]] auto await_resume()
425   requires detail::is_results_v<T> 424   requires detail::is_results_v<T>
426   { 425   {
HITCBC 427   567 lent_ = false; 426   566 lent_ = false;
HITCBC 428   567 if(h_.promise().has_ep_) 427   566 if(h_.promise().has_ep_)
HITCBC 429   105 std::rethrow_exception(h_.promise().ep_); 428   104 std::rethrow_exception(h_.promise().ep_);
HITCBC 430   462 return std::move(*h_.promise().result_); 429   462 return std::move(*h_.promise().result_);
431   } 430   }
432   431  
HITCBC 433   245 auto await_resume() 432   243 auto await_resume()
434   requires (! detail::is_results_v<T>) 433   requires (! detail::is_results_v<T>)
435   { 434   {
HITCBC 436   245 lent_ = false; 435   243 lent_ = false;
HITCBC 437   245 if(h_.promise().has_ep_) 436   243 if(h_.promise().has_ep_)
HITCBC 438   18 std::rethrow_exception(h_.promise().ep_); 437   17 std::rethrow_exception(h_.promise().ep_);
439   if constexpr (! std::is_void_v<T>) 438   if constexpr (! std::is_void_v<T>)
HITCBC 440   152 return std::move(*h_.promise().result_); 439   151 return std::move(*h_.promise().result_);
441   else 440   else
HITCBC 442   75 return; 441   75 return;
443   } 442   }
444   443  
445   /** Start the task with the awaiting coroutine's context. 444   /** Start the task with the awaiting coroutine's context.
446   445  
447   Stores `cont` as the continuation to resume on completion. 446   Stores `cont` as the continuation to resume on completion.
448   Stores `env` as the execution environment propagated to nested 447   Stores `env` as the execution environment propagated to nested
449   `co_await` expressions. Then transfers control into the task's 448   `co_await` expressions. Then transfers control into the task's
450   coroutine body via the returned handle. 449   coroutine body via the returned handle.
451   450  
452   @param cont The awaiting coroutine to resume when the task 451   @param cont The awaiting coroutine to resume when the task
453   completes. 452   completes.
454   453  
455   @param env The execution environment (executor, stop token, and 454   @param env The execution environment (executor, stop token, and
456   frame allocator). It must outlive the task. 455   frame allocator). It must outlive the task.
457   456  
458   @return The task's coroutine handle, for symmetric transfer. 457   @return The task's coroutine handle, for symmetric transfer.
459   */ 458   */
HITCBC 460   2009 std::coroutine_handle<> await_suspend(std::coroutine_handle<> cont, await_context const* env) 459   2005 std::coroutine_handle<> await_suspend(std::coroutine_handle<> cont, await_context const* env)
461   { 460   {
HITCBC 462   2009 h_.promise().set_continuation(cont); 461   2005 h_.promise().set_continuation(cont);
HITCBC 463   2009 h_.promise().set_environment(env); 462   2005 h_.promise().set_environment(env);
HITCBC 464   2009 lent_ = true; 463   2005 lent_ = true;
HITCBC 465   2009 return h_; 464   2005 return h_;
466   } 465   }
467   466  
468   /** Return the coroutine handle. 467   /** Return the coroutine handle.
469   468  
470   @note Do not call `destroy()` on the returned handle while the 469   @note Do not call `destroy()` on the returned handle while the
471   task is being awaited: the awaiting frame still holds it and 470   task is being awaited: the awaiting frame still holds it and
472   destroys it again, which has undefined behavior. A holder that 471   destroys it again, which has undefined behavior. A holder that
473   gives the coroutine up passes its continuation to @ref abandon 472   gives the coroutine up passes its continuation to @ref abandon
474   instead. (Destroying or 473   instead. (Destroying or
475   move-assigning over the task object itself while its frame is 474   move-assigning over the task object itself while its frame is
476   lent is a different operation, and calls `std::terminate`.) 475   lent is a different operation, and calls `std::terminate`.)
477   The task's lifetime is normally managed by `run_async`, `run`, 476   The task's lifetime is normally managed by `run_async`, `run`,
478   or the awaiting parent. For cooperative cancellation, use 477   or the awaiting parent. For cooperative cancellation, use
479   `std::stop_token`. 478   `std::stop_token`.
480   479  
481   @note The operation, queue or primitive holding a suspended 480   @note The operation, queue or primitive holding a suspended
482   coroutine gives it up with @ref abandon, through the 481   coroutine gives it up with @ref abandon, through the
483   continuation it keeps, never by destroying a handle. 482   continuation it keeps, never by destroying a handle.
484   `handle()` belongs to the owning task object. 483   `handle()` belongs to the owning task object.
485   484  
486   @return The coroutine handle. 485   @return The coroutine handle.
487   */ 486   */
HITCBC 488   107285 std::coroutine_handle<promise_type> handle() const noexcept 487   107267 std::coroutine_handle<promise_type> handle() const noexcept
489   { 488   {
HITCBC 490   107285 return h_; 489   107267 return h_;
491   } 490   }
492   491  
493   /** Release ownership of the coroutine frame. 492   /** Release ownership of the coroutine frame.
494   493  
495   After calling this, destroying the task does not destroy the 494   After calling this, destroying the task does not destroy the
496   coroutine frame. The caller becomes responsible for the frame's 495   coroutine frame. The caller becomes responsible for the frame's
497   lifetime. 496   lifetime.
498   497  
499   @note The caller may call `destroy()` on the released handle 498   @note The caller may call `destroy()` on the released handle
500   only when the task has not started or has fully completed. 499   only when the task has not started or has fully completed.
501   Destroying the released handle of a task that is being 500   Destroying the released handle of a task that is being
502   awaited is not supported and has undefined behavior. 501   awaited is not supported and has undefined behavior.
503   502  
504   @par Postconditions 503   @par Postconditions
505   `handle()` returns a null handle. Callers needing the 504   `handle()` returns a null handle. Callers needing the
506   original handle must save it, via @ref handle, before 505   original handle must save it, via @ref handle, before
507   calling this. 506   calling this.
508   */ 507   */
HITCBC 509   107142 void release() noexcept 508   107124 void release() noexcept
510   { 509   {
HITCBC 511   107142 h_ = nullptr; 510   107124 h_ = nullptr;
HITCBC 512   107142 } 511   107124 }
513   512  
514   /** Copy construction is disabled; a task uniquely owns its frame. 513   /** Copy construction is disabled; a task uniquely owns its frame.
515   514  
516   @param other The task that would be copied. 515   @param other The task that would be copied.
517   */ 516   */
518   task(task const& other) = delete; 517   task(task const& other) = delete;
519   518  
520   /** Copy assignment is disabled; a task uniquely owns its frame. 519   /** Copy assignment is disabled; a task uniquely owns its frame.
521   520  
522   @param other The task that would be assigned from. 521   @param other The task that would be assigned from.
523   522  
524   @return A reference to `*this`. 523   @return A reference to `*this`.
525   */ 524   */
526   task& operator=(task const& other) = delete; 525   task& operator=(task const& other) = delete;
527   526  
528   /** Construct by moving, transferring ownership of the frame. 527   /** Construct by moving, transferring ownership of the frame.
529   528  
530   @par Postconditions 529   @par Postconditions
531   `other` is empty and must not be awaited. 530   `other` is empty and must not be awaited.
532   531  
533   @param other The task to move from. 532   @param other The task to move from.
534   */ 533   */
HITCBC 535   4079 task(task&& other) noexcept 534   4074 task(task&& other) noexcept
HITCBC 536   4079 : h_(std::exchange(other.h_, nullptr)) 535   4074 : h_(std::exchange(other.h_, nullptr))
537   { 536   {
HITCBC 538   4079 } 537   4074 }
539   538  
540   /** Assign by moving, transferring ownership of the frame. 539   /** Assign by moving, transferring ownership of the frame.
541   540  
542   If this task already owns a coroutine frame, that frame is 541   If this task already owns a coroutine frame, that frame is
543   destroyed first. Self-assignment is a no-op. 542   destroyed first. Self-assignment is a no-op.
544   543  
545   @par Preconditions 544   @par Preconditions
546   The frame being replaced is not lent: no parent is suspended 545   The frame being replaced is not lent: no parent is suspended
547   awaiting it. Replacing a lent frame calls `std::terminate`. 546   awaiting it. Replacing a lent frame calls `std::terminate`.
548   547  
549   @par Postconditions 548   @par Postconditions
550   `other` is empty and must not be awaited. 549   `other` is empty and must not be awaited.
551   550  
552   @param other The task to move from. 551   @param other The task to move from.
553   552  
554   @return A reference to `*this`. 553   @return A reference to `*this`.
555   */ 554   */
HITCBC 556   1 task& operator=(task&& other) noexcept 555   1 task& operator=(task&& other) noexcept
557   { 556   {
HITCBC 558   1 if(this != &other) 557   1 if(this != &other)
559   { 558   {
HITCBC 560   1 destroy_frame(); 559   1 destroy_frame();
HITCBC 561   1 h_ = std::exchange(other.h_, nullptr); 560   1 h_ = std::exchange(other.h_, nullptr);
562   } 561   }
HITCBC 563   1 return *this; 562   1 return *this;
564   } 563   }
565   564  
566   private: 565   private:
567   // Set while a parent is suspended awaiting this task. 566   // Set while a parent is suspended awaiting this task.
568   bool lent_ = false; 567   bool lent_ = false;
569   568  
HITCBC 570   109265 explicit task(std::coroutine_handle<promise_type> h) 569   109243 explicit task(std::coroutine_handle<promise_type> h)
HITCBC 571   109265 : h_(h) 570   109243 : h_(h)
572   { 571   {
HITCBC 573   109265 } 572   109243 }
574   573  
HITCBC 575   113345 void destroy_frame() noexcept 574   113318 void destroy_frame() noexcept
576   { 575   {
HITCBC 577   113345 if(!h_) 576   113318 if(!h_)
HITCBC 578   111222 return; 577   111199 return;
HITCBC 579   2123 if(lent_ && !h_.done()) 578   2119 if(lent_ && !h_.done())
580   { 579   {
581   // Only the unwind of an abandoned chain destroys a frame 580   // Only the unwind of an abandoned chain destroys a frame
582   // that is still awaited. 581   // that is still awaited.
HITCBC 583   1278 auto* owner = h_.promise().environment()->owner; 582   1277 auto* owner = h_.promise().environment()->owner;
HITCBC 584   1278 if(!owner || !owner->abandoned()) 583   1277 if(!owner || !owner->abandoned())
585   std::terminate(); // LCOV_EXCL_LINE reached only in a death test's child process 584   std::terminate(); // LCOV_EXCL_LINE reached only in a death test's child process
586   } 585   }
HITCBC 587   2123 std::exchange(h_, nullptr).destroy(); 586   2119 std::exchange(h_, nullptr).destroy();
HITCBC 588   2123 lent_ = false; 587   2119 lent_ = false;
589   } 588   }
590   }; 589   };
591   590  
592   } // namespace capy 591   } // namespace capy
593   } // namespace boost 592   } // namespace boost
594   593  
595   #endif 594   #endif