100.00% Lines (76/76) 100.00% Functions (27/27)
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   // 3   //
4   // Distributed under the Boost Software License, Version 1.0. (See accompanying 4   // Distributed under the Boost Software License, Version 1.0. (See accompanying
5   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6   // 6   //
7   // Official repository: https://github.com/cppalliance/capy 7   // Official repository: https://github.com/cppalliance/capy
8   // 8   //
9   9  
10   #ifndef BOOST_CAPY_TASK_HPP 10   #ifndef BOOST_CAPY_TASK_HPP
11   #define BOOST_CAPY_TASK_HPP 11   #define BOOST_CAPY_TASK_HPP
12   12  
13   #include <boost/capy/detail/config.hpp> 13   #include <boost/capy/detail/config.hpp>
14   #include <boost/capy/concept/executor.hpp> 14   #include <boost/capy/concept/executor.hpp>
15   #include <boost/capy/concept/io_awaitable.hpp> 15   #include <boost/capy/concept/io_awaitable.hpp>
16   #include <boost/capy/ex/io_awaitable_promise_base.hpp> 16   #include <boost/capy/ex/io_awaitable_promise_base.hpp>
17   #include <boost/capy/ex/io_env.hpp> 17   #include <boost/capy/ex/io_env.hpp>
18   #include <boost/capy/ex/frame_allocator.hpp> 18   #include <boost/capy/ex/frame_allocator.hpp>
19   #include <boost/capy/detail/await_suspend_helper.hpp> 19   #include <boost/capy/detail/await_suspend_helper.hpp>
20   20  
21   #include <exception> 21   #include <exception>
22   #include <optional> 22   #include <optional>
23   #include <type_traits> 23   #include <type_traits>
24   #include <utility> 24   #include <utility>
25   #include <variant> 25   #include <variant>
26   26  
27   namespace boost { 27   namespace boost {
28   namespace capy { 28   namespace capy {
29   29  
30   namespace detail { 30   namespace detail {
31   31  
32   // Helper base for result storage and return_void/return_value 32   // Helper base for result storage and return_void/return_value
33   template<typename T> 33   template<typename T>
34   struct task_return_base 34   struct task_return_base
35   { 35   {
36   std::optional<T> result_; 36   std::optional<T> result_;
37   37  
HITCBC 38   1184 void return_value(T value) 38   1184 void return_value(T value)
39   { 39   {
HITCBC 40   1184 result_ = std::move(value); 40   1184 result_ = std::move(value);
HITCBC 41   1184 } 41   1184 }
42   42  
HITCBC 43   152 T&& result() noexcept 43   152 T&& result() noexcept
44   { 44   {
HITCBC 45   152 return std::move(*result_); 45   152 return std::move(*result_);
46   } 46   }
47   }; 47   };
48   48  
49   template<> 49   template<>
50   struct task_return_base<void> 50   struct task_return_base<void>
51   { 51   {
HITCBC 52   1976 void return_void() 52   1943 void return_void()
53   { 53   {
HITCBC 54   1976 } 54   1943 }
55   }; 55   };
56   56  
57   } // namespace detail 57   } // namespace detail
58   58  
59   /** Lazy coroutine task satisfying @ref IoRunnable. 59   /** Lazy coroutine task satisfying @ref IoRunnable.
60   60  
61   Use `task<T>` as the return type for coroutines that perform I/O 61   Use `task<T>` as the return type for coroutines that perform I/O
62   and return a value of type `T`. The coroutine body does not start 62   and return a value of type `T`. The coroutine body does not start
63   executing until the task is awaited, enabling efficient composition 63   executing until the task is awaited, enabling efficient composition
64   without unnecessary eager execution. 64   without unnecessary eager execution.
65   65  
66   The task participates in the I/O awaitable protocol: when awaited, 66   The task participates in the I/O awaitable protocol: when awaited,
67   it receives the caller's executor and stop token, propagating them 67   it receives the caller's executor and stop token, propagating them
68   to nested `co_await` expressions. This enables cancellation and 68   to nested `co_await` expressions. This enables cancellation and
69   proper completion dispatch across executor boundaries. 69   proper completion dispatch across executor boundaries.
70   70  
71   @par Thread Safety 71   @par Thread Safety
72   Distinct objects: Safe. 72   Distinct objects: Safe.
73   Shared objects: Unsafe. 73   Shared objects: Unsafe.
74   74  
75   @par Example 75   @par Example
76   76  
77   @code 77   @code
78   task<int> compute_value() 78   task<int> compute_value()
79   { 79   {
80   auto [ec, n] = co_await stream.read_some( buf ); 80   auto [ec, n] = co_await stream.read_some( buf );
81   if( ec ) 81   if( ec )
82   co_return 0; 82   co_return 0;
83   co_return process( buf, n ); 83   co_return process( buf, n );
84   } 84   }
85   85  
86   task<> run_session( tcp_socket sock ) 86   task<> run_session( tcp_socket sock )
87   { 87   {
88   int result = co_await compute_value(); 88   int result = co_await compute_value();
89   // ... 89   // ...
90   } 90   }
91   @endcode 91   @endcode
92   92  
93   @tparam T The result type. Use `task<>` for `task<void>`. 93   @tparam T The result type. Use `task<>` for `task<void>`.
94   94  
95   @see IoRunnable, IoAwaitable, run, run_async 95   @see IoRunnable, IoAwaitable, run, run_async
96   */ 96   */
97   template<typename T = void> 97   template<typename T = void>
98   struct [[nodiscard]] BOOST_CAPY_CORO_AWAIT_ELIDABLE 98   struct [[nodiscard]] BOOST_CAPY_CORO_AWAIT_ELIDABLE
99   task 99   task
100   { 100   {
101   /** The coroutine promise type for `task<T>`. 101   /** The coroutine promise type for `task<T>`.
102   102  
103   This is the promise object the compiler associates with a 103   This is the promise object the compiler associates with a
104   `task<T>` coroutine. It satisfies the coroutine promise 104   `task<T>` coroutine. It satisfies the coroutine promise
105   requirements and participates in the I/O awaitable protocol via 105   requirements and participates in the I/O awaitable protocol via
106   @ref io_awaitable_promise_base. It is part of the coroutine 106   @ref io_awaitable_promise_base. It is part of the coroutine
107   machinery and is not intended to be used directly by callers. 107   machinery and is not intended to be used directly by callers.
108   108  
109   Result storage and `return_value`/`return_void` are provided by 109   Result storage and `return_value`/`return_void` are provided by
110   `detail::task_return_base<T>`. 110   `detail::task_return_base<T>`.
111   111  
112   @see io_awaitable_promise_base, IoRunnable 112   @see io_awaitable_promise_base, IoRunnable
113   */ 113   */
114   struct promise_type 114   struct promise_type
115   : io_awaitable_promise_base<promise_type> 115   : io_awaitable_promise_base<promise_type>
116   , detail::task_return_base<T> 116   , detail::task_return_base<T>
117   { 117   {
118   private: 118   private:
119   friend task; 119   friend task;
120   union { std::exception_ptr ep_; }; 120   union { std::exception_ptr ep_; };
121   bool has_ep_; 121   bool has_ep_;
122   122  
123   public: 123   public:
124   /// Construct the promise with no stored exception. 124   /// Construct the promise with no stored exception.
HITCBC 125   4701 promise_type() noexcept 125   4668 promise_type() noexcept
HITCBC 126   4701 : has_ep_(false) 126   4668 : has_ep_(false)
127   { 127   {
HITCBC 128   4701 } 128   4668 }
129   129  
130   /// Destroy the promise, releasing any stored exception. 130   /// Destroy the promise, releasing any stored exception.
HITCBC 131   4701 ~promise_type() 131   4668 ~promise_type()
132   { 132   {
HITCBC 133   4701 if(has_ep_) 133   4668 if(has_ep_)
HITCBC 134   1391 ep_.~exception_ptr(); 134   1382 ep_.~exception_ptr();
HITCBC 135   4701 } 135   4668 }
136   136  
137   /** Return the exception captured by the coroutine body, if any. 137   /** Return the exception captured by the coroutine body, if any.
138   138  
139   @return The stored exception, or a null `std::exception_ptr` 139   @return The stored exception, or a null `std::exception_ptr`
140   if the coroutine did not exit via an unhandled exception. 140   if the coroutine did not exit via an unhandled exception.
141   */ 141   */
HITCBC 142   3882 std::exception_ptr exception() const noexcept 142   3831 std::exception_ptr exception() const noexcept
143   { 143   {
HITCBC 144   3882 if(has_ep_) 144   3831 if(has_ep_)
HITCBC 145   1828 return ep_; 145   1810 return ep_;
HITCBC 146   2054 return {}; 146   2021 return {};
147   } 147   }
148   148  
149   /** Return the owning `task` for this coroutine. 149   /** Return the owning `task` for this coroutine.
150   150  
151   Called by the compiler to produce the object returned to the 151   Called by the compiler to produce the object returned to the
152   caller when the coroutine is created. 152   caller when the coroutine is created.
153   153  
154   @return A `task` owning the coroutine frame. 154   @return A `task` owning the coroutine frame.
155   */ 155   */
HITCBC 156   4701 task get_return_object() 156   4668 task get_return_object()
157   { 157   {
HITCBC 158   4701 return task{std::coroutine_handle<promise_type>::from_promise(*this)}; 158   4668 return task{std::coroutine_handle<promise_type>::from_promise(*this)};
159   } 159   }
160   160  
161   /** Return the initial-suspend awaiter. 161   /** Return the initial-suspend awaiter.
162   162  
163   The coroutine always suspends at the initial suspend point, 163   The coroutine always suspends at the initial suspend point,
164   so the body does not start until the task is awaited. When the 164   so the body does not start until the task is awaited. When the
165   body is resumed, the awaiter restores the thread-local frame 165   body is resumed, the awaiter restores the thread-local frame
166   allocator from the stored environment. 166   allocator from the stored environment.
167   167  
168   @return An awaiter that suspends unconditionally. 168   @return An awaiter that suspends unconditionally.
169   */ 169   */
HITCBC 170   4701 auto initial_suspend() noexcept 170   4668 auto initial_suspend() noexcept
171   { 171   {
172   struct awaiter 172   struct awaiter
173   { 173   {
174   promise_type* p_; 174   promise_type* p_;
175   175  
HITCBC 176   4701 bool await_ready() const noexcept 176   4668 bool await_ready() const noexcept
177   { 177   {
HITCBC 178   4701 return false; 178   4668 return false;
179   } 179   }
180   180  
HITCBC 181   4701 void await_suspend(std::coroutine_handle<>) const noexcept 181   4668 void await_suspend(std::coroutine_handle<>) const noexcept
182   { 182   {
HITCBC 183   4701 } 183   4668 }
184   184  
HITCBC 185   4698 void await_resume() const noexcept 185   4665 void await_resume() const noexcept
186   { 186   {
187   // Restore TLS when body starts executing 187   // Restore TLS when body starts executing
HITCBC 188   4698 set_current_frame_allocator(p_->environment()->frame_allocator); 188   4665 set_current_frame_allocator(p_->environment()->frame_allocator);
HITCBC 189   4698 } 189   4665 }
190   }; 190   };
HITCBC 191   4701 return awaiter{this}; 191   4668 return awaiter{this};
192   } 192   }
193   193  
194   /** Return the final-suspend awaiter. 194   /** Return the final-suspend awaiter.
195   195  
196   The coroutine always suspends at the final suspend point. The 196   The coroutine always suspends at the final suspend point. The
197   awaiter's `await_suspend` performs symmetric transfer to the 197   awaiter's `await_suspend` performs symmetric transfer to the
198   stored continuation (consuming it), resuming the awaiting 198   stored continuation (consuming it), resuming the awaiting
199   coroutine. 199   coroutine.
200   200  
201   @return An awaiter that suspends and transfers to the 201   @return An awaiter that suspends and transfers to the
202   continuation. 202   continuation.
203   */ 203   */
HITCBC 204   4551 auto final_suspend() noexcept 204   4509 auto final_suspend() noexcept
205   { 205   {
206   struct awaiter 206   struct awaiter
207   { 207   {
208   promise_type* p_; 208   promise_type* p_;
209   209  
HITCBC 210   4551 bool await_ready() const noexcept 210   4509 bool await_ready() const noexcept
211   { 211   {
HITCBC 212   4551 return false; 212   4509 return false;
213   } 213   }
214   214  
HITCBC 215   4551 std::coroutine_handle<> await_suspend(std::coroutine_handle<>) const noexcept 215   4509 std::coroutine_handle<> await_suspend(std::coroutine_handle<>) const noexcept
216   { 216   {
HITCBC 217   4551 return p_->continuation(); 217   4509 return p_->continuation();
218   } 218   }
219   219  
220   void await_resume() const noexcept {} // LCOV_EXCL_LINE final_suspend awaiter, never resumed 220   void await_resume() const noexcept {} // LCOV_EXCL_LINE final_suspend awaiter, never resumed
221   }; 221   };
HITCBC 222   4551 return awaiter{this}; 222   4509 return awaiter{this};
223   } 223   }
224   224  
225   /** Capture the in-flight exception from the coroutine body. 225   /** Capture the in-flight exception from the coroutine body.
226   226  
227   Called by the compiler when the coroutine body exits via an 227   Called by the compiler when the coroutine body exits via an
228   unhandled exception. The captured exception is rethrown when 228   unhandled exception. The captured exception is rethrown when
229   the task is awaited. 229   the task is awaited.
230   */ 230   */
HITCBC 231   1391 void unhandled_exception() noexcept 231   1382 void unhandled_exception() noexcept
232   { 232   {
HITCBC 233   1391 new (&ep_) std::exception_ptr(std::current_exception()); 233   1382 new (&ep_) std::exception_ptr(std::current_exception());
HITCBC 234   1391 has_ep_ = true; 234   1382 has_ep_ = true;
HITCBC 235   1391 } 235   1382 }
236   236  
237   /** Awaiter wrapping a nested `co_await` of an @ref IoAwaitable. 237   /** Awaiter wrapping a nested `co_await` of an @ref IoAwaitable.
238   238  
239   Forwards the environment to the inner awaitable's 239   Forwards the environment to the inner awaitable's
240   environment-taking `await_suspend` and restores the 240   environment-taking `await_suspend` and restores the
241   thread-local frame allocator before the body resumes. 241   thread-local frame allocator before the body resumes.
242   242  
243   @tparam Awaitable The awaitable being transformed. 243   @tparam Awaitable The awaitable being transformed.
244   */ 244   */
245   template<class Awaitable> 245   template<class Awaitable>
246   struct transform_awaiter 246   struct transform_awaiter
247   { 247   {
248   std::decay_t<Awaitable> a_; 248   std::decay_t<Awaitable> a_;
249   promise_type* p_; 249   promise_type* p_;
250   250  
HITCBC 251   6797 bool await_ready() noexcept 251   6786 bool await_ready() noexcept
252   { 252   {
HITCBC 253   6797 return a_.await_ready(); 253   6786 return a_.await_ready();
254   } 254   }
255   255  
HITCBC 256   6650 decltype(auto) await_resume() 256   6630 decltype(auto) await_resume()
257   { 257   {
258   // Restore TLS before body resumes 258   // Restore TLS before body resumes
HITCBC 259   6650 set_current_frame_allocator(p_->environment()->frame_allocator); 259   6630 set_current_frame_allocator(p_->environment()->frame_allocator);
HITCBC 260   6650 return a_.await_resume(); 260   6630 return a_.await_resume();
261   } 261   }
262   262  
263   template<class Promise> 263   template<class Promise>
HITCBC 264   6215 auto await_suspend(std::coroutine_handle<Promise> h) noexcept 264   6202 auto await_suspend(std::coroutine_handle<Promise> h) noexcept
265   { 265   {
266   using R = decltype(a_.await_suspend(h, p_->environment())); 266   using R = decltype(a_.await_suspend(h, p_->environment()));
267   if constexpr (std::is_same_v<R, std::coroutine_handle<>>) 267   if constexpr (std::is_same_v<R, std::coroutine_handle<>>)
HITCBC 268   2726 return detail::symmetric_transfer(a_.await_suspend(h, p_->environment())); 268   2713 return detail::symmetric_transfer(a_.await_suspend(h, p_->environment()));
269   else 269   else
HITCBC 270   3489 return a_.await_suspend(h, p_->environment()); 270   3489 return a_.await_suspend(h, p_->environment());
271   } 271   }
272   }; 272   };
273   273  
274   /** Transform a nested awaitable before `co_await`. 274   /** Transform a nested awaitable before `co_await`.
275   275  
276   Wraps an @ref IoAwaitable in a @ref transform_awaiter so the 276   Wraps an @ref IoAwaitable in a @ref transform_awaiter so the
277   coroutine's environment is propagated into it. A diagnostic 277   coroutine's environment is propagated into it. A diagnostic
278   is emitted if the awaitable does not satisfy @ref IoAwaitable. 278   is emitted if the awaitable does not satisfy @ref IoAwaitable.
279   279  
280   @param a The awaitable expression from `co_await a`. 280   @param a The awaitable expression from `co_await a`.
281   281  
282   @return A @ref transform_awaiter wrapping `a`. 282   @return A @ref transform_awaiter wrapping `a`.
283   */ 283   */
284   template<class Awaitable> 284   template<class Awaitable>
HITCBC 285   6797 auto transform_awaitable(Awaitable&& a) 285   6786 auto transform_awaitable(Awaitable&& a)
286   { 286   {
287   using A = std::decay_t<Awaitable>; 287   using A = std::decay_t<Awaitable>;
288   if constexpr (IoAwaitable<A>) 288   if constexpr (IoAwaitable<A>)
289   { 289   {
290   return transform_awaiter<Awaitable>{ 290   return transform_awaiter<Awaitable>{
HITCBC 291   9187 std::forward<Awaitable>(a), this}; 291   9165 std::forward<Awaitable>(a), this};
292   } 292   }
293   else 293   else
294   { 294   {
295   static_assert(sizeof(A) == 0, "requires IoAwaitable"); 295   static_assert(sizeof(A) == 0, "requires IoAwaitable");
296   } 296   }
HITCBC 297   2390 } 297   2379 }
298   }; 298   };
299   299  
300   /** Handle to the owned coroutine frame. 300   /** Handle to the owned coroutine frame.
301   301  
302   Null when the task is empty (for example after a move or after 302   Null when the task is empty (for example after a move or after
303   @ref release). Prefer @ref handle to read this; the member is 303   @ref release). Prefer @ref handle to read this; the member is
304   public for use by the coroutine machinery. 304   public for use by the coroutine machinery.
305   */ 305   */
306   std::coroutine_handle<promise_type> h_; 306   std::coroutine_handle<promise_type> h_;
307   307  
308   /// Destroy the task and its coroutine frame if owned. 308   /// Destroy the task and its coroutine frame if owned.
HITCBC 309   9636 ~task() 309   9555 ~task()
310   { 310   {
HITCBC 311   9636 if(h_) 311   9555 if(h_)
HITCBC 312   1525 h_.destroy(); 312   1525 h_.destroy();
HITCBC 313   9636 } 313   9555 }
314   314  
315   /** Report whether the awaited task is already complete. 315   /** Report whether the awaited task is already complete.
316   316  
317   Always returns `false`; a task is lazy and has not started when 317   Always returns `false`; a task is lazy and has not started when
318   it is awaited, so the awaiting coroutine always suspends. 318   it is awaited, so the awaiting coroutine always suspends.
319   319  
320   @return `false`. 320   @return `false`.
321   */ 321   */
HITCBC 322   1523 bool await_ready() const noexcept 322   1523 bool await_ready() const noexcept
323   { 323   {
HITCBC 324   1523 return false; 324   1523 return false;
325   } 325   }
326   326  
327   /** Return the task's result, rethrowing any captured exception. 327   /** Return the task's result, rethrowing any captured exception.
328   328  
329   If the coroutine body exited via an unhandled exception, that 329   If the coroutine body exited via an unhandled exception, that
330   exception is rethrown here. Otherwise the result is returned by 330   exception is rethrown here. Otherwise the result is returned by
331   move (for `task<T>`) or nothing is returned (for `task<void>`). 331   move (for `task<T>`) or nothing is returned (for `task<void>`).
332   332  
333   @return The result value for non-void `T`; otherwise `void`. 333   @return The result value for non-void `T`; otherwise `void`.
334   334  
335   @throws The exception captured by the coroutine body, if any. 335   @throws The exception captured by the coroutine body, if any.
336   */ 336   */
HITCBC 337   1522 auto await_resume() 337   1522 auto await_resume()
338   { 338   {
HITCBC 339   1522 if(h_.promise().has_ep_) 339   1522 if(h_.promise().has_ep_)
HITCBC 340   476 std::rethrow_exception(h_.promise().ep_); 340   476 std::rethrow_exception(h_.promise().ep_);
341   if constexpr (! std::is_void_v<T>) 341   if constexpr (! std::is_void_v<T>)
HITCBC 342   1030 return std::move(*h_.promise().result_); 342   1030 return std::move(*h_.promise().result_);
343   else 343   else
HITCBC 344   16 return; 344   16 return;
345   } 345   }
346   346  
347   /** Start the task with the awaiting coroutine's context. 347   /** Start the task with the awaiting coroutine's context.
348   348  
349   Stores `cont` as the continuation to resume on completion and 349   Stores `cont` as the continuation to resume on completion and
350   `env` as the execution environment propagated to nested 350   `env` as the execution environment propagated to nested
351   `co_await` expressions, then transfers control into the task's 351   `co_await` expressions, then transfers control into the task's
352   coroutine body via the returned handle. 352   coroutine body via the returned handle.
353   353  
354   @param cont The awaiting coroutine to resume when the task 354   @param cont The awaiting coroutine to resume when the task
355   completes. 355   completes.
356   356  
357   @param env The execution environment (executor, stop token, and 357   @param env The execution environment (executor, stop token, and
358   frame allocator). It must outlive the task. 358   frame allocator). It must outlive the task.
359   359  
360   @return The task's coroutine handle, for symmetric transfer. 360   @return The task's coroutine handle, for symmetric transfer.
361   */ 361   */
HITCBC 362   1500 std::coroutine_handle<> await_suspend(std::coroutine_handle<> cont, io_env const* env) 362   1500 std::coroutine_handle<> await_suspend(std::coroutine_handle<> cont, io_env const* env)
363   { 363   {
HITCBC 364   1500 h_.promise().set_continuation(cont); 364   1500 h_.promise().set_continuation(cont);
HITCBC 365   1500 h_.promise().set_environment(env); 365   1500 h_.promise().set_environment(env);
HITCBC 366   1500 return h_; 366   1500 return h_;
367   } 367   }
368   368  
369   /** Return the coroutine handle. 369   /** Return the coroutine handle.
370   370  
371   @note Do not call `destroy()` on the returned handle while the 371   @note Do not call `destroy()` on the returned handle while the
372   task is being awaited. The task's lifetime is normally managed 372   task is being awaited. The task's lifetime is normally managed
373   by `run_async`, `run`, or the awaiting parent; manually 373   by `run_async`, `run`, or the awaiting parent; manually
374   destroying a suspended task that another coroutine is awaiting 374   destroying a suspended task that another coroutine is awaiting
375   produces undefined behavior. For cooperative cancellation, use 375   produces undefined behavior. For cooperative cancellation, use
376   `std::stop_token`. 376   `std::stop_token`.
377   377  
378   @return The coroutine handle. 378   @return The coroutine handle.
379   */ 379   */
HITCBC 380   3201 std::coroutine_handle<promise_type> handle() const noexcept 380   3168 std::coroutine_handle<promise_type> handle() const noexcept
381   { 381   {
HITCBC 382   3201 return h_; 382   3168 return h_;
383   } 383   }
384   384  
385   /** Release ownership of the coroutine frame. 385   /** Release ownership of the coroutine frame.
386   386  
387   After calling this, destroying the task does not destroy the 387   After calling this, destroying the task does not destroy the
388   coroutine frame. The caller becomes responsible for the frame's 388   coroutine frame. The caller becomes responsible for the frame's
389   lifetime. 389   lifetime.
390   390  
391   @note If the caller intends to call `destroy()` on the 391   @note If the caller intends to call `destroy()` on the
392   released handle, it must do so only when the task has not 392   released handle, it must do so only when the task has not
393   started or has fully completed. Destroying a suspended task 393   started or has fully completed. Destroying a suspended task
394   that is being awaited produces undefined behavior. 394   that is being awaited produces undefined behavior.
395   395  
396   @par Postconditions 396   @par Postconditions
397   `handle()` returns the original handle, but the task no longer 397   `handle()` returns the original handle, but the task no longer
398   owns it. 398   owns it.
399   */ 399   */
HITCBC 400   3176 void release() noexcept 400   3143 void release() noexcept
401   { 401   {
HITCBC 402   3176 h_ = nullptr; 402   3143 h_ = nullptr;
HITCBC 403   3176 } 403   3143 }
404   404  
405   task(task const&) = delete; 405   task(task const&) = delete;
406   task& operator=(task const&) = delete; 406   task& operator=(task const&) = delete;
407   407  
408   /** Construct by moving, transferring ownership of the frame. 408   /** Construct by moving, transferring ownership of the frame.
409   409  
410   @par Postconditions 410   @par Postconditions
411   `other` is empty and must not be awaited. 411   `other` is empty and must not be awaited.
412   412  
413   @param other The task to move from. 413   @param other The task to move from.
414   */ 414   */
HITCBC 415   4935 task(task&& other) noexcept 415   4887 task(task&& other) noexcept
HITCBC 416   4935 : h_(std::exchange(other.h_, nullptr)) 416   4887 : h_(std::exchange(other.h_, nullptr))
417   { 417   {
HITCBC 418   4935 } 418   4887 }
419   419  
420   /** Assign by moving, transferring ownership of the frame. 420   /** Assign by moving, transferring ownership of the frame.
421   421  
422   If this task already owns a coroutine frame, that frame is 422   If this task already owns a coroutine frame, that frame is
423   destroyed first. Self-assignment is a no-op. 423   destroyed first. Self-assignment is a no-op.
424   424  
425   @par Postconditions 425   @par Postconditions
426   `other` is empty and must not be awaited. 426   `other` is empty and must not be awaited.
427   427  
428   @param other The task to move from. 428   @param other The task to move from.
429   429  
430   @return `*this`. 430   @return `*this`.
431   */ 431   */
432   task& operator=(task&& other) noexcept 432   task& operator=(task&& other) noexcept
433   { 433   {
434   if(this != &other) 434   if(this != &other)
435   { 435   {
436   if(h_) 436   if(h_)
437   h_.destroy(); 437   h_.destroy();
438   h_ = std::exchange(other.h_, nullptr); 438   h_ = std::exchange(other.h_, nullptr);
439   } 439   }
440   return *this; 440   return *this;
441   } 441   }
442   442  
443   private: 443   private:
HITCBC 444   4701 explicit task(std::coroutine_handle<promise_type> h) 444   4668 explicit task(std::coroutine_handle<promise_type> h)
HITCBC 445   4701 : h_(h) 445   4668 : h_(h)
446   { 446   {
HITCBC 447   4701 } 447   4668 }
448   }; 448   };
449   449  
450   } // namespace capy 450   } // namespace capy
451   } // namespace boost 451   } // namespace boost
452   452  
453   #endif 453   #endif