include/boost/capy/quitter.hpp

100.0% Lines (81/81) 97.2% List of functions (104/107)
quitter.hpp
f(x) Functions (107)
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 9x 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 4x 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 2x 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 14x 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 12x 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 14x 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 12x 100.0% 100.0% boost::capy::quitter<int>::promise_type::exception() const :119 10x 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 5x 100.0% 100.0% boost::capy::quitter<void>::promise_type::stopped() const :128 1x 100.0% 100.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 14x 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 12x 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 14x 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 12x 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 14x 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 12x 100.0% 100.0% boost::capy::quitter<boost::capy::io_result<unsigned long> >::promise_type::unhandled_exception() :189 5x 66.7% 73.0% boost::capy::quitter<int>::promise_type::unhandled_exception() :189 5x 100.0% 100.0% boost::capy::quitter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::promise_type::unhandled_exception() :189 0 0.0% 0.0% boost::capy::quitter<void>::promise_type::unhandled_exception() :189 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() :222 4x 100.0% 100.0% boost::capy::quitter<int>::promise_type::transform_awaiter<boost::capy::quitter<int> >::await_ready() :222 3x 100.0% 100.0% boost::capy::quitter<int>::promise_type::transform_awaiter<boost::capy::quitter<void> >::await_ready() :222 1x 100.0% 100.0% boost::capy::quitter<int>::promise_type::transform_awaiter<boost::capy::quitter_test::bool_resume_awaitable>::await_ready() :222 1x 100.0% 100.0% boost::capy::quitter<int>::promise_type::transform_awaiter<boost::capy::stop_only_awaitable>::await_ready() :222 1x 100.0% 100.0% boost::capy::quitter<int>::promise_type::transform_awaiter<boost::capy::yield_awaitable>::await_ready() :222 1x 100.0% 100.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::delay_awaitable>::await_ready() :222 1x 100.0% 100.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::quitter<void> >::await_ready() :222 3x 100.0% 100.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::stop_only_awaitable>::await_ready() :222 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() :222 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() :222 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() :229 4x 83.3% 89.0% boost::capy::quitter<int>::promise_type::transform_awaiter<boost::capy::quitter<int> >::await_resume() :229 3x 83.3% 80.0% boost::capy::quitter<int>::promise_type::transform_awaiter<boost::capy::quitter<void> >::await_resume() :229 1x 83.3% 78.0% boost::capy::quitter<int>::promise_type::transform_awaiter<boost::capy::quitter_test::bool_resume_awaitable>::await_resume() :229 1x 83.3% 78.0% boost::capy::quitter<int>::promise_type::transform_awaiter<boost::capy::stop_only_awaitable>::await_resume() :229 1x 83.3% 89.0% boost::capy::quitter<int>::promise_type::transform_awaiter<boost::capy::yield_awaitable>::await_resume() :229 1x 83.3% 78.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::delay_awaitable>::await_resume() :229 1x 83.3% 89.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::quitter<void> >::await_resume() :229 3x 83.3% 89.0% boost::capy::quitter<void>::promise_type::transform_awaiter<boost::capy::stop_only_awaitable>::await_resume() :229 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() :229 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() :229 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>) :239 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>) :239 3x 100.0% 100.0% auto boost::capy::quitter<int>::promise_type::transform_awaiter<boost::capy::quitter<void> >::await_suspend<boost::capy::quitter<int>::promise_type>(std::__n4861::coroutine_handle<boost::capy::quitter<int>::promise_type>) :239 1x 100.0% 100.0% auto boost::capy::quitter<int>::promise_type::transform_awaiter<boost::capy::quitter_test::bool_resume_awaitable>::await_suspend<boost::capy::quitter<int>::promise_type>(std::__n4861::coroutine_handle<boost::capy::quitter<int>::promise_type>) :239 1x 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>) :239 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>) :239 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>) :239 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>) :239 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>) :239 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>) :239 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>) :239 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&&) :255 4x 100.0% 100.0% auto boost::capy::quitter<int>::promise_type::transform_awaitable<boost::capy::quitter<int> >(boost::capy::quitter<int>&&) :255 3x 100.0% 100.0% auto boost::capy::quitter<int>::promise_type::transform_awaitable<boost::capy::quitter<void> >(boost::capy::quitter<void>&&) :255 1x 100.0% 100.0% auto boost::capy::quitter<int>::promise_type::transform_awaitable<boost::capy::quitter_test::bool_resume_awaitable>(boost::capy::quitter_test::bool_resume_awaitable&&) :255 1x 100.0% 100.0% auto boost::capy::quitter<int>::promise_type::transform_awaitable<boost::capy::stop_only_awaitable>(boost::capy::stop_only_awaitable&&) :255 1x 100.0% 100.0% auto boost::capy::quitter<int>::promise_type::transform_awaitable<boost::capy::yield_awaitable>(boost::capy::yield_awaitable&&) :255 1x 100.0% 100.0% auto boost::capy::quitter<void>::promise_type::transform_awaitable<boost::capy::delay_awaitable>(boost::capy::delay_awaitable&&) :255 1x 100.0% 100.0% auto boost::capy::quitter<void>::promise_type::transform_awaitable<boost::capy::quitter<void> >(boost::capy::quitter<void>&&) :255 3x 100.0% 100.0% auto boost::capy::quitter<void>::promise_type::transform_awaitable<boost::capy::stop_only_awaitable>(boost::capy::stop_only_awaitable&&) :255 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&&) :255 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&&) :255 1x 100.0% 100.0% boost::capy::quitter<boost::capy::io_result<unsigned long> >::~quitter() :274 36x 100.0% 100.0% boost::capy::quitter<int>::~quitter() :274 26x 100.0% 100.0% boost::capy::quitter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::~quitter() :274 2x 75.0% 75.0% boost::capy::quitter<void>::~quitter() :274 18x 100.0% 100.0% boost::capy::quitter<boost::capy::io_result<unsigned long> >::await_ready() const :281 6x 100.0% 100.0% boost::capy::quitter<int>::await_ready() const :281 5x 100.0% 100.0% boost::capy::quitter<void>::await_ready() const :281 4x 100.0% 100.0% boost::capy::quitter<boost::capy::io_result<unsigned long> >::await_resume() :292 6x 83.3% 72.0% boost::capy::quitter<int>::await_resume() :292 5x 100.0% 100.0% boost::capy::quitter<void>::await_resume() :292 1x 66.7% 53.0% boost::capy::quitter<boost::capy::io_result<unsigned long> >::await_suspend(std::__n4861::coroutine_handle<void>, boost::capy::io_env const*) :305 6x 100.0% 100.0% boost::capy::quitter<int>::await_suspend(std::__n4861::coroutine_handle<void>, boost::capy::io_env const*) :305 5x 100.0% 100.0% boost::capy::quitter<void>::await_suspend(std::__n4861::coroutine_handle<void>, boost::capy::io_env const*) :305 4x 100.0% 100.0% boost::capy::quitter<int>::handle() const :325 11x 100.0% 100.0% boost::capy::quitter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::handle() const :325 1x 100.0% 100.0% boost::capy::quitter<void>::handle() const :325 8x 100.0% 100.0% boost::capy::quitter<int>::release() :337 9x 100.0% 100.0% boost::capy::quitter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::release() :337 1x 100.0% 100.0% boost::capy::quitter<void>::release() :337 8x 100.0% 100.0% boost::capy::quitter<boost::capy::io_result<unsigned long> >::quitter(boost::capy::quitter<boost::capy::io_result<unsigned long> >&&) :346 30x 100.0% 100.0% boost::capy::quitter<int>::quitter(boost::capy::quitter<int>&&) :346 12x 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> > >&&) :346 1x 100.0% 100.0% boost::capy::quitter<void>::quitter(boost::capy::quitter<void>&&) :346 6x 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>) :364 6x 100.0% 100.0% boost::capy::quitter<int>::quitter(std::__n4861::coroutine_handle<boost::capy::quitter<int>::promise_type>) :364 14x 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>) :364 1x 100.0% 100.0% boost::capy::quitter<void>::quitter(std::__n4861::coroutine_handle<boost::capy::quitter<void>::promise_type>) :364 12x 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 11x void return_value(T value)
51 {
52 11x result_ = std::move(value);
53 11x }
54
55 5x T&& result() noexcept
56 {
57 5x return std::move(*result_);
58 }
59 };
60
61 template<>
62 struct quitter_return_base<void>
63 {
64 2x void return_void()
65 {
66 2x }
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 33x promise_type() noexcept
103 33x : state_(completion::running)
104 {
105 33x }
106
107 33x ~promise_type()
108 {
109 33x if(state_ == completion::exception ||
110 29x state_ == completion::stopped)
111 20x ep_.~exception_ptr();
112 33x }
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 26x std::exception_ptr exception() const noexcept
120 {
121 26x if(state_ == completion::exception ||
122 20x state_ == completion::stopped)
123 20x return ep_;
124 6x return {};
125 }
126
127 /// True when the coroutine was stopped via the stop token.
128 12x bool stopped() const noexcept
129 {
130 12x return state_ == completion::stopped;
131 }
132
133 33x quitter get_return_object()
134 {
135 return quitter{
136 33x std::coroutine_handle<promise_type>::from_promise(*this)};
137 }
138
139 33x 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 33x return awaiter{this};
165 }
166
167 33x 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 {} // LCOV_EXCL_LINE final_suspend awaiter, never resumed
185 };
186 33x return awaiter{this};
187 }
188
189 20x void unhandled_exception()
190 {
191 try
192 {
193 20x throw;
194 }
195 20x catch(detail::stop_requested_exception const&)
196 {
197 // Store the exception_ptr so that run_async's
198 // invoke_impl routes to the error handler
199 // instead of accessing a non-existent result.
200 16x new (&ep_) std::exception_ptr(
201 std::current_exception());
202 16x state_ = completion::stopped;
203 }
204 4x catch(...)
205 {
206 4x new (&ep_) std::exception_ptr(
207 std::current_exception());
208 4x state_ = completion::exception;
209 }
210 20x }
211
212 //------------------------------------------------------
213 // transform_awaitable — the key difference from task<T>
214 //------------------------------------------------------
215
216 template<class Awaitable>
217 struct transform_awaiter
218 {
219 std::decay_t<Awaitable> a_;
220 promise_type* p_;
221
222 21x bool await_ready() noexcept
223 {
224 21x return a_.await_ready();
225 }
226
227 // Check the stop token BEFORE the coroutine body
228 // sees the result of the I/O operation.
229 21x decltype(auto) await_resume()
230 {
231 21x set_current_frame_allocator(
232 21x p_->environment()->frame_allocator);
233 21x if(p_->environment()->stop_token.stop_requested())
234 13x throw detail::stop_requested_exception{};
235 8x return a_.await_resume();
236 }
237
238 template<class Promise>
239 19x auto await_suspend(
240 std::coroutine_handle<Promise> h) noexcept
241 {
242 using R = decltype(
243 a_.await_suspend(h, p_->environment()));
244 if constexpr (std::is_same_v<
245 R, std::coroutine_handle<>>)
246 18x return detail::symmetric_transfer(
247 36x a_.await_suspend(h, p_->environment()));
248 else
249 1x return a_.await_suspend(
250 2x h, p_->environment());
251 }
252 };
253
254 template<class Awaitable>
255 21x auto transform_awaitable(Awaitable&& a)
256 {
257 using A = std::decay_t<Awaitable>;
258 if constexpr (IoAwaitable<A>)
259 {
260 return transform_awaiter<Awaitable>{
261 38x std::forward<Awaitable>(a), this};
262 }
263 else
264 {
265 static_assert(sizeof(A) == 0,
266 "requires IoAwaitable");
267 }
268 17x }
269 };
270
271 std::coroutine_handle<promise_type> h_;
272
273 /// Destroy the quitter and its coroutine frame if owned.
274 82x ~quitter()
275 {
276 82x if(h_)
277 15x h_.destroy();
278 82x }
279
280 /// Return false; quitters are never immediately ready.
281 15x bool await_ready() const noexcept
282 {
283 15x return false;
284 }
285
286 /** Return the result, rethrow exception, or propagate stop.
287
288 When stopped, throws stop_requested_exception so that a
289 parent quitter also stops. A parent task<T> will see this
290 as an unhandled exception — by design.
291 */
292 12x auto await_resume()
293 {
294 12x if(h_.promise().stopped())
295 6x throw detail::stop_requested_exception{};
296 6x if(h_.promise().state_ == promise_type::completion::exception)
297 1x std::rethrow_exception(h_.promise().ep_);
298 if constexpr (! std::is_void_v<T>)
299 4x return std::move(*h_.promise().result_);
300 else
301 1x return;
302 }
303
304 /// Start execution with the caller's context.
305 15x std::coroutine_handle<> await_suspend(
306 std::coroutine_handle<> cont,
307 io_env const* env)
308 {
309 15x h_.promise().set_continuation(cont);
310 15x h_.promise().set_environment(env);
311 15x return h_;
312 }
313
314 /** Return the coroutine handle.
315
316 @note Do not call `destroy()` on the returned handle while
317 the quitter is being awaited. The quitter's lifetime is
318 normally managed by `run_async`, `run`, or the awaiting
319 parent; manually destroying a suspended quitter that another
320 coroutine is awaiting produces undefined behavior. For
321 cooperative cancellation, use `std::stop_token`.
322
323 @return The coroutine handle.
324 */
325 20x std::coroutine_handle<promise_type> handle() const noexcept
326 {
327 20x return h_;
328 }
329
330 /** Release ownership of the coroutine frame.
331
332 @note If the caller intends to call `destroy()` on the
333 released handle, it must do so only when the quitter has not
334 started or has fully completed. Destroying a suspended
335 quitter that is being awaited produces undefined behavior.
336 */
337 18x void release() noexcept
338 {
339 18x h_ = nullptr;
340 18x }
341
342 quitter(quitter const&) = delete;
343 quitter& operator=(quitter const&) = delete;
344
345 /// Construct by moving, transferring ownership.
346 49x quitter(quitter&& other) noexcept
347 49x : h_(std::exchange(other.h_, nullptr))
348 {
349 49x }
350
351 /// Assign by moving, transferring ownership.
352 quitter& operator=(quitter&& other) noexcept
353 {
354 if(this != &other)
355 {
356 if(h_)
357 h_.destroy();
358 h_ = std::exchange(other.h_, nullptr);
359 }
360 return *this;
361 }
362
363 private:
364 33x explicit quitter(std::coroutine_handle<promise_type> h)
365 33x : h_(h)
366 {
367 33x }
368 };
369
370 } // namespace capy
371 } // namespace boost
372
373 #endif
374