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