100.00% Lines (95/95) 100.00% Functions (28/28)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2026 Michael Vandeberg 2   // Copyright (c) 2026 Michael Vandeberg
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_QUITTER_HPP 10   #ifndef BOOST_CAPY_QUITTER_HPP
11   #define BOOST_CAPY_QUITTER_HPP 11   #define BOOST_CAPY_QUITTER_HPP
12   12  
13   #include <boost/capy/detail/config.hpp> 13   #include <boost/capy/detail/config.hpp>
14   #include <boost/capy/detail/stop_requested_exception.hpp> 14   #include <boost/capy/detail/stop_requested_exception.hpp>
15   #include <boost/capy/concept/executor.hpp> 15   #include <boost/capy/concept/executor.hpp>
16   #include <boost/capy/concept/io_awaitable.hpp> 16   #include <boost/capy/concept/io_awaitable.hpp>
17   #include <boost/capy/ex/io_awaitable_promise_base.hpp> 17   #include <boost/capy/ex/io_awaitable_promise_base.hpp>
18   #include <boost/capy/ex/io_env.hpp> 18   #include <boost/capy/ex/io_env.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   21  
22   #include <exception> 22   #include <exception>
23   #include <optional> 23   #include <optional>
24   #include <type_traits> 24   #include <type_traits>
25   #include <utility> 25   #include <utility>
26   26  
27   /* Stop-aware coroutine task. 27   /* Stop-aware coroutine task.
28   28  
29   quitter<T> is identical to task<T> except that when the stop token 29   quitter<T> is identical to task<T> except that when the stop token
30   is triggered, the coroutine body never sees the cancellation. The 30   is triggered, the coroutine body never sees the cancellation. The
31   promise intercepts it on resume (in transform_awaiter::await_resume) 31   promise intercepts it on resume (in transform_awaiter::await_resume)
32   and throws a sentinel exception that unwinds through RAII destructors 32   and throws a sentinel exception that unwinds through RAII destructors
33   to final_suspend. The parent sees a "stopped" completion. 33   to final_suspend. The parent sees a "stopped" completion.
34   34  
35   See doc/quitter.md for the full design rationale. */ 35   See doc/quitter.md for the full design rationale. */
36   36  
37   namespace boost { 37   namespace boost {
38   namespace capy { 38   namespace capy {
39   39  
40   namespace detail { 40   namespace detail {
41   41  
42   // Reuse the same return-value storage as task<T>. 42   // Reuse the same return-value storage as task<T>.
43   // task_return_base is defined in task.hpp, but quitter needs its own 43   // task_return_base is defined in task.hpp, but quitter needs its own
44   // copy to avoid a header dependency on task.hpp. 44   // copy to avoid a header dependency on task.hpp.
45   template<typename T> 45   template<typename T>
46   struct quitter_return_base 46   struct quitter_return_base
47   { 47   {
48   std::optional<T> result_; 48   std::optional<T> result_;
49   49  
HITCBC 50   11 void return_value(T value) 50   11 void return_value(T value)
51   { 51   {
HITCBC 52   11 result_ = std::move(value); 52   11 result_ = std::move(value);
HITCBC 53   11 } 53   11 }
54   54  
HITCBC 55   5 T&& result() noexcept 55   5 T&& result() noexcept
56   { 56   {
HITCBC 57   5 return std::move(*result_); 57   5 return std::move(*result_);
58   } 58   }
59   }; 59   };
60   60  
61   template<> 61   template<>
62   struct quitter_return_base<void> 62   struct quitter_return_base<void>
63   { 63   {
HITCBC 64   2 void return_void() 64   2 void return_void()
65   { 65   {
HITCBC 66   2 } 66   2 }
67   }; 67   };
68   68  
69   } // namespace detail 69   } // namespace detail
70   70  
71   /** Stop-aware lazy coroutine task satisfying @ref IoRunnable. 71   /** Stop-aware lazy coroutine task satisfying @ref IoRunnable.
72   72  
73   When the stop token is triggered, the next `co_await` inside the 73   When the stop token is triggered, the next `co_await` inside the
74   coroutine short-circuits: the body never sees the result and RAII 74   coroutine short-circuits: the body never sees the result and RAII
75   destructors run normally. The parent observes a "stopped" 75   destructors run normally. The parent observes a "stopped"
76   completion via @ref promise_type::stopped. 76   completion via @ref promise_type::stopped.
77   77  
78   Everything else — frame allocation, environment propagation, 78   Everything else — frame allocation, environment propagation,
79   symmetric transfer, move semantics — is identical to @ref task. 79   symmetric transfer, move semantics — is identical to @ref task.
80   80  
81   @tparam T The result type. Use `quitter<>` for `quitter<void>`. 81   @tparam T The result type. Use `quitter<>` for `quitter<void>`.
82   82  
83   @see task, IoRunnable, IoAwaitable 83   @see task, IoRunnable, IoAwaitable
84   */ 84   */
85   template<typename T = void> 85   template<typename T = void>
86   struct [[nodiscard]] BOOST_CAPY_CORO_AWAIT_ELIDABLE 86   struct [[nodiscard]] BOOST_CAPY_CORO_AWAIT_ELIDABLE
87   quitter 87   quitter
88   { 88   {
89   /** The coroutine promise type for `quitter<T>`. 89   /** The coroutine promise type for `quitter<T>`.
90   90  
91   This is the promise object the compiler associates with a 91   This is the promise object the compiler associates with a
92   `quitter<T>` coroutine. It satisfies the coroutine promise 92   `quitter<T>` coroutine. It satisfies the coroutine promise
93   requirements and participates in the I/O awaitable protocol via 93   requirements and participates in the I/O awaitable protocol via
94   @ref io_awaitable_promise_base. Unlike @ref task::promise_type, 94   @ref io_awaitable_promise_base. Unlike @ref task::promise_type,
95   its `transform_awaitable` checks the stop token before each 95   its `transform_awaitable` checks the stop token before each
96   awaited result reaches the body, throwing an internal sentinel 96   awaited result reaches the body, throwing an internal sentinel
97   exception that unwinds to a "stopped" completion. It is part of 97   exception that unwinds to a "stopped" completion. It is part of
98   the coroutine machinery and is not intended to be used directly 98   the coroutine machinery and is not intended to be used directly
99   by callers. 99   by callers.
100   100  
101   Result storage and `return_value`/`return_void` are provided by 101   Result storage and `return_value`/`return_void` are provided by
102   `detail::quitter_return_base<T>`. 102   `detail::quitter_return_base<T>`.
103   103  
104   @see io_awaitable_promise_base, IoRunnable 104   @see io_awaitable_promise_base, IoRunnable
105   */ 105   */
106   struct promise_type 106   struct promise_type
107   : io_awaitable_promise_base<promise_type> 107   : io_awaitable_promise_base<promise_type>
108   , detail::quitter_return_base<T> 108   , detail::quitter_return_base<T>
109   { 109   {
110   private: 110   private:
111   friend quitter; 111   friend quitter;
112   112  
113   enum class completion { running, value, exception, stopped }; 113   enum class completion { running, value, exception, stopped };
114   114  
115   union { std::exception_ptr ep_; }; 115   union { std::exception_ptr ep_; };
116   completion state_; 116   completion state_;
117   117  
118   public: 118   public:
119   /// Construct the promise in the running state. 119   /// Construct the promise in the running state.
HITCBC 120   33 promise_type() noexcept 120   33 promise_type() noexcept
HITCBC 121   33 : state_(completion::running) 121   33 : state_(completion::running)
122   { 122   {
HITCBC 123   33 } 123   33 }
124   124  
125   /// Destroy the promise, releasing any stored exception. 125   /// Destroy the promise, releasing any stored exception.
HITCBC 126   33 ~promise_type() 126   33 ~promise_type()
127   { 127   {
HITCBC 128   33 if(state_ == completion::exception || 128   33 if(state_ == completion::exception ||
HITCBC 129   29 state_ == completion::stopped) 129   29 state_ == completion::stopped)
HITCBC 130   20 ep_.~exception_ptr(); 130   20 ep_.~exception_ptr();
HITCBC 131   33 } 131   33 }
132   132  
133   /// Return a non-null exception_ptr when the coroutine threw 133   /// Return a non-null exception_ptr when the coroutine threw
134   /// or was stopped. Stopped quitters report the sentinel 134   /// or was stopped. Stopped quitters report the sentinel
135   /// stop_requested_exception so that run_async routes to 135   /// stop_requested_exception so that run_async routes to
136   /// the error handler instead of accessing a non-existent 136   /// the error handler instead of accessing a non-existent
137   /// result. 137   /// result.
HITCBC 138   26 std::exception_ptr exception() const noexcept 138   26 std::exception_ptr exception() const noexcept
139   { 139   {
HITCBC 140   26 if(state_ == completion::exception || 140   26 if(state_ == completion::exception ||
HITCBC 141   20 state_ == completion::stopped) 141   20 state_ == completion::stopped)
HITCBC 142   20 return ep_; 142   20 return ep_;
HITCBC 143   6 return {}; 143   6 return {};
144   } 144   }
145   145  
146   /// True when the coroutine was stopped via the stop token. 146   /// True when the coroutine was stopped via the stop token.
HITCBC 147   12 bool stopped() const noexcept 147   12 bool stopped() const noexcept
148   { 148   {
HITCBC 149   12 return state_ == completion::stopped; 149   12 return state_ == completion::stopped;
150   } 150   }
151   151  
152   /** Return the owning `quitter` for this coroutine. 152   /** Return the owning `quitter` for this coroutine.
153   153  
154   Called by the compiler to produce the object returned to the 154   Called by the compiler to produce the object returned to the
155   caller when the coroutine is created. 155   caller when the coroutine is created.
156   156  
157   @return A `quitter` owning the coroutine frame. 157   @return A `quitter` owning the coroutine frame.
158   */ 158   */
HITCBC 159   33 quitter get_return_object() 159   33 quitter get_return_object()
160   { 160   {
161   return quitter{ 161   return quitter{
HITCBC 162   33 std::coroutine_handle<promise_type>::from_promise(*this)}; 162   33 std::coroutine_handle<promise_type>::from_promise(*this)};
163   } 163   }
164   164  
165   /** Return the initial-suspend awaiter. 165   /** Return the initial-suspend awaiter.
166   166  
167   The coroutine always suspends at the initial suspend point, 167   The coroutine always suspends at the initial suspend point,
168   so the body does not start until the quitter is awaited. When 168   so the body does not start until the quitter is awaited. When
169   the body is resumed, the awaiter restores the thread-local 169   the body is resumed, the awaiter restores the thread-local
170   frame allocator and, if stop has already been requested, 170   frame allocator and, if stop has already been requested,
171   throws the internal sentinel exception so the body never 171   throws the internal sentinel exception so the body never
172   runs and the coroutine completes as stopped. 172   runs and the coroutine completes as stopped.
173   173  
174   @return An awaiter that suspends unconditionally. 174   @return An awaiter that suspends unconditionally.
175   */ 175   */
HITCBC 176   33 auto initial_suspend() noexcept 176   33 auto initial_suspend() noexcept
177   { 177   {
178   struct awaiter 178   struct awaiter
179   { 179   {
180   promise_type* p_; 180   promise_type* p_;
181   181  
HITCBC 182   33 bool await_ready() const noexcept 182   33 bool await_ready() const noexcept
183   { 183   {
HITCBC 184   33 return false; 184   33 return false;
185   } 185   }
186   186  
HITCBC 187   33 void await_suspend(std::coroutine_handle<>) const noexcept 187   33 void await_suspend(std::coroutine_handle<>) const noexcept
188   { 188   {
HITCBC 189   33 } 189   33 }
190   190  
191   // Potentially-throwing: checks the stop token before 191   // Potentially-throwing: checks the stop token before
192   // the coroutine body executes its first statement. 192   // the coroutine body executes its first statement.
HITCBC 193   33 void await_resume() const 193   33 void await_resume() const
194   { 194   {
HITCBC 195   33 set_current_frame_allocator( 195   33 set_current_frame_allocator(
HITCBC 196   33 p_->environment()->frame_allocator); 196   33 p_->environment()->frame_allocator);
HITCBC 197   33 if(p_->environment()->stop_token.stop_requested()) 197   33 if(p_->environment()->stop_token.stop_requested())
HITCBC 198   3 throw detail::stop_requested_exception{}; 198   3 throw detail::stop_requested_exception{};
HITCBC 199   30 } 199   30 }
200   }; 200   };
HITCBC 201   33 return awaiter{this}; 201   33 return awaiter{this};
202   } 202   }
203   203  
204   /** Return the final-suspend awaiter. 204   /** Return the final-suspend awaiter.
205   205  
206   The coroutine always suspends at the final suspend point. The 206   The coroutine always suspends at the final suspend point. The
207   awaiter's `await_suspend` performs symmetric transfer to the 207   awaiter's `await_suspend` performs symmetric transfer to the
208   stored continuation, resuming the awaiting coroutine. 208   stored continuation, resuming the awaiting coroutine.
209   209  
210   @return An awaiter that suspends and transfers to the 210   @return An awaiter that suspends and transfers to the
211   continuation. 211   continuation.
212   */ 212   */
HITCBC 213   33 auto final_suspend() noexcept 213   33 auto final_suspend() noexcept
214   { 214   {
215   struct awaiter 215   struct awaiter
216   { 216   {
217   promise_type* p_; 217   promise_type* p_;
218   218  
HITCBC 219   33 bool await_ready() const noexcept 219   33 bool await_ready() const noexcept
220   { 220   {
HITCBC 221   33 return false; 221   33 return false;
222   } 222   }
223   223  
HITCBC 224   33 std::coroutine_handle<> await_suspend( 224   33 std::coroutine_handle<> await_suspend(
225   std::coroutine_handle<>) const noexcept 225   std::coroutine_handle<>) const noexcept
226   { 226   {
HITCBC 227   33 return p_->continuation(); 227   33 return p_->continuation();
228   } 228   }
229   229  
230   void await_resume() const noexcept {} // LCOV_EXCL_LINE final_suspend awaiter, never resumed 230   void await_resume() const noexcept {} // LCOV_EXCL_LINE final_suspend awaiter, never resumed
231   }; 231   };
HITCBC 232   33 return awaiter{this}; 232   33 return awaiter{this};
233   } 233   }
234   234  
235   /** Capture the in-flight exception from the coroutine body. 235   /** Capture the in-flight exception from the coroutine body.
236   236  
237   Called by the compiler when the coroutine body exits via an 237   Called by the compiler when the coroutine body exits via an
238   unhandled exception. The internal stop sentinel is recorded as 238   unhandled exception. The internal stop sentinel is recorded as
239   a stopped completion; any other exception is recorded as an 239   a stopped completion; any other exception is recorded as an
240   exception completion. The stored exception is surfaced (or 240   exception completion. The stored exception is surfaced (or
241   routed to the error handler) when the quitter is awaited or run. 241   routed to the error handler) when the quitter is awaited or run.
242   */ 242   */
HITCBC 243   20 void unhandled_exception() 243   20 void unhandled_exception()
244   { 244   {
245   try 245   try
246   { 246   {
HITCBC 247   20 throw; 247   20 throw;
248   } 248   }
HITCBC 249   20 catch(detail::stop_requested_exception const&) 249   20 catch(detail::stop_requested_exception const&)
250   { 250   {
251   // Store the exception_ptr so that run_async's 251   // Store the exception_ptr so that run_async's
252   // invoke_impl routes to the error handler 252   // invoke_impl routes to the error handler
253   // instead of accessing a non-existent result. 253   // instead of accessing a non-existent result.
HITCBC 254   16 new (&ep_) std::exception_ptr( 254   16 new (&ep_) std::exception_ptr(
255   std::current_exception()); 255   std::current_exception());
HITCBC 256   16 state_ = completion::stopped; 256   16 state_ = completion::stopped;
257   } 257   }
HITCBC 258   4 catch(...) 258   4 catch(...)
259   { 259   {
HITCBC 260   4 new (&ep_) std::exception_ptr( 260   4 new (&ep_) std::exception_ptr(
261   std::current_exception()); 261   std::current_exception());
HITCBC 262   4 state_ = completion::exception; 262   4 state_ = completion::exception;
263   } 263   }
HITCBC 264   20 } 264   20 }
265   265  
266   //------------------------------------------------------ 266   //------------------------------------------------------
267   // transform_awaitable — the key difference from task<T> 267   // transform_awaitable — the key difference from task<T>
268   //------------------------------------------------------ 268   //------------------------------------------------------
269   269  
270   /** Awaiter wrapping a nested `co_await` of an @ref IoAwaitable. 270   /** Awaiter wrapping a nested `co_await` of an @ref IoAwaitable.
271   271  
272   Forwards the environment to the inner awaitable's 272   Forwards the environment to the inner awaitable's
273   environment-taking `await_suspend` and restores the 273   environment-taking `await_suspend` and restores the
274   thread-local frame allocator before the body resumes. Unlike 274   thread-local frame allocator before the body resumes. Unlike
275   `task`'s, it also checks the stop token on resumption, throwing 275   `task`'s, it also checks the stop token on resumption, throwing
276   the internal sentinel so a stop request unwinds the body before 276   the internal sentinel so a stop request unwinds the body before
277   it observes the I/O result. 277   it observes the I/O result.
278   278  
279   @tparam Awaitable The awaitable being transformed. 279   @tparam Awaitable The awaitable being transformed.
280   */ 280   */
281   template<class Awaitable> 281   template<class Awaitable>
282   struct transform_awaiter 282   struct transform_awaiter
283   { 283   {
284   std::decay_t<Awaitable> a_; 284   std::decay_t<Awaitable> a_;
285   promise_type* p_; 285   promise_type* p_;
286   286  
HITCBC 287   21 bool await_ready() noexcept 287   21 bool await_ready() noexcept
288   { 288   {
HITCBC 289   21 return a_.await_ready(); 289   21 return a_.await_ready();
290   } 290   }
291   291  
292   // Check the stop token BEFORE the coroutine body 292   // Check the stop token BEFORE the coroutine body
293   // sees the result of the I/O operation. 293   // sees the result of the I/O operation.
HITCBC 294   21 decltype(auto) await_resume() 294   21 decltype(auto) await_resume()
295   { 295   {
HITCBC 296   21 set_current_frame_allocator( 296   21 set_current_frame_allocator(
HITCBC 297   21 p_->environment()->frame_allocator); 297   21 p_->environment()->frame_allocator);
HITCBC 298   21 if(p_->environment()->stop_token.stop_requested()) 298   21 if(p_->environment()->stop_token.stop_requested())
HITCBC 299   13 throw detail::stop_requested_exception{}; 299   13 throw detail::stop_requested_exception{};
HITCBC 300   8 return a_.await_resume(); 300   8 return a_.await_resume();
301   } 301   }
302   302  
303   template<class Promise> 303   template<class Promise>
HITCBC 304   20 auto await_suspend( 304   20 auto await_suspend(
305   std::coroutine_handle<Promise> h) noexcept 305   std::coroutine_handle<Promise> h) noexcept
306   { 306   {
307   using R = decltype( 307   using R = decltype(
308   a_.await_suspend(h, p_->environment())); 308   a_.await_suspend(h, p_->environment()));
309   if constexpr (std::is_same_v< 309   if constexpr (std::is_same_v<
310   R, std::coroutine_handle<>>) 310   R, std::coroutine_handle<>>)
HITCBC 311   18 return detail::symmetric_transfer( 311   18 return detail::symmetric_transfer(
HITCBC 312   36 a_.await_suspend(h, p_->environment())); 312   36 a_.await_suspend(h, p_->environment()));
313   else 313   else
HITCBC 314   2 return a_.await_suspend( 314   2 return a_.await_suspend(
HITCBC 315   4 h, p_->environment()); 315   4 h, p_->environment());
316   } 316   }
317   }; 317   };
318   318  
319   /** Transform a nested awaitable before `co_await`. 319   /** Transform a nested awaitable before `co_await`.
320   320  
321   Wraps an @ref IoAwaitable in a @ref transform_awaiter so the 321   Wraps an @ref IoAwaitable in a @ref transform_awaiter so the
322   coroutine's environment is propagated into it and the stop 322   coroutine's environment is propagated into it and the stop
323   token is checked on resumption. A diagnostic is emitted if the 323   token is checked on resumption. A diagnostic is emitted if the
324   awaitable does not satisfy @ref IoAwaitable. 324   awaitable does not satisfy @ref IoAwaitable.
325   325  
326   @param a The awaitable expression from `co_await a`. 326   @param a The awaitable expression from `co_await a`.
327   327  
328   @return A @ref transform_awaiter wrapping `a`. 328   @return A @ref transform_awaiter wrapping `a`.
329   */ 329   */
330   template<class Awaitable> 330   template<class Awaitable>
HITCBC 331   21 auto transform_awaitable(Awaitable&& a) 331   21 auto transform_awaitable(Awaitable&& a)
332   { 332   {
333   using A = std::decay_t<Awaitable>; 333   using A = std::decay_t<Awaitable>;
334   if constexpr (IoAwaitable<A>) 334   if constexpr (IoAwaitable<A>)
335   { 335   {
336   return transform_awaiter<Awaitable>{ 336   return transform_awaiter<Awaitable>{
HITCBC 337   39 std::forward<Awaitable>(a), this}; 337   39 std::forward<Awaitable>(a), this};
338   } 338   }
339   else 339   else
340   { 340   {
341   static_assert(sizeof(A) == 0, 341   static_assert(sizeof(A) == 0,
342   "requires IoAwaitable"); 342   "requires IoAwaitable");
343   } 343   }
HITCBC 344   18 } 344   18 }
345   }; 345   };
346   346  
347   /** Handle to the owned coroutine frame. 347   /** Handle to the owned coroutine frame.
348   348  
349   Null when the quitter is empty (for example after a move or after 349   Null when the quitter is empty (for example after a move or after
350   @ref release). Prefer @ref handle to read this; the member is 350   @ref release). Prefer @ref handle to read this; the member is
351   public for use by the coroutine machinery. 351   public for use by the coroutine machinery.
352   */ 352   */
353   std::coroutine_handle<promise_type> h_; 353   std::coroutine_handle<promise_type> h_;
354   354  
355   /// Destroy the quitter and its coroutine frame if owned. 355   /// Destroy the quitter and its coroutine frame if owned.
HITCBC 356   82 ~quitter() 356   82 ~quitter()
357   { 357   {
HITCBC 358   82 if(h_) 358   82 if(h_)
HITCBC 359   15 h_.destroy(); 359   15 h_.destroy();
HITCBC 360   82 } 360   82 }
361   361  
362   /// Return false; quitters are never immediately ready. 362   /// Return false; quitters are never immediately ready.
HITCBC 363   15 bool await_ready() const noexcept 363   15 bool await_ready() const noexcept
364   { 364   {
HITCBC 365   15 return false; 365   15 return false;
366   } 366   }
367   367  
368   /** Return the result, rethrow exception, or propagate stop. 368   /** Return the result, rethrow exception, or propagate stop.
369   369  
370   When stopped, throws stop_requested_exception so that a 370   When stopped, throws stop_requested_exception so that a
371   parent quitter also stops. A parent task<T> will see this 371   parent quitter also stops. A parent task<T> will see this
372   as an unhandled exception — by design. 372   as an unhandled exception — by design.
373   */ 373   */
HITCBC 374   12 auto await_resume() 374   12 auto await_resume()
375   { 375   {
HITCBC 376   12 if(h_.promise().stopped()) 376   12 if(h_.promise().stopped())
HITCBC 377   6 throw detail::stop_requested_exception{}; 377   6 throw detail::stop_requested_exception{};
HITCBC 378   6 if(h_.promise().state_ == promise_type::completion::exception) 378   6 if(h_.promise().state_ == promise_type::completion::exception)
HITCBC 379   1 std::rethrow_exception(h_.promise().ep_); 379   1 std::rethrow_exception(h_.promise().ep_);
380   if constexpr (! std::is_void_v<T>) 380   if constexpr (! std::is_void_v<T>)
HITCBC 381   4 return std::move(*h_.promise().result_); 381   4 return std::move(*h_.promise().result_);
382   else 382   else
HITCBC 383   1 return; 383   1 return;
384   } 384   }
385   385  
386   /// Start execution with the caller's context. 386   /// Start execution with the caller's context.
HITCBC 387   15 std::coroutine_handle<> await_suspend( 387   15 std::coroutine_handle<> await_suspend(
388   std::coroutine_handle<> cont, 388   std::coroutine_handle<> cont,
389   io_env const* env) 389   io_env const* env)
390   { 390   {
HITCBC 391   15 h_.promise().set_continuation(cont); 391   15 h_.promise().set_continuation(cont);
HITCBC 392   15 h_.promise().set_environment(env); 392   15 h_.promise().set_environment(env);
HITCBC 393   15 return h_; 393   15 return h_;
394   } 394   }
395   395  
396   /** Return the coroutine handle. 396   /** Return the coroutine handle.
397   397  
398   @note Do not call `destroy()` on the returned handle while 398   @note Do not call `destroy()` on the returned handle while
399   the quitter is being awaited. The quitter's lifetime is 399   the quitter is being awaited. The quitter's lifetime is
400   normally managed by `run_async`, `run`, or the awaiting 400   normally managed by `run_async`, `run`, or the awaiting
401   parent; manually destroying a suspended quitter that another 401   parent; manually destroying a suspended quitter that another
402   coroutine is awaiting produces undefined behavior. For 402   coroutine is awaiting produces undefined behavior. For
403   cooperative cancellation, use `std::stop_token`. 403   cooperative cancellation, use `std::stop_token`.
404   404  
405   @return The coroutine handle. 405   @return The coroutine handle.
406   */ 406   */
HITCBC 407   20 std::coroutine_handle<promise_type> handle() const noexcept 407   20 std::coroutine_handle<promise_type> handle() const noexcept
408   { 408   {
HITCBC 409   20 return h_; 409   20 return h_;
410   } 410   }
411   411  
412   /** Release ownership of the coroutine frame. 412   /** Release ownership of the coroutine frame.
413   413  
414   @note If the caller intends to call `destroy()` on the 414   @note If the caller intends to call `destroy()` on the
415   released handle, it must do so only when the quitter has not 415   released handle, it must do so only when the quitter has not
416   started or has fully completed. Destroying a suspended 416   started or has fully completed. Destroying a suspended
417   quitter that is being awaited produces undefined behavior. 417   quitter that is being awaited produces undefined behavior.
418   */ 418   */
HITCBC 419   18 void release() noexcept 419   18 void release() noexcept
420   { 420   {
HITCBC 421   18 h_ = nullptr; 421   18 h_ = nullptr;
HITCBC 422   18 } 422   18 }
423   423  
424   quitter(quitter const&) = delete; 424   quitter(quitter const&) = delete;
425   quitter& operator=(quitter const&) = delete; 425   quitter& operator=(quitter const&) = delete;
426   426  
427   /// Construct by moving, transferring ownership. 427   /// Construct by moving, transferring ownership.
HITCBC 428   49 quitter(quitter&& other) noexcept 428   49 quitter(quitter&& other) noexcept
HITCBC 429   49 : h_(std::exchange(other.h_, nullptr)) 429   49 : h_(std::exchange(other.h_, nullptr))
430   { 430   {
HITCBC 431   49 } 431   49 }
432   432  
433   /// Assign by moving, transferring ownership. 433   /// Assign by moving, transferring ownership.
434   quitter& operator=(quitter&& other) noexcept 434   quitter& operator=(quitter&& other) noexcept
435   { 435   {
436   if(this != &other) 436   if(this != &other)
437   { 437   {
438   if(h_) 438   if(h_)
439   h_.destroy(); 439   h_.destroy();
440   h_ = std::exchange(other.h_, nullptr); 440   h_ = std::exchange(other.h_, nullptr);
441   } 441   }
442   return *this; 442   return *this;
443   } 443   }
444   444  
445   private: 445   private:
HITCBC 446   33 explicit quitter(std::coroutine_handle<promise_type> h) 446   33 explicit quitter(std::coroutine_handle<promise_type> h)
HITCBC 447   33 : h_(h) 447   33 : h_(h)
448   { 448   {
HITCBC 449   33 } 449   33 }
450   }; 450   };
451   451  
452   } // namespace capy 452   } // namespace capy
453   } // namespace boost 453   } // namespace boost
454   454  
455   #endif 455   #endif