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