TLA Line data Source code
1 : //
2 : // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
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/corosio
8 : //
9 :
10 : #ifndef BOOST_CAPY_TASK_HPP
11 : #define BOOST_CAPY_TASK_HPP
12 :
13 : #include <boost/capy/detail/config.hpp>
14 : #include <boost/capy/concept/executor.hpp>
15 : #include <boost/capy/concept/io_awaitable.hpp>
16 : #include <boost/capy/ex/io_awaitable_promise_base.hpp>
17 : #include <boost/capy/ex/io_env.hpp>
18 : #include <boost/capy/ex/frame_allocator.hpp>
19 : #include <boost/capy/detail/await_suspend_helper.hpp>
20 :
21 : #include <exception>
22 : #include <optional>
23 : #include <type_traits>
24 : #include <utility>
25 : #include <variant>
26 :
27 : namespace boost {
28 : namespace capy {
29 :
30 : namespace detail {
31 :
32 : // Helper base for result storage and return_void/return_value
33 : template<typename T>
34 : struct task_return_base
35 : {
36 : std::optional<T> result_;
37 :
38 HIT 1263 : void return_value(T value)
39 : {
40 1263 : result_ = std::move(value);
41 1263 : }
42 :
43 144 : T&& result() noexcept
44 : {
45 144 : return std::move(*result_);
46 : }
47 : };
48 :
49 : template<>
50 : struct task_return_base<void>
51 : {
52 1869 : void return_void()
53 : {
54 1869 : }
55 : };
56 :
57 : } // namespace detail
58 :
59 : /** Lazy coroutine task satisfying @ref IoRunnable.
60 :
61 : Use `task<T>` as the return type for coroutines that perform I/O
62 : and return a value of type `T`. The coroutine body does not start
63 : executing until the task is awaited, enabling efficient composition
64 : without unnecessary eager execution.
65 :
66 : The task participates in the I/O awaitable protocol: when awaited,
67 : it receives the caller's executor and stop token, propagating them
68 : to nested `co_await` expressions. This enables cancellation and
69 : proper completion dispatch across executor boundaries.
70 :
71 : @tparam T The result type. Use `task<>` for `task<void>`.
72 :
73 : @par Thread Safety
74 : Distinct objects: Safe.
75 : Shared objects: Unsafe.
76 :
77 : @par Example
78 :
79 : @code
80 : task<int> compute_value()
81 : {
82 : auto [ec, n] = co_await stream.read_some( buf );
83 : if( ec )
84 : co_return 0;
85 : co_return process( buf, n );
86 : }
87 :
88 : task<> run_session( tcp_socket sock )
89 : {
90 : int result = co_await compute_value();
91 : // ...
92 : }
93 : @endcode
94 :
95 : @see IoRunnable, IoAwaitable, run, run_async
96 : */
97 : template<typename T = void>
98 : struct [[nodiscard]] BOOST_CAPY_CORO_AWAIT_ELIDABLE
99 : task
100 : {
101 : struct promise_type
102 : : io_awaitable_promise_base<promise_type>
103 : , detail::task_return_base<T>
104 : {
105 : private:
106 : friend task;
107 : union { std::exception_ptr ep_; };
108 : bool has_ep_;
109 :
110 : public:
111 4707 : promise_type() noexcept
112 4707 : : has_ep_(false)
113 : {
114 4707 : }
115 :
116 4707 : ~promise_type()
117 : {
118 4707 : if(has_ep_)
119 1567 : ep_.~exception_ptr();
120 4707 : }
121 :
122 3933 : std::exception_ptr exception() const noexcept
123 : {
124 3933 : if(has_ep_)
125 2058 : return ep_;
126 1875 : return {};
127 : }
128 :
129 4707 : task get_return_object()
130 : {
131 4707 : return task{std::coroutine_handle<promise_type>::from_promise(*this)};
132 : }
133 :
134 4707 : auto initial_suspend() noexcept
135 : {
136 : struct awaiter
137 : {
138 : promise_type* p_;
139 :
140 4707 : bool await_ready() const noexcept
141 : {
142 4707 : return false;
143 : }
144 :
145 4707 : void await_suspend(std::coroutine_handle<>) const noexcept
146 : {
147 4707 : }
148 :
149 4704 : void await_resume() const noexcept
150 : {
151 : // Restore TLS when body starts executing
152 4704 : set_current_frame_allocator(p_->environment()->frame_allocator);
153 4704 : }
154 : };
155 4707 : return awaiter{this};
156 : }
157 :
158 4699 : auto final_suspend() noexcept
159 : {
160 : struct awaiter
161 : {
162 : promise_type* p_;
163 :
164 4699 : bool await_ready() const noexcept
165 : {
166 4699 : return false;
167 : }
168 :
169 4699 : std::coroutine_handle<> await_suspend(std::coroutine_handle<>) const noexcept
170 : {
171 4699 : return p_->continuation();
172 : }
173 :
174 MIS 0 : void await_resume() const noexcept
175 : {
176 0 : }
177 : };
178 HIT 4699 : return awaiter{this};
179 : }
180 :
181 1567 : void unhandled_exception()
182 : {
183 1567 : new (&ep_) std::exception_ptr(std::current_exception());
184 1567 : has_ep_ = true;
185 1567 : }
186 :
187 : template<class Awaitable>
188 : struct transform_awaiter
189 : {
190 : std::decay_t<Awaitable> a_;
191 : promise_type* p_;
192 :
193 8607 : bool await_ready() noexcept
194 : {
195 8607 : return a_.await_ready();
196 : }
197 :
198 8602 : decltype(auto) await_resume()
199 : {
200 : // Restore TLS before body resumes
201 8602 : set_current_frame_allocator(p_->environment()->frame_allocator);
202 8602 : return a_.await_resume();
203 : }
204 :
205 : template<class Promise>
206 2227 : auto await_suspend(std::coroutine_handle<Promise> h) noexcept
207 : {
208 : using R = decltype(a_.await_suspend(h, p_->environment()));
209 : if constexpr (std::is_same_v<R, std::coroutine_handle<>>)
210 2227 : return detail::symmetric_transfer(a_.await_suspend(h, p_->environment()));
211 : else
212 MIS 0 : return a_.await_suspend(h, p_->environment());
213 : }
214 : };
215 :
216 : template<class Awaitable>
217 HIT 8607 : auto transform_awaitable(Awaitable&& a)
218 : {
219 : using A = std::decay_t<Awaitable>;
220 : if constexpr (IoAwaitable<A>)
221 : {
222 : return transform_awaiter<Awaitable>{
223 10459 : std::forward<Awaitable>(a), this};
224 : }
225 : else
226 : {
227 : static_assert(sizeof(A) == 0, "requires IoAwaitable");
228 : }
229 1852 : }
230 : };
231 :
232 : std::coroutine_handle<promise_type> h_;
233 :
234 : /// Destroy the task and its coroutine frame if owned.
235 10366 : ~task()
236 : {
237 10366 : if(h_)
238 1741 : h_.destroy();
239 10366 : }
240 :
241 : /// Return false; tasks are never immediately ready.
242 1613 : bool await_ready() const noexcept
243 : {
244 1613 : return false;
245 : }
246 :
247 : /// Return the result or rethrow any stored exception.
248 1738 : auto await_resume()
249 : {
250 1738 : if(h_.promise().has_ep_)
251 537 : std::rethrow_exception(h_.promise().ep_);
252 : if constexpr (! std::is_void_v<T>)
253 1117 : return std::move(*h_.promise().result_);
254 : else
255 84 : return;
256 : }
257 :
258 : /// Start execution with the caller's context.
259 1725 : std::coroutine_handle<> await_suspend(std::coroutine_handle<> cont, io_env const* env)
260 : {
261 1725 : h_.promise().set_continuation(cont);
262 1725 : h_.promise().set_environment(env);
263 1725 : return h_;
264 : }
265 :
266 : /// Return the coroutine handle.
267 2982 : std::coroutine_handle<promise_type> handle() const noexcept
268 : {
269 2982 : return h_;
270 : }
271 :
272 : /** Release ownership of the coroutine frame.
273 :
274 : After calling this, destroying the task does not destroy the
275 : coroutine frame. The caller becomes responsible for the frame's
276 : lifetime.
277 :
278 : @par Postconditions
279 : `handle()` returns the original handle, but the task no longer
280 : owns it.
281 : */
282 2966 : void release() noexcept
283 : {
284 2966 : h_ = nullptr;
285 2966 : }
286 :
287 : task(task const&) = delete;
288 : task& operator=(task const&) = delete;
289 :
290 : /// Move construct, transferring ownership.
291 5659 : task(task&& other) noexcept
292 5659 : : h_(std::exchange(other.h_, nullptr))
293 : {
294 5659 : }
295 :
296 : /// Move assign, transferring ownership.
297 : task& operator=(task&& other) noexcept
298 : {
299 : if(this != &other)
300 : {
301 : if(h_)
302 : h_.destroy();
303 : h_ = std::exchange(other.h_, nullptr);
304 : }
305 : return *this;
306 : }
307 :
308 : private:
309 4707 : explicit task(std::coroutine_handle<promise_type> h)
310 4707 : : h_(h)
311 : {
312 4707 : }
313 : };
314 :
315 : } // namespace capy
316 : } // namespace boost
317 :
318 : #endif
|