0.00% Lines (0/0) 0.00% Functions (0/0)
TLA Baseline Branch
Line Hits Code Line Hits Code
1 - //  
2 - // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)  
3 - // Copyright (c) 2026 Michael Vandeberg  
4 - //  
5 - // Distributed under the Boost Software License, Version 1.0. (See accompanying  
6 - // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)  
7 - //  
8 - // Official repository: https://github.com/cppalliance/capy  
9 - //  
10 -  
11 - #ifndef BOOST_CAPY_EX_IO_AWAITABLE_PROMISE_BASE_HPP  
12 - #define BOOST_CAPY_EX_IO_AWAITABLE_PROMISE_BASE_HPP  
13 -  
14 - #include <boost/capy/detail/config.hpp>  
15 - #include <boost/capy/ex/abandon.hpp>  
16 - #include <boost/capy/ex/frame_alloc_mixin.hpp>  
17 - #include <boost/capy/ex/frame_allocator.hpp>  
18 - #include <boost/capy/ex/io_env.hpp>  
19 - #include <boost/capy/ex/this_coro.hpp>  
20 -  
21 - #include <coroutine>  
22 - #include <memory_resource>  
23 - #include <stop_token>  
24 - #include <type_traits>  
25 -  
26 - namespace boost {  
27 - namespace capy {  
28 -  
29 - /** CRTP mixin that adds I/O awaitable support to a promise type.  
30 -  
31 - Inherit from this class to enable these capabilities in your coroutine:  
32 -  
33 - 1. **Frame allocation** — The mixin provides `operator new/delete` that  
34 - use the thread-local frame allocator set by `run_async`.  
35 -  
36 - 2. **Environment storage** — The mixin stores a pointer to the `io_env`  
37 - containing the executor, stop token, and allocator for this coroutine.  
38 -  
39 - 3. **Environment access** — Coroutine code can retrieve the environment  
40 - via `co_await this_coro::environment`, or individual fields via  
41 - `co_await this_coro::executor`, `co_await this_coro::stop_token`,  
42 - and `co_await this_coro::frame_allocator`.  
43 -  
44 - @tparam Derived The derived promise type (CRTP pattern).  
45 -  
46 - @par Basic Usage  
47 -  
48 - For coroutines that need to access their execution environment:  
49 -  
50 - @par !example example_1  
51 -  
52 -  
53 - @par Custom Awaitable Transformation  
54 -  
55 - If your promise needs to transform awaitables (e.g., for affinity or  
56 - logging), override `transform_awaitable` instead of `await_transform`:  
57 -  
58 - @par !example example_2  
59 -  
60 -  
61 - The mixin's `await_transform` intercepts @ref this_coro::environment_tag  
62 - and the fine-grained tag types (@ref this_coro::executor_tag,  
63 - @ref this_coro::stop_token_tag, @ref this_coro::frame_allocator_tag),  
64 - then delegates all other awaitables to your `transform_awaitable`.  
65 -  
66 - @par Making Your Coroutine an IoAwaitable  
67 -  
68 - The mixin handles the "inside the coroutine" part—accessing the  
69 - environment. To receive the environment when your coroutine is awaited  
70 - (satisfying @ref IoAwaitable), implement the `await_suspend` overload  
71 - on your coroutine return type:  
72 -  
73 - @par !example example_3  
74 -  
75 -  
76 - @par Ownership  
77 - A coroutine's frame is owned by the object that awaits it, such as  
78 - the parent's `task`, so a chain is destroyed from its top down, each  
79 - frame freeing the one it awaits. The suspended coroutine at the  
80 - bottom is held by whoever will resume it: an operation, queue or  
81 - primitive. A holder that will never resume it calls @ref abandon,  
82 - which unwinds the chain's segment from its owner (see  
83 - @ref chain_owner) rather than destroying the bottom frame.  
84 - Destroying a frame that is still awaited, other than in that  
85 - unwind, calls `std::terminate`.  
86 -  
87 - @par Thread Safety  
88 - The environment is stored during `await_suspend` and read during  
89 - `co_await this_coro::environment`. These occur on the same logical  
90 - thread of execution, so no synchronization is required.  
91 -  
92 - @note A chain waiting on a primitive stored in its own frames owns  
93 - itself through that primitive; it is freed only by a stop request or  
94 - by the primitive being signalled. A mutex must outlive every  
95 - coroutine holding or handed its lock. A cross-thread `wake()` racing  
96 - the teardown of an armed waker's frame on its executor thread is a  
97 - precondition violation.  
98 -  
99 - @see this_coro::environment, this_coro::executor,  
100 - this_coro::stop_token, this_coro::frame_allocator  
101 - @see io_env  
102 - @see IoAwaitable  
103 - */  
104 - template<typename Derived>  
105 - class io_awaitable_promise_base  
106 - : public frame_alloc_mixin  
107 - {  
108 - io_env const* env_ = nullptr;  
109 - mutable std::coroutine_handle<> cont_{std::noop_coroutine()};  
110 -  
111 - public:  
112 - //----------------------------------------------------------  
113 - // Continuation support  
114 - //----------------------------------------------------------  
115 -  
116 - /** Store the continuation to resume on completion.  
117 -  
118 - Call this from your coroutine type's `await_suspend` overload  
119 - to set up the completion path. The `final_suspend` awaiter  
120 - returns this handle via unconditional symmetric transfer.  
121 -  
122 - @param cont The continuation to resume on completion.  
123 - */  
DCB 124 - 107616 void set_continuation(std::coroutine_handle<> cont) noexcept  
125 - {  
DCB 126 - 107616 cont_ = cont;  
DCB 127 - 107616 }  
128 -  
129 - /** Return and consume the stored continuation handle.  
130 -  
131 - Resets the stored handle to `noop_coroutine()` so the  
132 - destructor does not double-destroy it.  
133 -  
134 - @return The continuation for symmetric transfer.  
135 - */  
DCB 136 - 5064 std::coroutine_handle<> continuation() const noexcept  
137 - {  
DCB 138 - 5064 return std::exchange(cont_, std::noop_coroutine());  
139 - }  
140 -  
141 - //----------------------------------------------------------  
142 - // Environment support  
143 - //----------------------------------------------------------  
144 -  
145 - /** Store a pointer to the execution environment.  
146 -  
147 - Call this from your coroutine type's `await_suspend`  
148 - overload to make the environment available via  
149 - `co_await this_coro::environment`. The pointed-to  
150 - `io_env` must outlive this coroutine.  
151 -  
152 - @param env The environment to store.  
153 - */  
DCB 154 - 107702 void set_environment(io_env const* env) noexcept  
155 - {  
DCB 156 - 107702 env_ = env;  
DCB 157 - 107702 }  
158 -  
159 - /** Return the stored execution environment.  
160 -  
161 - @return The environment.  
162 - */  
DCB 163 - 221261 io_env const* environment() const noexcept  
164 - {  
DCB 165 - 221261 BOOST_CAPY_ASSERT(env_);  
DCB 166 - 221261 return env_;  
167 - }  
168 -  
169 - /** Transform an awaitable before co_await.  
170 -  
171 - Override this in your derived promise type to customize how  
172 - awaitables are transformed. The default implementation passes  
173 - the awaitable through unchanged.  
174 -  
175 - @param a The awaitable expression from `co_await a`.  
176 -  
177 - @return The transformed awaitable.  
178 - */  
179 - template<typename A>  
DCB 180 - 1 decltype(auto) transform_awaitable(A&& a)  
181 - {  
DCB 182 - 1 return std::forward<A>(a);  
183 - }  
184 -  
185 - /** Intercept co_await expressions.  
186 -  
187 - This function handles @ref this_coro::environment_tag and  
188 - the fine-grained tags (@ref this_coro::executor_tag,  
189 - @ref this_coro::stop_token_tag, @ref this_coro::frame_allocator_tag)  
190 - specially, returning an awaiter that yields the stored value.  
191 - All other awaitables are delegated to @ref transform_awaitable.  
192 -  
193 - @param t The awaited expression.  
194 -  
195 - @return An awaiter for the expression.  
196 - */  
197 - template<typename T>  
DCB 198 - 107798 auto await_transform(T&& t)  
199 - {  
200 - using Tag = std::decay_t<T>;  
201 -  
202 - if constexpr (std::is_same_v<Tag, this_coro::environment_tag>)  
203 - {  
DCB 204 - 24 BOOST_CAPY_ASSERT(env_);  
205 - struct awaiter  
206 - {  
207 - io_env const* env_;  
DCB 208 - 22 bool await_ready() const noexcept { return true; }  
DCB 209 - 2 void await_suspend(std::coroutine_handle<>) const noexcept { }  
DCB 210 - 21 io_env const* await_resume() const noexcept { return env_; }  
211 - };  
DCB 212 - 24 return awaiter{env_};  
213 - }  
214 - else if constexpr (std::is_same_v<Tag, this_coro::executor_tag>)  
215 - {  
DCB 216 - 4 BOOST_CAPY_ASSERT(env_);  
217 - struct awaiter  
218 - {  
219 - executor_ref executor_;  
DCB 220 - 3 bool await_ready() const noexcept { return true; }  
221 - void await_suspend(std::coroutine_handle<>) const noexcept { } // LCOV_EXCL_LINE await_ready() always true, never suspends  
DCB 222 - 3 executor_ref await_resume() const noexcept { return executor_; }  
223 - };  
DCB 224 - 4 return awaiter{env_->executor};  
225 - }  
226 - else if constexpr (std::is_same_v<Tag, this_coro::stop_token_tag>)  
227 - {  
DCB 228 - 24 BOOST_CAPY_ASSERT(env_);  
229 - struct awaiter  
230 - {  
231 - std::stop_token token_;  
DCB 232 - 23 bool await_ready() const noexcept { return true; }  
233 - void await_suspend(std::coroutine_handle<>) const noexcept { } // LCOV_EXCL_LINE await_ready() always true, never suspends  
DCB 234 - 23 std::stop_token await_resume() const noexcept { return token_; }  
235 - };  
DCB 236 - 24 return awaiter{env_->stop_token};  
237 - }  
238 - else if constexpr (std::is_same_v<Tag, this_coro::frame_allocator_tag>)  
239 - {  
DCB 240 - 8 BOOST_CAPY_ASSERT(env_);  
241 - struct awaiter  
242 - {  
243 - std::pmr::memory_resource* frame_allocator_;  
DCB 244 - 6 bool await_ready() const noexcept { return true; }  
245 - void await_suspend(std::coroutine_handle<>) const noexcept { } // LCOV_EXCL_LINE await_ready() always true, never suspends  
DCB 246 - 7 std::pmr::memory_resource* await_resume() const noexcept { return frame_allocator_; }  
247 - };  
DCB 248 - 8 return awaiter{env_->frame_allocator};  
249 - }  
250 - else  
251 - {  
DCB 252 - 2506 return static_cast<Derived*>(this)->transform_awaitable(  
DCB 253 - 107738 std::forward<T>(t));  
254 - }  
255 - }  
256 - };  
257 -  
258 - } // namespace capy  
259 - } // namespace boost  
260 -  
261 - #endif