include/boost/capy/quitter.hpp

88.3% Lines (302/342) 94.9% List of functions (94/99)
quitter.hpp
f(x) Functions (99)
Function Calls Lines Blocks
boost::capy::detail::quitter_return_base<boost::capy::io_result<unsigned long> >::return_value(boost::capy::io_result<unsigned long>) :50 1x 100.0% 100.0% boost::capy::detail::quitter_return_base<int>::return_value(int) :50 7x 100.0% 100.0% boost::capy::detail::quitter_return_base<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::return_value(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) :50 1x 100.0% 100.0% boost::capy::detail::quitter_return_base<int>::result() :55 2x 100.0% 100.0% boost::capy::detail::quitter_return_base<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::result() :55 1x 100.0% 100.0% boost::capy::detail::quitter_return_base<void>::return_void() :64 1x 100.0% 100.0% boost::capy::quitter<boost::capy::io_result<unsigned long> >::promise_type::promise_type() :102 6x 100.0% 100.0% boost::capy::quitter<int>::promise_type::promise_type() :102 10x 100.0% 100.0% boost::capy::quitter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::promise_type::promise_type() :102 1x 100.0% 100.0% boost::capy::quitter<void>::promise_type::promise_type() :102 11x 100.0% 100.0% boost::capy::quitter<boost::capy::io_result<unsigned long> >::promise_type::~promise_type() :107 6x 100.0% 100.0% boost::capy::quitter<int>::promise_type::~promise_type() :107 10x 100.0% 100.0% boost::capy::quitter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::promise_type::~promise_type() :107 1x 80.0% 83.0% boost::capy::quitter<void>::promise_type::~promise_type() :107 11x 100.0% 100.0% boost::capy::quitter<int>::promise_type::exception() const :119 6x 100.0% 100.0% boost::capy::quitter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::promise_type::exception() const :119 1x 80.0% 80.0% boost::capy::quitter<void>::promise_type::exception() const :119 15x 100.0% 100.0% boost::capy::quitter<boost::capy::io_result<unsigned long> >::promise_type::stopped() const :128 6x 100.0% 100.0% boost::capy::quitter<int>::promise_type::stopped() const :128 4x 100.0% 100.0% boost::capy::quitter<void>::promise_type::stopped() const :128 0 0.0% 0.0% boost::capy::quitter<boost::capy::io_result<unsigned long> >::promise_type::get_return_object() :133 6x 100.0% 100.0% boost::capy::quitter<int>::promise_type::get_return_object() :133 10x 100.0% 100.0% boost::capy::quitter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::promise_type::get_return_object() :133 1x 100.0% 100.0% boost::capy::quitter<void>::promise_type::get_return_object() :133 11x 100.0% 100.0% boost::capy::quitter<boost::capy::io_result<unsigned long> >::promise_type::initial_suspend() :139 6x 100.0% 100.0% boost::capy::quitter<int>::promise_type::initial_suspend() :139 10x 100.0% 100.0% boost::capy::quitter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::promise_type::initial_suspend() :139 1x 100.0% 100.0% boost::capy::quitter<void>::promise_type::initial_suspend() :139 11x 100.0% 100.0% boost::capy::quitter<boost::capy::io_result<unsigned long> >::promise_type::final_suspend() :167 6x 100.0% 100.0% boost::capy::quitter<int>::promise_type::final_suspend() :167 10x 100.0% 100.0% boost::capy::quitter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::promise_type::final_suspend() :167 1x 100.0% 100.0% boost::capy::quitter<void>::promise_type::final_suspend() :167 11x 100.0% 100.0% boost::capy::quitter<boost::capy::io_result<unsigned long> >::promise_type::unhandled_exception() :191 5x 66.7% 73.0% boost::capy::quitter<int>::promise_type::unhandled_exception() :191 3x 100.0% 100.0% boost::capy::quitter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::promise_type::unhandled_exception() :191 0 0.0% 0.0% boost::capy::quitter<void>::promise_type::unhandled_exception() :191 10x 100.0% 100.0% boost::capy::quitter<boost::capy::io_result<unsigned long> >::promise_type::transform_awaiter<boost::capy::stop_only_awaitable>::await_ready() :224 4x 100.0% 100.0% boost::capy::quitter<int>::promise_type::transform_awaiter<boost::capy::quitter<int> >::await_ready() :224 2x 100.0% 100.0% boost::capy::quitter<int>::promise_type::transform_awaiter<boost::capy::stop_only_awaitable>::await_ready() :224 1x 100.0% 100.0% boost::capy::quitter<int>::promise_type::transform_awaiter<boost::capy::yield_awaitable>::await_ready() :224 1x 100.0% 100.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::delay_awaitable>::await_ready() :224 1x 100.0% 100.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::quitter<void> >::await_ready() :224 3x 100.0% 100.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::stop_only_awaitable>::await_ready() :224 4x 100.0% 100.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::test::stream::read_some<boost::capy::mutable_buffer>(boost::capy::mutable_buffer)::awaitable>::await_ready() :224 1x 100.0% 100.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::test::stream::write_some<boost::capy::mutable_buffer>(boost::capy::mutable_buffer)::awaitable>::await_ready() :224 1x 100.0% 100.0% boost::capy::quitter<boost::capy::io_result<unsigned long> >::promise_type::transform_awaiter<boost::capy::stop_only_awaitable>::await_resume() :231 4x 83.3% 89.0% boost::capy::quitter<int>::promise_type::transform_awaiter<boost::capy::quitter<int> >::await_resume() :231 2x 83.3% 80.0% boost::capy::quitter<int>::promise_type::transform_awaiter<boost::capy::stop_only_awaitable>::await_resume() :231 1x 83.3% 89.0% boost::capy::quitter<int>::promise_type::transform_awaiter<boost::capy::yield_awaitable>::await_resume() :231 1x 83.3% 78.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::delay_awaitable>::await_resume() :231 1x 83.3% 89.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::quitter<void> >::await_resume() :231 3x 83.3% 89.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::stop_only_awaitable>::await_resume() :231 4x 83.3% 89.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::test::stream::read_some<boost::capy::mutable_buffer>(boost::capy::mutable_buffer)::awaitable>::await_resume() :231 1x 83.3% 78.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::test::stream::write_some<boost::capy::mutable_buffer>(boost::capy::mutable_buffer)::awaitable>::await_resume() :231 1x 83.3% 78.0% auto boost::capy::quitter<boost::capy::io_result<unsigned long> >::promise_type::transform_awaiter<boost::capy::stop_only_awaitable>::await_suspend<boost::capy::quitter<boost::capy::io_result<unsigned long> >::promise_type>(std::__n4861::coroutine_handle<boost::capy::quitter<boost::capy::io_result<unsigned long> >::promise_type>) :241 4x 100.0% 100.0% auto boost::capy::quitter<int>::promise_type::transform_awaiter<boost::capy::quitter<int> >::await_suspend<boost::capy::quitter<int>::promise_type>(std::__n4861::coroutine_handle<boost::capy::quitter<int>::promise_type>) :241 2x 100.0% 100.0% auto boost::capy::quitter<int>::promise_type::transform_awaiter<boost::capy::stop_only_awaitable>::await_suspend<boost::capy::quitter<int>::promise_type>(std::__n4861::coroutine_handle<boost::capy::quitter<int>::promise_type>) :241 1x 100.0% 100.0% auto boost::capy::quitter<int>::promise_type::transform_awaiter<boost::capy::yield_awaitable>::await_suspend<boost::capy::quitter<int>::promise_type>(std::__n4861::coroutine_handle<boost::capy::quitter<int>::promise_type>) :241 1x 100.0% 100.0% auto boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::delay_awaitable>::await_suspend<boost::capy::quitter<void>::promise_type>(std::__n4861::coroutine_handle<boost::capy::quitter<void>::promise_type>) :241 1x 100.0% 100.0% auto boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::quitter<void> >::await_suspend<boost::capy::quitter<void>::promise_type>(std::__n4861::coroutine_handle<boost::capy::quitter<void>::promise_type>) :241 3x 100.0% 100.0% auto boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::stop_only_awaitable>::await_suspend<boost::capy::quitter<void>::promise_type>(std::__n4861::coroutine_handle<boost::capy::quitter<void>::promise_type>) :241 4x 100.0% 100.0% auto boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::test::stream::read_some<boost::capy::mutable_buffer>(boost::capy::mutable_buffer)::awaitable>::await_suspend<boost::capy::quitter<void>::promise_type>(std::__n4861::coroutine_handle<boost::capy::quitter<void>::promise_type>) :241 0 0.0% 0.0% auto boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::test::stream::write_some<boost::capy::mutable_buffer>(boost::capy::mutable_buffer)::awaitable>::await_suspend<boost::capy::quitter<void>::promise_type>(std::__n4861::coroutine_handle<boost::capy::quitter<void>::promise_type>) :241 0 0.0% 0.0% auto boost::capy::quitter<boost::capy::io_result<unsigned long> >::promise_type::transform_awaitable<boost::capy::stop_only_awaitable>(boost::capy::stop_only_awaitable&&) :257 4x 100.0% 100.0% auto boost::capy::quitter<int>::promise_type::transform_awaitable<boost::capy::quitter<int> >(boost::capy::quitter<int>&&) :257 2x 100.0% 100.0% auto boost::capy::quitter<int>::promise_type::transform_awaitable<boost::capy::stop_only_awaitable>(boost::capy::stop_only_awaitable&&) :257 1x 100.0% 100.0% auto boost::capy::quitter<int>::promise_type::transform_awaitable<boost::capy::yield_awaitable>(boost::capy::yield_awaitable&&) :257 1x 100.0% 100.0% auto boost::capy::quitter<void>::promise_type::transform_awaitable<boost::capy::delay_awaitable>(boost::capy::delay_awaitable&&) :257 1x 100.0% 100.0% auto boost::capy::quitter<void>::promise_type::transform_awaitable<boost::capy::quitter<void> >(boost::capy::quitter<void>&&) :257 3x 100.0% 100.0% auto boost::capy::quitter<void>::promise_type::transform_awaitable<boost::capy::stop_only_awaitable>(boost::capy::stop_only_awaitable&&) :257 4x 100.0% 100.0% auto boost::capy::quitter<void>::promise_type::transform_awaitable<boost::capy::test::stream::read_some<boost::capy::mutable_buffer>(boost::capy::mutable_buffer)::awaitable>(boost::capy::test::stream::read_some<boost::capy::mutable_buffer>(boost::capy::mutable_buffer)::awaitable&&) :257 1x 100.0% 100.0% auto boost::capy::quitter<void>::promise_type::transform_awaitable<boost::capy::test::stream::write_some<boost::capy::mutable_buffer>(boost::capy::mutable_buffer)::awaitable>(boost::capy::test::stream::write_some<boost::capy::mutable_buffer>(boost::capy::mutable_buffer)::awaitable&&) :257 1x 100.0% 100.0% boost::capy::quitter<boost::capy::io_result<unsigned long> >::~quitter() :276 36x 100.0% 100.0% boost::capy::quitter<int>::~quitter() :276 18x 100.0% 100.0% boost::capy::quitter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::~quitter() :276 2x 75.0% 75.0% boost::capy::quitter<void>::~quitter() :276 16x 100.0% 100.0% boost::capy::quitter<boost::capy::io_result<unsigned long> >::await_ready() const :283 6x 100.0% 100.0% boost::capy::quitter<int>::await_ready() const :283 4x 100.0% 100.0% boost::capy::quitter<void>::await_ready() const :283 3x 100.0% 100.0% boost::capy::quitter<boost::capy::io_result<unsigned long> >::await_resume() :294 6x 83.3% 72.0% boost::capy::quitter<int>::await_resume() :294 4x 83.3% 72.0% boost::capy::quitter<void>::await_resume() :294 0 0.0% 0.0% boost::capy::quitter<boost::capy::io_result<unsigned long> >::await_suspend(std::__n4861::coroutine_handle<void>, boost::capy::io_env const*) :307 6x 100.0% 100.0% boost::capy::quitter<int>::await_suspend(std::__n4861::coroutine_handle<void>, boost::capy::io_env const*) :307 4x 100.0% 100.0% boost::capy::quitter<void>::await_suspend(std::__n4861::coroutine_handle<void>, boost::capy::io_env const*) :307 3x 100.0% 100.0% boost::capy::quitter<int>::handle() const :317 8x 100.0% 100.0% boost::capy::quitter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::handle() const :317 1x 100.0% 100.0% boost::capy::quitter<void>::handle() const :317 8x 100.0% 100.0% boost::capy::quitter<int>::release() :323 6x 100.0% 100.0% boost::capy::quitter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::release() :323 1x 100.0% 100.0% boost::capy::quitter<void>::release() :323 8x 100.0% 100.0% boost::capy::quitter<boost::capy::io_result<unsigned long> >::quitter(boost::capy::quitter<boost::capy::io_result<unsigned long> >&&) :332 30x 100.0% 100.0% boost::capy::quitter<int>::quitter(boost::capy::quitter<int>&&) :332 8x 100.0% 100.0% boost::capy::quitter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::quitter(boost::capy::quitter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >&&) :332 1x 100.0% 100.0% boost::capy::quitter<void>::quitter(boost::capy::quitter<void>&&) :332 5x 100.0% 100.0% boost::capy::quitter<boost::capy::io_result<unsigned long> >::quitter(std::__n4861::coroutine_handle<boost::capy::quitter<boost::capy::io_result<unsigned long> >::promise_type>) :350 6x 100.0% 100.0% boost::capy::quitter<int>::quitter(std::__n4861::coroutine_handle<boost::capy::quitter<int>::promise_type>) :350 10x 100.0% 100.0% boost::capy::quitter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::quitter(std::__n4861::coroutine_handle<boost::capy::quitter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::promise_type>) :350 1x 100.0% 100.0% boost::capy::quitter<void>::quitter(std::__n4861::coroutine_handle<boost::capy::quitter<void>::promise_type>) :350 11x 100.0% 100.0%
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2026 Michael Vandeberg
3 //
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)
6 //
7 // Official repository: https://github.com/cppalliance/capy
8 //
9
10 #ifndef BOOST_CAPY_QUITTER_HPP
11 #define BOOST_CAPY_QUITTER_HPP
12
13 #include <boost/capy/detail/config.hpp>
14 #include <boost/capy/detail/stop_requested_exception.hpp>
15 #include <boost/capy/concept/executor.hpp>
16 #include <boost/capy/concept/io_awaitable.hpp>
17 #include <boost/capy/ex/io_awaitable_promise_base.hpp>
18 #include <boost/capy/ex/io_env.hpp>
19 #include <boost/capy/ex/frame_allocator.hpp>
20 #include <boost/capy/detail/await_suspend_helper.hpp>
21
22 #include <exception>
23 #include <optional>
24 #include <type_traits>
25 #include <utility>
26
27 /* Stop-aware coroutine task.
28
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
31 promise intercepts it on resume (in transform_awaiter::await_resume)
32 and throws a sentinel exception that unwinds through RAII destructors
33 to final_suspend. The parent sees a "stopped" completion.
34
35 See doc/quitter.md for the full design rationale. */
36
37 namespace boost {
38 namespace capy {
39
40 namespace detail {
41
42 // Reuse the same return-value storage as task<T>.
43 // task_return_base is defined in task.hpp, but quitter needs its own
44 // copy to avoid a header dependency on task.hpp.
45 template<typename T>
46 struct quitter_return_base
47 {
48 std::optional<T> result_;
49
50 9x void return_value(T value)
51 {
52 9x result_ = std::move(value);
53 9x }
54
55 3x T&& result() noexcept
56 {
57 3x return std::move(*result_);
58 }
59 };
60
61 template<>
62 struct quitter_return_base<void>
63 {
64 1x void return_void()
65 {
66 1x }
67 };
68
69 } // namespace detail
70
71 /** Stop-aware lazy coroutine task satisfying @ref IoRunnable.
72
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
75 destructors run normally. The parent observes a "stopped"
76 completion via @ref promise_type::stopped.
77
78 Everything else — frame allocation, environment propagation,
79 symmetric transfer, move semantics — is identical to @ref task.
80
81 @tparam T The result type. Use `quitter<>` for `quitter<void>`.
82
83 @see task, IoRunnable, IoAwaitable
84 */
85 template<typename T = void>
86 struct [[nodiscard]] BOOST_CAPY_CORO_AWAIT_ELIDABLE
87 quitter
88 {
89 struct promise_type
90 : io_awaitable_promise_base<promise_type>
91 , detail::quitter_return_base<T>
92 {
93 private:
94 friend quitter;
95
96 enum class completion { running, value, exception, stopped };
97
98 union { std::exception_ptr ep_; };
99 completion state_;
100
101 public:
102 28x promise_type() noexcept
103 28x : state_(completion::running)
104 {
105 28x }
106
107 28x ~promise_type()
108 {
109 28x if(state_ == completion::exception ||
110 26x state_ == completion::stopped)
111 18x ep_.~exception_ptr();
112 28x }
113
114 /// Return a non-null exception_ptr when the coroutine threw
115 /// or was stopped. Stopped quitters report the sentinel
116 /// stop_requested_exception so that run_async routes to
117 /// the error handler instead of accessing a non-existent
118 /// result.
119 22x std::exception_ptr exception() const noexcept
120 {
121 22x if(state_ == completion::exception ||
122 18x state_ == completion::stopped)
123 18x return ep_;
124 4x return {};
125 }
126
127 /// True when the coroutine was stopped via the stop token.
128 10x bool stopped() const noexcept
129 {
130 10x return state_ == completion::stopped;
131 }
132
133 28x quitter get_return_object()
134 {
135 return quitter{
136 28x std::coroutine_handle<promise_type>::from_promise(*this)};
137 }
138
139 28x auto initial_suspend() noexcept
140 {
141 struct awaiter
142 {
143 promise_type* p_;
144
145 bool await_ready() const noexcept
146 {
147 return false;
148 }
149
150 void await_suspend(std::coroutine_handle<>) const noexcept
151 {
152 }
153
154 // Potentially-throwing: checks the stop token before
155 // the coroutine body executes its first statement.
156 void await_resume() const
157 {
158 set_current_frame_allocator(
159 p_->environment()->frame_allocator);
160 if(p_->environment()->stop_token.stop_requested())
161 throw detail::stop_requested_exception{};
162 }
163 };
164 28x return awaiter{this};
165 }
166
167 28x auto final_suspend() noexcept
168 {
169 struct awaiter
170 {
171 promise_type* p_;
172
173 bool await_ready() const noexcept
174 {
175 return false;
176 }
177
178 std::coroutine_handle<> await_suspend(
179 std::coroutine_handle<>) const noexcept
180 {
181 return p_->continuation();
182 }
183
184 void await_resume() const noexcept
185 {
186 }
187 };
188 28x return awaiter{this};
189 }
190
191 18x void unhandled_exception()
192 {
193 try
194 {
195 18x throw;
196 }
197 18x catch(detail::stop_requested_exception const&)
198 {
199 // Store the exception_ptr so that run_async's
200 // invoke_impl routes to the error handler
201 // instead of accessing a non-existent result.
202 16x new (&ep_) std::exception_ptr(
203 std::current_exception());
204 16x state_ = completion::stopped;
205 }
206 2x catch(...)
207 {
208 2x new (&ep_) std::exception_ptr(
209 std::current_exception());
210 2x state_ = completion::exception;
211 }
212 18x }
213
214 //------------------------------------------------------
215 // transform_awaitable — the key difference from task<T>
216 //------------------------------------------------------
217
218 template<class Awaitable>
219 struct transform_awaiter
220 {
221 std::decay_t<Awaitable> a_;
222 promise_type* p_;
223
224 18x bool await_ready() noexcept
225 {
226 18x return a_.await_ready();
227 }
228
229 // Check the stop token BEFORE the coroutine body
230 // sees the result of the I/O operation.
231 18x decltype(auto) await_resume()
232 {
233 18x set_current_frame_allocator(
234 18x p_->environment()->frame_allocator);
235 18x if(p_->environment()->stop_token.stop_requested())
236 13x throw detail::stop_requested_exception{};
237 5x return a_.await_resume();
238 }
239
240 template<class Promise>
241 16x auto await_suspend(
242 std::coroutine_handle<Promise> h) noexcept
243 {
244 using R = decltype(
245 a_.await_suspend(h, p_->environment()));
246 if constexpr (std::is_same_v<
247 R, std::coroutine_handle<>>)
248 16x return detail::symmetric_transfer(
249 32x a_.await_suspend(h, p_->environment()));
250 else
251 return a_.await_suspend(
252 h, p_->environment());
253 }
254 };
255
256 template<class Awaitable>
257 18x auto transform_awaitable(Awaitable&& a)
258 {
259 using A = std::decay_t<Awaitable>;
260 if constexpr (IoAwaitable<A>)
261 {
262 return transform_awaiter<Awaitable>{
263 33x std::forward<Awaitable>(a), this};
264 }
265 else
266 {
267 static_assert(sizeof(A) == 0,
268 "requires IoAwaitable");
269 }
270 15x }
271 };
272
273 std::coroutine_handle<promise_type> h_;
274
275 /// Destroy the quitter and its coroutine frame if owned.
276 72x ~quitter()
277 {
278 72x if(h_)
279 13x h_.destroy();
280 72x }
281
282 /// Return false; quitters are never immediately ready.
283 13x bool await_ready() const noexcept
284 {
285 13x return false;
286 }
287
288 /** Return the result, rethrow exception, or propagate stop.
289
290 When stopped, throws stop_requested_exception so that a
291 parent quitter also stops. A parent task<T> will see this
292 as an unhandled exception — by design.
293 */
294 10x auto await_resume()
295 {
296 10x if(h_.promise().stopped())
297 6x throw detail::stop_requested_exception{};
298 4x if(h_.promise().state_ == promise_type::completion::exception)
299 std::rethrow_exception(h_.promise().ep_);
300 if constexpr (! std::is_void_v<T>)
301 4x return std::move(*h_.promise().result_);
302 else
303 return;
304 }
305
306 /// Start execution with the caller's context.
307 13x std::coroutine_handle<> await_suspend(
308 std::coroutine_handle<> cont,
309 io_env const* env)
310 {
311 13x h_.promise().set_continuation(cont);
312 13x h_.promise().set_environment(env);
313 13x return h_;
314 }
315
316 /// Return the coroutine handle.
317 17x std::coroutine_handle<promise_type> handle() const noexcept
318 {
319 17x return h_;
320 }
321
322 /// Release ownership of the coroutine frame.
323 15x void release() noexcept
324 {
325 15x h_ = nullptr;
326 15x }
327
328 quitter(quitter const&) = delete;
329 quitter& operator=(quitter const&) = delete;
330
331 /// Construct by moving, transferring ownership.
332 44x quitter(quitter&& other) noexcept
333 44x : h_(std::exchange(other.h_, nullptr))
334 {
335 44x }
336
337 /// Assign by moving, transferring ownership.
338 quitter& operator=(quitter&& other) noexcept
339 {
340 if(this != &other)
341 {
342 if(h_)
343 h_.destroy();
344 h_ = std::exchange(other.h_, nullptr);
345 }
346 return *this;
347 }
348
349 private:
350 28x explicit quitter(std::coroutine_handle<promise_type> h)
351 28x : h_(h)
352 {
353 28x }
354 };
355
356 } // namespace capy
357 } // namespace boost
358
359 #endif
360