100.00% Lines (283/283)
100.00% Functions (64/64)
| TLA | Baseline | Branch | ||||||
|---|---|---|---|---|---|---|---|---|
| Line | Hits | Code | Line | Hits | Code | |||
| 1 | // | 1 | // | |||||
| 2 | // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com) | 2 | // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com) | |||||
| 3 | // Copyright (c) 2026 Michael Vandeberg | 3 | // Copyright (c) 2026 Michael Vandeberg | |||||
| 4 | // | 4 | // | |||||
| 5 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | 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) | 6 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |||||
| 7 | // | 7 | // | |||||
| 8 | // Official repository: https://github.com/cppalliance/capy | 8 | // Official repository: https://github.com/cppalliance/capy | |||||
| 9 | // | 9 | // | |||||
| 10 | 10 | |||||||
| 11 | #ifndef BOOST_CAPY_RUN_HPP | 11 | #ifndef BOOST_CAPY_RUN_HPP | |||||
| 12 | #define BOOST_CAPY_RUN_HPP | 12 | #define BOOST_CAPY_RUN_HPP | |||||
| 13 | 13 | |||||||
| 14 | #include <boost/capy/detail/config.hpp> | 14 | #include <boost/capy/detail/config.hpp> | |||||
| 15 | #include <boost/capy/detail/await_suspend_helper.hpp> | 15 | #include <boost/capy/detail/await_suspend_helper.hpp> | |||||
| 16 | #include <boost/capy/detail/run.hpp> | 16 | #include <boost/capy/detail/run.hpp> | |||||
| 17 | #include <boost/capy/concept/executor.hpp> | 17 | #include <boost/capy/concept/executor.hpp> | |||||
| 18 | #include <boost/capy/concept/context_runnable.hpp> | 18 | #include <boost/capy/concept/context_runnable.hpp> | |||||
| 19 | #include <boost/capy/ex/abandon.hpp> | 19 | #include <boost/capy/ex/abandon.hpp> | |||||
| 20 | #include <boost/capy/ex/executor_ref.hpp> | 20 | #include <boost/capy/ex/executor_ref.hpp> | |||||
| 21 | #include <coroutine> | 21 | #include <coroutine> | |||||
| 22 | #include <boost/capy/ex/frame_alloc_mixin.hpp> | 22 | #include <boost/capy/ex/frame_alloc_mixin.hpp> | |||||
| 23 | #include <boost/capy/ex/frame_allocator.hpp> | 23 | #include <boost/capy/ex/frame_allocator.hpp> | |||||
| 24 | #include <boost/capy/ex/await_context.hpp> | 24 | #include <boost/capy/ex/await_context.hpp> | |||||
| 25 | 25 | |||||||
| 26 | #include <exception> | 26 | #include <exception> | |||||
| 27 | #include <memory_resource> | 27 | #include <memory_resource> | |||||
| 28 | #include <stop_token> | 28 | #include <stop_token> | |||||
| 29 | #include <type_traits> | 29 | #include <type_traits> | |||||
| 30 | #include <utility> | 30 | #include <utility> | |||||
| 31 | #include <variant> | 31 | #include <variant> | |||||
| 32 | 32 | |||||||
| 33 | /* | 33 | /* | |||||
| 34 | Allocator Lifetime Strategy | 34 | Allocator Lifetime Strategy | |||||
| 35 | =========================== | 35 | =========================== | |||||
| 36 | 36 | |||||||
| 37 | When using run() with a custom allocator: | 37 | When using run() with a custom allocator: | |||||
| 38 | 38 | |||||||
| 39 | co_await run(ex, alloc)(my_task()); | 39 | co_await run(ex, alloc)(my_task()); | |||||
| 40 | 40 | |||||||
| 41 | The wrapper run(ex, alloc) returns is a temporary holding the | 41 | The wrapper run(ex, alloc) returns is a temporary holding the | |||||
| 42 | frame_memory_resource my_task()'s frame is allocated from, so it must | 42 | frame_memory_resource my_task()'s frame is allocated from, so it must | |||||
| 43 | live until that frame is freed: the awaitable is awaited in the | 43 | live until that frame is freed: the awaitable is awaited in the | |||||
| 44 | full-expression that made the wrapper, and the temporary lives until | 44 | full-expression that made the wrapper, and the temporary lives until | |||||
| 45 | the co_await completes. Keeping the awaitable and awaiting it later | 45 | the co_await completes. Keeping the awaitable and awaiting it later | |||||
| 46 | frees the task's frame through a dead resource. | 46 | frees the task's frame through a dead resource. | |||||
| 47 | 47 | |||||||
| 48 | The awaitable also holds a copy of the allocator. In await_suspend it | 48 | The awaitable also holds a copy of the allocator. In await_suspend it | |||||
| 49 | overwrites the promise's saved frame_allocator pointer with the | 49 | overwrites the promise's saved frame_allocator pointer with the | |||||
| 50 | awaitable's resource, so tasks the inner task creates allocate from | 50 | awaitable's resource, so tasks the inner task creates allocate from | |||||
| 51 | it through TLS. | 51 | it through TLS. | |||||
| 52 | */ | 52 | */ | |||||
| 53 | 53 | |||||||
| 54 | namespace boost::capy::detail { | 54 | namespace boost::capy::detail { | |||||
| 55 | 55 | |||||||
| 56 | /** Minimal coroutine that dispatches through the caller's executor. | 56 | /** Minimal coroutine that dispatches through the caller's executor. | |||||
| 57 | 57 | |||||||
| 58 | Sits between the inner task and the parent when executors | 58 | Sits between the inner task and the parent when executors | |||||
| 59 | diverge. The inner task's `final_suspend` resumes this | 59 | diverge. The inner task's `final_suspend` resumes this | |||||
| 60 | trampoline via symmetric transfer. The trampoline's own | 60 | trampoline via symmetric transfer. The trampoline's own | |||||
| 61 | `final_suspend` posts the parent through the caller's executor to | 61 | `final_suspend` posts the parent through the caller's executor to | |||||
| 62 | restore the correct execution context. | 62 | restore the correct execution context. | |||||
| 63 | 63 | |||||||
| 64 | It also owns the inner task's segment. Abandoning that segment | 64 | It also owns the inner task's segment. Abandoning that segment | |||||
| 65 | resumes the trampoline on the inner executor, or has a closed | 65 | resumes the trampoline on the inner executor, or has a closed | |||||
| 66 | executor destroy it: either way it destroys the inner task, which | 66 | executor destroy it: either way it destroys the inner task, which | |||||
| 67 | unwinds the segment, then abandons the caller's segment, which | 67 | unwinds the segment, then abandons the caller's segment, which | |||||
| 68 | unwinds the parent on its own executor. The caller's execution | 68 | unwinds the parent on its own executor. The caller's execution | |||||
| 69 | context must outlive that. | 69 | context must outlive that. | |||||
| 70 | */ | 70 | */ | |||||
| 71 | struct boundary_trampoline | 71 | struct boundary_trampoline | |||||
| 72 | { | 72 | { | |||||
| 73 | struct promise_type | 73 | struct promise_type | |||||
| 74 | : detail::process_frame_mixin | 74 | : detail::process_frame_mixin | |||||
| 75 | { | 75 | { | |||||
| 76 | executor_ref caller_ex_; | 76 | executor_ref caller_ex_; | |||||
| 77 | continuation parent_; | 77 | continuation parent_; | |||||
| 78 | chain_owner segment_; | 78 | chain_owner segment_; | |||||
| 79 | + | // The inner task reports a stop-caused abandon (a quitter). For | ||||||
| 80 | + | // any other task such an abandon is torn down like any other. | ||||||
| 81 | + | bool stoppable_ = false; | ||||||
| 79 | // The awaitable's trampoline object until the inner task | 82 | // The awaitable's trampoline object until the inner task | |||||
| 80 | // completes or is unwound; null afterward. | 83 | // completes or is unwound; null afterward. | |||||
| 81 | boundary_trampoline* owner_ = nullptr; | 84 | boundary_trampoline* owner_ = nullptr; | |||||
| 82 | // Destroys the inner task the awaitable owns. | 85 | // Destroys the inner task the awaitable owns. | |||||
| 83 | void (*destroy_inner_)(void*) noexcept = nullptr; | 86 | void (*destroy_inner_)(void*) noexcept = nullptr; | |||||
| 84 | void* awaitable_ = nullptr; | 87 | void* awaitable_ = nullptr; | |||||
| 85 | 88 | |||||||
| 86 | // Unwind the inner segment, then hand the parent to its own | 89 | // Unwind the inner segment, then hand the parent to its own | |||||
| 87 | // segment's owner. The last touch of this frame: abandoning the | 90 | // segment's owner. The last touch of this frame: abandoning the | |||||
| 88 | // parent can destroy the awaitable, and this frame with it. | 91 | // parent can destroy the awaitable, and this frame with it. | |||||
| HITCBC | 89 | 10 | void unwind() noexcept | 92 | 10 | void unwind() noexcept | ||
| 90 | { | 93 | { | |||||
| HITCBC | 91 | 10 | destroy_inner_(awaitable_); | 94 | 10 | destroy_inner_(awaitable_); | ||
| HITCBC | 92 | 10 | owner_ = nullptr; | 95 | 10 | owner_ = nullptr; | ||
| HITCBC | 93 | 10 | auto parent = parent_; | 96 | 10 | auto parent = parent_; | ||
| HITCBC | 94 | 10 | capy::abandon(parent); | 97 | 10 | capy::abandon(parent); | ||
| HITCBC | 95 | 10 | } | 98 | 10 | } | ||
| 96 | 99 | |||||||
| HITCBC | 97 | 87 | ~promise_type() | 100 | 95 | ~promise_type() | ||
| 98 | { | 101 | { | |||||
| 99 | // Linked only when a closed executor destroyed this frame | 102 | // Linked only when a closed executor destroyed this frame | |||||
| 100 | // instead of resuming it to unwind. | 103 | // instead of resuming it to unwind. | |||||
| HITCBC | 101 | 87 | if(owner_) | 104 | 95 | if(owner_) | ||
| 102 | { | 105 | { | |||||
| HITCBC | 103 | 1 | owner_->h_ = nullptr; | 106 | 1 | owner_->h_ = nullptr; | ||
| HITCBC | 104 | 1 | unwind(); | 107 | 1 | unwind(); | ||
| 105 | } | 108 | } | |||||
| HITCBC | 106 | 87 | } | 109 | 95 | } | ||
| 107 | 110 | |||||||
| HITCBC | 108 | 87 | boundary_trampoline get_return_object() noexcept | 111 | 95 | boundary_trampoline get_return_object() noexcept | ||
| 109 | { | 112 | { | |||||
| 110 | return boundary_trampoline{ | 113 | return boundary_trampoline{ | |||||
| HITCBC | 111 | 87 | std::coroutine_handle<promise_type>::from_promise(*this)}; | 114 | 95 | std::coroutine_handle<promise_type>::from_promise(*this)}; | ||
| 112 | } | 115 | } | |||||
| 113 | 116 | |||||||
| HITCBC | 114 | 87 | std::suspend_always initial_suspend() noexcept { return {}; } | 117 | 95 | std::suspend_always initial_suspend() noexcept { return {}; } | ||
| 115 | 118 | |||||||
| HITCBC | 116 | 85 | auto final_suspend() noexcept | 119 | 93 | auto final_suspend() noexcept | ||
| 117 | { | 120 | { | |||||
| 118 | struct awaiter | 121 | struct awaiter | |||||
| 119 | { | 122 | { | |||||
| 120 | promise_type* p_; | 123 | promise_type* p_; | |||||
| HITCBC | 121 | 85 | bool await_ready() const noexcept { return false; } | 124 | 93 | bool await_ready() const noexcept { return false; } | ||
| 122 | 125 | |||||||
| HITCBC | 123 | 85 | auto await_suspend( | 126 | 93 | auto await_suspend( | ||
| 124 | std::coroutine_handle<>) noexcept | 127 | std::coroutine_handle<>) noexcept | |||||
| 125 | { | 128 | { | |||||
| 126 | // Nothing here touches p_ after the post or the | 129 | // Nothing here touches p_ after the post or the | |||||
| 127 | // unwind: either can destroy this frame. | 130 | // unwind: either can destroy this frame. | |||||
| HITCBC | 128 | - | 85 | if(p_->segment_.abandoned()) | 131 | + | 93 | auto& segment = p_->segment_; |
| 132 | + | // See run_async's trampoline: only a quitter run | ||||||
| 133 | + | // here is stopped through this owner. | ||||||
| HITGNC | 134 | + | 93 | BOOST_CAPY_ASSERT( | ||||
| 135 | + | (p_->stoppable_ || !segment.stopped()) && | ||||||
| 136 | + | "a quitter stopped a task's segment: " | ||||||
| 137 | + | "see quitter::handle"); | ||||||
| HITGNC | 138 | + | 109 | if(segment.abandoned() && | ||||
| HITGNC | 139 | + | 16 | !(p_->stoppable_ && segment.stopped())) | ||||
| 129 | { | 140 | { | |||||
| HITCBC | 130 | 9 | p_->unwind(); | 141 | 9 | p_->unwind(); | ||
| 131 | } | 142 | } | |||||
| 132 | else | 143 | else | |||||
| 133 | { | 144 | { | |||||
| 145 | + | // A quitter's stop: its frames are destroyed | ||||||
| 146 | + | // here, and the parent resumes to receive the | ||||||
| 147 | + | // stopped value. | ||||||
| HITGNC | 148 | + | 84 | if(segment.abandoned()) | ||||
| HITGNC | 149 | + | 7 | p_->destroy_inner_(p_->awaitable_); | ||||
| HITCBC | 134 | 76 | p_->owner_ = nullptr; | 150 | 84 | p_->owner_ = nullptr; | ||
| HITCBC | 135 | 76 | auto ex = p_->caller_ex_; | 151 | 84 | auto ex = p_->caller_ex_; | ||
| HITCBC | 136 | 76 | ex.post(p_->parent_); | 152 | 84 | ex.post(p_->parent_); | ||
| 137 | } | 153 | } | |||||
| HITCBC | 138 | 85 | return detail::symmetric_transfer( | 154 | 93 | return detail::symmetric_transfer( | ||
| HITCBC | 139 | 170 | std::noop_coroutine()); | 155 | 186 | std::noop_coroutine()); | ||
| 140 | } | 156 | } | |||||
| 141 | 157 | |||||||
| 142 | void await_resume() const noexcept {} // LCOV_EXCL_LINE final_suspend awaiter, never resumed | 158 | void await_resume() const noexcept {} // LCOV_EXCL_LINE final_suspend awaiter, never resumed | |||||
| 143 | }; | 159 | }; | |||||
| HITCBC | 144 | 85 | return awaiter{this}; | 160 | 93 | return awaiter{this}; | ||
| 145 | } | 161 | } | |||||
| 146 | 162 | |||||||
| HITCBC | 147 | 85 | void return_void() noexcept {} | 163 | 93 | void return_void() noexcept {} | ||
| 148 | void unhandled_exception() noexcept {} | 164 | void unhandled_exception() noexcept {} | |||||
| 149 | }; | 165 | }; | |||||
| 150 | 166 | |||||||
| 151 | std::coroutine_handle<promise_type> h_{nullptr}; | 167 | std::coroutine_handle<promise_type> h_{nullptr}; | |||||
| 152 | 168 | |||||||
| HITCBC | 153 | 88 | boundary_trampoline() noexcept = default; | 169 | 96 | boundary_trampoline() noexcept = default; | ||
| 154 | 170 | |||||||
| HITCBC | 155 | 261 | ~boundary_trampoline() | 171 | 285 | ~boundary_trampoline() | ||
| 156 | { | 172 | { | |||||
| HITCBC | 157 | 261 | if(h_) | 173 | 285 | if(h_) | ||
| 158 | { | 174 | { | |||||
| HITCBC | 159 | 85 | h_.promise().owner_ = nullptr; | 175 | 93 | h_.promise().owner_ = nullptr; | ||
| HITCBC | 160 | 85 | h_.destroy(); | 176 | 93 | h_.destroy(); | ||
| 161 | } | 177 | } | |||||
| HITCBC | 162 | 261 | } | 178 | 285 | } | ||
| 163 | 179 | |||||||
| 164 | boundary_trampoline(boundary_trampoline const&) = delete; | 180 | boundary_trampoline(boundary_trampoline const&) = delete; | |||||
| 165 | boundary_trampoline& operator=(boundary_trampoline const&) = delete; | 181 | boundary_trampoline& operator=(boundary_trampoline const&) = delete; | |||||
| 166 | 182 | |||||||
| HITCBC | 167 | 86 | boundary_trampoline(boundary_trampoline&& o) noexcept | 183 | 94 | boundary_trampoline(boundary_trampoline&& o) noexcept | ||
| HITCBC | 168 | 86 | : h_(std::exchange(o.h_, nullptr)) {} | 184 | 94 | : h_(std::exchange(o.h_, nullptr)) {} | ||
| 169 | 185 | |||||||
| HITCBC | 170 | 88 | boundary_trampoline& operator=(boundary_trampoline&& o) noexcept | 186 | 96 | boundary_trampoline& operator=(boundary_trampoline&& o) noexcept | ||
| 171 | { | 187 | { | |||||
| HITCBC | 172 | 88 | if(this != &o) | 188 | 96 | if(this != &o) | ||
| 173 | { | 189 | { | |||||
| HITCBC | 174 | 88 | if(h_) | 190 | 96 | if(h_) | ||
| 175 | { | 191 | { | |||||
| HITCBC | 176 | 1 | h_.promise().owner_ = nullptr; | 192 | 1 | h_.promise().owner_ = nullptr; | ||
| HITCBC | 177 | 1 | h_.destroy(); | 193 | 1 | h_.destroy(); | ||
| 178 | } | 194 | } | |||||
| HITCBC | 179 | 88 | h_ = std::exchange(o.h_, nullptr); | 195 | 96 | h_ = std::exchange(o.h_, nullptr); | ||
| 180 | } | 196 | } | |||||
| HITCBC | 181 | 88 | return *this; | 197 | 96 | return *this; | ||
| 182 | } | 198 | } | |||||
| 183 | 199 | |||||||
| 184 | private: | 200 | private: | |||||
| HITCBC | 185 | 87 | explicit boundary_trampoline(std::coroutine_handle<promise_type> h) noexcept | 201 | 95 | explicit boundary_trampoline(std::coroutine_handle<promise_type> h) noexcept | ||
| HITCBC | 186 | 87 | : h_(h) {} | 202 | 95 | : h_(h) {} | ||
| 187 | }; | 203 | }; | |||||
| 188 | 204 | |||||||
| HITCBC | 189 | 87 | inline boundary_trampoline make_boundary_trampoline() | 205 | 95 | inline boundary_trampoline make_boundary_trampoline() | ||
| 190 | { | 206 | { | |||||
| 191 | co_return; | 207 | co_return; | |||||
| HITCBC | 192 | 174 | } | 208 | 190 | } | ||
| 193 | 209 | |||||||
| 194 | /** Awaitable that binds a ContextRunnable to a specific executor. | 210 | /** Awaitable that binds a ContextRunnable to a specific executor. | |||||
| 195 | 211 | |||||||
| 196 | Stores the executor and inner task by value. When co_awaited, the | 212 | Stores the executor and inner task by value. When co_awaited, the | |||||
| 197 | co_await expression's lifetime extension keeps both alive for the | 213 | co_await expression's lifetime extension keeps both alive for the | |||||
| 198 | duration of the operation. | 214 | duration of the operation. | |||||
| 199 | 215 | |||||||
| 200 | A dispatch trampoline handles the executor switch on completion: | 216 | A dispatch trampoline handles the executor switch on completion: | |||||
| 201 | the inner task's `final_suspend` resumes the trampoline, which | 217 | the inner task's `final_suspend` resumes the trampoline, which | |||||
| 202 | dispatches back through the caller's executor. | 218 | dispatches back through the caller's executor. | |||||
| 203 | 219 | |||||||
| 204 | The `await_context` is owned by this awaitable and is guaranteed to | 220 | The `await_context` is owned by this awaitable and is guaranteed to | |||||
| 205 | outlive the inner task and all awaitables in its chain. Awaitables | 221 | outlive the inner task and all awaitables in its chain. Awaitables | |||||
| 206 | may store `await_context const*` without concern for dangling references. | 222 | may store `await_context const*` without concern for dangling references. | |||||
| 207 | 223 | |||||||
| 208 | @tparam Task The ContextRunnable type | 224 | @tparam Task The ContextRunnable type | |||||
| 209 | @tparam Ex The executor type | 225 | @tparam Ex The executor type | |||||
| 210 | @tparam InheritStopToken If true, inherit caller's stop token | 226 | @tparam InheritStopToken If true, inherit caller's stop token | |||||
| 211 | @tparam Alloc The allocator type (void for no allocator) | 227 | @tparam Alloc The allocator type (void for no allocator) | |||||
| 212 | */ | 228 | */ | |||||
| 213 | template<ContextRunnable Task, Executor Ex, bool InheritStopToken, class Alloc = void> | 229 | template<ContextRunnable Task, Executor Ex, bool InheritStopToken, class Alloc = void> | |||||
| 214 | struct [[nodiscard]] run_awaitable_ex | 230 | struct [[nodiscard]] run_awaitable_ex | |||||
| 215 | { | 231 | { | |||||
| 216 | Ex ex_; | 232 | Ex ex_; | |||||
| 217 | frame_memory_resource<Alloc> resource_; | 233 | frame_memory_resource<Alloc> resource_; | |||||
| 218 | std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_; | 234 | std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_; | |||||
| 219 | await_context env_; | 235 | await_context env_; | |||||
| 220 | executor_ref caller_ex_; | 236 | executor_ref caller_ex_; | |||||
| 221 | boundary_trampoline tr_; | 237 | boundary_trampoline tr_; | |||||
| 222 | continuation task_cont_; | 238 | continuation task_cont_; | |||||
| 223 | bool holds_work_ = false; | 239 | bool holds_work_ = false; | |||||
| 224 | Task inner_; // Last: destroyed first, while env_ is still valid | 240 | Task inner_; // Last: destroyed first, while env_ is still valid | |||||
| 225 | 241 | |||||||
| HITCBC | 226 | 173 | ~run_awaitable_ex() | 242 | 189 | ~run_awaitable_ex() | ||
| 227 | { | 243 | { | |||||
| 228 | // Only a parent destroyed from above leaves the trampoline | 244 | // Only a parent destroyed from above leaves the trampoline | |||||
| 229 | // still linked: the inner task is still running. | 245 | // still linked: the inner task is still running. | |||||
| HITCBC | 230 | 173 | if(tr_.h_ && tr_.h_.promise().owner_) | 246 | 189 | if(tr_.h_ && tr_.h_.promise().owner_) | ||
| 231 | std::terminate(); // LCOV_EXCL_LINE reached only in a death test's child process | 247 | std::terminate(); // LCOV_EXCL_LINE reached only in a death test's child process | |||||
| 232 | // A parent unwound instead of resumed never reaches | 248 | // A parent unwound instead of resumed never reaches | |||||
| 233 | // await_resume. | 249 | // await_resume. | |||||
| HITCBC | 234 | 173 | if(holds_work_) | 250 | 189 | if(holds_work_) | ||
| HITCBC | 235 | 11 | caller_ex_.on_work_finished(); | 251 | 13 | caller_ex_.on_work_finished(); | ||
| HITCBC | 236 | 173 | } | 252 | 189 | } | ||
| 237 | 253 | |||||||
| 238 | // Called by the trampoline to unwind the inner segment. | 254 | // Called by the trampoline to unwind the inner segment. | |||||
| HITCBC | 239 | 10 | static void destroy_inner(void* p) noexcept | 255 | 17 | static void destroy_inner(void* p) noexcept | ||
| 240 | { | 256 | { | |||||
| HITCBC | 241 | 10 | auto& self = *static_cast<run_awaitable_ex*>(p); | 257 | 17 | auto& self = *static_cast<run_awaitable_ex*>(p); | ||
| HITCBC | 242 | 10 | auto h = self.inner_.handle(); | 258 | 17 | auto h = self.inner_.handle(); | ||
| HITCBC | 243 | 10 | self.inner_.release(); | 259 | 17 | self.inner_.release(); | ||
| HITCBC | 244 | 10 | h.destroy(); | 260 | 17 | h.destroy(); | ||
| HITCBC | 245 | 10 | } | 261 | 17 | } | ||
| 246 | 262 | |||||||
| 247 | // void allocator, inherit stop token | 263 | // void allocator, inherit stop token | |||||
| HITCBC | 248 | 75 | run_awaitable_ex(Ex ex, Task inner) | 264 | 82 | run_awaitable_ex(Ex ex, Task inner) | ||
| 249 | requires (InheritStopToken && std::is_void_v<Alloc>) | 265 | requires (InheritStopToken && std::is_void_v<Alloc>) | |||||
| HITCBC | 250 | 75 | : ex_(std::move(ex)) | 266 | 82 | : ex_(std::move(ex)) | ||
| HITCBC | 251 | 75 | , inner_(std::move(inner)) | 267 | 82 | , inner_(std::move(inner)) | ||
| 252 | { | 268 | { | |||||
| HITCBC | 253 | 75 | } | 269 | 82 | } | ||
| 254 | 270 | |||||||
| 255 | // void allocator, explicit stop token | 271 | // void allocator, explicit stop token | |||||
| HITCBC | 256 | 5 | run_awaitable_ex(Ex ex, Task inner, std::stop_token st) | 272 | 6 | run_awaitable_ex(Ex ex, Task inner, std::stop_token st) | ||
| 257 | requires (!InheritStopToken && std::is_void_v<Alloc>) | 273 | requires (!InheritStopToken && std::is_void_v<Alloc>) | |||||
| HITCBC | 258 | 5 | : ex_(std::move(ex)) | 274 | 6 | : ex_(std::move(ex)) | ||
| HITCBC | 259 | 5 | , st_(std::move(st)) | 275 | 6 | , st_(std::move(st)) | ||
| HITCBC | 260 | 5 | , inner_(std::move(inner)) | 276 | 6 | , inner_(std::move(inner)) | ||
| 261 | { | 277 | { | |||||
| HITCBC | 262 | 5 | } | 278 | 6 | } | ||
| 263 | 279 | |||||||
| 264 | // with allocator, inherit stop token (use template to avoid void parameter) | 280 | // with allocator, inherit stop token (use template to avoid void parameter) | |||||
| 265 | template<class A> | 281 | template<class A> | |||||
| 266 | requires (InheritStopToken && !std::is_void_v<Alloc> && std::same_as<A, Alloc>) | 282 | requires (InheritStopToken && !std::is_void_v<Alloc> && std::same_as<A, Alloc>) | |||||
| HITCBC | 267 | 5 | run_awaitable_ex(Ex ex, A alloc, Task inner) | 283 | 5 | run_awaitable_ex(Ex ex, A alloc, Task inner) | ||
| HITCBC | 268 | 5 | : ex_(std::move(ex)) | 284 | 5 | : ex_(std::move(ex)) | ||
| HITCBC | 269 | 5 | , resource_(std::move(alloc)) | 285 | 5 | , resource_(std::move(alloc)) | ||
| HITCBC | 270 | 5 | , inner_(std::move(inner)) | 286 | 5 | , inner_(std::move(inner)) | ||
| 271 | { | 287 | { | |||||
| HITCBC | 272 | 5 | } | 288 | 5 | } | ||
| 273 | 289 | |||||||
| 274 | // with allocator, explicit stop token (use template to avoid void parameter) | 290 | // with allocator, explicit stop token (use template to avoid void parameter) | |||||
| 275 | template<class A> | 291 | template<class A> | |||||
| 276 | requires (!InheritStopToken && !std::is_void_v<Alloc> && std::same_as<A, Alloc>) | 292 | requires (!InheritStopToken && !std::is_void_v<Alloc> && std::same_as<A, Alloc>) | |||||
| HITCBC | 277 | 2 | run_awaitable_ex(Ex ex, A alloc, Task inner, std::stop_token st) | 293 | 2 | run_awaitable_ex(Ex ex, A alloc, Task inner, std::stop_token st) | ||
| HITCBC | 278 | 2 | : ex_(std::move(ex)) | 294 | 2 | : ex_(std::move(ex)) | ||
| HITCBC | 279 | 2 | , resource_(std::move(alloc)) | 295 | 2 | , resource_(std::move(alloc)) | ||
| HITCBC | 280 | 2 | , st_(std::move(st)) | 296 | 2 | , st_(std::move(st)) | ||
| HITCBC | 281 | 2 | , inner_(std::move(inner)) | 297 | 2 | , inner_(std::move(inner)) | ||
| 282 | { | 298 | { | |||||
| HITCBC | 283 | 2 | } | 299 | 2 | } | ||
| 284 | 300 | |||||||
| HITCBC | 285 | 86 | bool await_ready() const noexcept | 301 | 94 | bool await_ready() const noexcept | ||
| 286 | { | 302 | { | |||||
| HITCBC | 287 | 86 | return inner_.await_ready(); | 303 | 94 | return inner_.await_ready(); | ||
| 288 | } | 304 | } | |||||
| 289 | 305 | |||||||
| HITCBC | 290 | 75 | decltype(auto) await_resume() | 306 | 81 | decltype(auto) await_resume() | ||
| 291 | { | 307 | { | |||||
| HITCBC | 292 | 75 | if(std::exchange(holds_work_, false)) | 308 | 81 | if(std::exchange(holds_work_, false)) | ||
| HITCBC | 293 | 75 | caller_ex_.on_work_finished(); | 309 | 81 | caller_ex_.on_work_finished(); | ||
| 310 | + | if constexpr(stops_by_abandonment<Task>) | ||||||
| 311 | + | { | ||||||
| HITGNC | 312 | + | 6 | if(tr_.h_ && tr_.h_.promise().segment_.stopped()) | ||||
| HITGNC | 313 | + | 6 | return Task::promise_type::stopped_result(); | ||||
| 314 | + | } | ||||||
| HITCBC | 294 | 75 | return inner_.await_resume(); | 315 | 75 | return inner_.await_resume(); | ||
| 295 | } | 316 | } | |||||
| 296 | 317 | |||||||
| HITCBC | 297 | 87 | std::coroutine_handle<> await_suspend(std::coroutine_handle<> cont, await_context const* caller_env) | 318 | 95 | std::coroutine_handle<> await_suspend(std::coroutine_handle<> cont, await_context const* caller_env) | ||
| 298 | { | 319 | { | |||||
| HITCBC | 299 | 87 | tr_ = make_boundary_trampoline(); | 320 | 95 | tr_ = make_boundary_trampoline(); | ||
| HITCBC | 300 | 87 | auto& tp = tr_.h_.promise(); | 321 | 95 | auto& tp = tr_.h_.promise(); | ||
| HITCBC | 301 | 87 | tp.owner_ = &tr_; | 322 | 95 | tp.owner_ = &tr_; | ||
| HITCBC | 302 | 87 | tp.caller_ex_ = caller_env->executor; | 323 | 95 | tp.caller_ex_ = caller_env->executor; | ||
| HITCBC | 303 | 87 | tp.parent_.h = cont; | 324 | 95 | tp.parent_.h = cont; | ||
| HITCBC | 304 | 87 | tp.parent_.owner = caller_env->owner; | 325 | 95 | tp.parent_.owner = caller_env->owner; | ||
| HITCBC | 305 | 87 | tp.destroy_inner_ = &run_awaitable_ex::destroy_inner; | 326 | 95 | tp.destroy_inner_ = &run_awaitable_ex::destroy_inner; | ||
| HITCBC | 306 | 87 | tp.awaitable_ = this; | 327 | 95 | tp.awaitable_ = this; | ||
| HITCBC | 307 | 87 | tp.segment_.bind(tr_.h_, ex_); | 328 | 95 | tp.segment_.bind(tr_.h_, ex_); | ||
| HITGNC | 329 | + | 95 | tp.stoppable_ = stops_by_abandonment<Task>; | ||||
| HITCBC | 308 | 87 | caller_ex_ = caller_env->executor; | 330 | 95 | caller_ex_ = caller_env->executor; | ||
| 309 | // Released once the parent resumes or is unwound. Not on the | 331 | // Released once the parent resumes or is unwound. Not on the | |||||
| 310 | // post back: the parent's chain may own the executor object and | 332 | // post back: the parent's chain may own the executor object and | |||||
| 311 | // can be gone by the time post returns. | 333 | // can be gone by the time post returns. | |||||
| HITCBC | 312 | 87 | caller_ex_.on_work_started(); | 334 | 95 | caller_ex_.on_work_started(); | ||
| HITCBC | 313 | 87 | holds_work_ = true; | 335 | 95 | holds_work_ = true; | ||
| 314 | 336 | |||||||
| HITCBC | 315 | 87 | auto h = inner_.handle(); | 337 | 95 | auto h = inner_.handle(); | ||
| HITCBC | 316 | 87 | auto& p = h.promise(); | 338 | 95 | auto& p = h.promise(); | ||
| HITCBC | 317 | 87 | p.set_continuation(tr_.h_); | 339 | 95 | p.set_continuation(tr_.h_); | ||
| 318 | 340 | |||||||
| HITCBC | 319 | 87 | env_.owner = &tp.segment_; | 341 | 95 | env_.owner = &tp.segment_; | ||
| HITCBC | 320 | 87 | env_.executor = ex_; | 342 | 95 | env_.executor = ex_; | ||
| 321 | if constexpr (InheritStopToken) | 343 | if constexpr (InheritStopToken) | |||||
| HITCBC | 322 | 80 | env_.stop_token = caller_env->stop_token; | 344 | 87 | env_.stop_token = caller_env->stop_token; | ||
| 323 | else | 345 | else | |||||
| HITCBC | 324 | 7 | env_.stop_token = st_; | 346 | 8 | env_.stop_token = st_; | ||
| 325 | 347 | |||||||
| 326 | if constexpr (!std::is_void_v<Alloc>) | 348 | if constexpr (!std::is_void_v<Alloc>) | |||||
| HITCBC | 327 | 7 | env_.frame_allocator = resource_.get(); | 349 | 7 | env_.frame_allocator = resource_.get(); | ||
| 328 | else | 350 | else | |||||
| HITCBC | 329 | 80 | env_.frame_allocator = caller_env->frame_allocator; | 351 | 88 | env_.frame_allocator = caller_env->frame_allocator; | ||
| 330 | 352 | |||||||
| HITCBC | 331 | 87 | p.set_environment(&env_); | 353 | 95 | p.set_environment(&env_); | ||
| 354 | + | |||||||
| HITCBC | 332 | 87 | task_cont_.h = h; | 355 | 95 | task_cont_.h = h; | ||
| HITCBC | 333 | 87 | task_cont_.owner = &tp.segment_; | 356 | 95 | task_cont_.owner = &tp.segment_; | ||
| 334 | try | 357 | try | |||||
| 335 | { | 358 | { | |||||
| 359 | + | // A quitter starts through its gate, which checks the stop | ||||||
| 360 | + | // when the body would start: a stopped quitter's segment is | ||||||
| 361 | + | // abandoned for the stop, and the parent receives the | ||||||
| 362 | + | // stopped value. | ||||||
| 363 | + | if constexpr(stops_by_abandonment<Task>) | ||||||
| HITGNC | 364 | + | 7 | task_cont_.h = p.launch_handle(); | ||||
| HITCBC | 336 | 87 | ex_.post(task_cont_); | 365 | 95 | ex_.post(task_cont_); | ||
| 337 | } | 366 | } | |||||
| HITCBC | 338 | 2 | catch(...) | 367 | 2 | catch(...) | ||
| 339 | { | 368 | { | |||||
| 340 | // The parent resumes with the exception: nothing may still | 369 | // The parent resumes with the exception: nothing may still | |||||
| 341 | // treat the task as lent or hold work for the parent. | 370 | // treat the task as lent or hold work for the parent. | |||||
| HITCBC | 342 | 1 | p.set_continuation(std::noop_coroutine()); | 371 | 1 | p.set_continuation(std::noop_coroutine()); | ||
| HITCBC | 343 | 1 | task_cont_.h = nullptr; | 372 | 1 | task_cont_.h = nullptr; | ||
| HITCBC | 344 | 1 | tr_ = boundary_trampoline(); | 373 | 1 | tr_ = boundary_trampoline(); | ||
| HITCBC | 345 | 1 | holds_work_ = false; | 374 | 1 | holds_work_ = false; | ||
| HITCBC | 346 | 1 | caller_ex_.on_work_finished(); | 375 | 1 | caller_ex_.on_work_finished(); | ||
| HITCBC | 347 | 1 | throw; | 376 | 1 | throw; | ||
| 348 | } | 377 | } | |||||
| HITCBC | 349 | 86 | return std::noop_coroutine(); | 378 | 94 | return std::noop_coroutine(); | ||
| 350 | } | 379 | } | |||||
| 351 | 380 | |||||||
| 352 | // Non-copyable | 381 | // Non-copyable | |||||
| 353 | run_awaitable_ex(run_awaitable_ex const&) = delete; | 382 | run_awaitable_ex(run_awaitable_ex const&) = delete; | |||||
| 354 | run_awaitable_ex& operator=(run_awaitable_ex const&) = delete; | 383 | run_awaitable_ex& operator=(run_awaitable_ex const&) = delete; | |||||
| 355 | 384 | |||||||
| 356 | // Movable (no noexcept - Task may throw) | 385 | // Movable (no noexcept - Task may throw) | |||||
| HITCBC | 357 | 86 | run_awaitable_ex(run_awaitable_ex&&) = default; | 386 | 94 | run_awaitable_ex(run_awaitable_ex&&) = default; | ||
| 358 | run_awaitable_ex& operator=(run_awaitable_ex&&) = default; | 387 | run_awaitable_ex& operator=(run_awaitable_ex&&) = default; | |||||
| 359 | }; | 388 | }; | |||||
| 360 | 389 | |||||||
| 361 | /** Awaitable that runs a task with optional stop_token override. | 390 | /** Awaitable that runs a task with optional stop_token override. | |||||
| 362 | 391 | |||||||
| 363 | Does NOT store an executor - the task inherits the caller's executor | 392 | Does NOT store an executor - the task inherits the caller's executor | |||||
| 364 | directly. Executors always match, so no dispatch trampoline is needed. | 393 | directly. Executors always match, so no dispatch trampoline is needed. | |||||
| 365 | The inner task's `final_suspend` resumes the parent directly via | 394 | The inner task's `final_suspend` resumes the parent directly via | |||||
| 366 | unconditional symmetric transfer. | 395 | unconditional symmetric transfer. | |||||
| 367 | 396 | |||||||
| 368 | @tparam Task The ContextRunnable type | 397 | @tparam Task The ContextRunnable type | |||||
| 369 | @tparam InheritStopToken If true, inherit caller's stop token | 398 | @tparam InheritStopToken If true, inherit caller's stop token | |||||
| 370 | @tparam Alloc The allocator type (void for no allocator) | 399 | @tparam Alloc The allocator type (void for no allocator) | |||||
| 371 | */ | 400 | */ | |||||
| 372 | template<ContextRunnable Task, bool InheritStopToken, class Alloc = void> | 401 | template<ContextRunnable Task, bool InheritStopToken, class Alloc = void> | |||||
| 373 | struct [[nodiscard]] run_awaitable | 402 | struct [[nodiscard]] run_awaitable | |||||
| 374 | { | 403 | { | |||||
| 375 | frame_memory_resource<Alloc> resource_; | 404 | frame_memory_resource<Alloc> resource_; | |||||
| 376 | std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_; | 405 | std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_; | |||||
| 377 | await_context env_; | 406 | await_context env_; | |||||
| 378 | // Set while the parent is suspended awaiting the inner task. | 407 | // Set while the parent is suspended awaiting the inner task. | |||||
| 379 | bool lent_ = false; | 408 | bool lent_ = false; | |||||
| 380 | Task inner_; // Last: destroyed first, while env_ is still valid | 409 | Task inner_; // Last: destroyed first, while env_ is still valid | |||||
| 381 | 410 | |||||||
| HITCBC | 382 | 36 | ~run_awaitable() | 411 | 42 | ~run_awaitable() | ||
| 383 | { | 412 | { | |||||
| 384 | // Only the unwind of an abandoned chain destroys an inner task | 413 | // Only the unwind of an abandoned chain destroys an inner task | |||||
| 385 | // still awaited; checked here so it terminates whatever the | 414 | // still awaited; checked here so it terminates whatever the | |||||
| 386 | // task type does when destroyed while lent. | 415 | // task type does when destroyed while lent. | |||||
| HITCBC | 387 | 36 | auto h = inner_.handle(); | 416 | 42 | auto h = inner_.handle(); | ||
| HITCBC | 388 | 46 | if(lent_ && h && !h.done() && | 417 | 52 | if(lent_ && h && !h.done() && | ||
| HITCBC | 389 | 10 | !(env_.owner && env_.owner->abandoned())) | 418 | 10 | !(env_.owner && env_.owner->abandoned())) | ||
| 390 | std::terminate(); // LCOV_EXCL_LINE reached only in a death test's child process | 419 | std::terminate(); // LCOV_EXCL_LINE reached only in a death test's child process | |||||
| HITCBC | 391 | 36 | } | 420 | 42 | } | ||
| 392 | 421 | |||||||
| 393 | // void allocator, inherit stop token | 422 | // void allocator, inherit stop token | |||||
| 394 | explicit run_awaitable(Task inner) | 423 | explicit run_awaitable(Task inner) | |||||
| 395 | requires (InheritStopToken && std::is_void_v<Alloc>) | 424 | requires (InheritStopToken && std::is_void_v<Alloc>) | |||||
| 396 | : inner_(std::move(inner)) | 425 | : inner_(std::move(inner)) | |||||
| 397 | { | 426 | { | |||||
| 398 | } | 427 | } | |||||
| 399 | 428 | |||||||
| 400 | // void allocator, explicit stop token | 429 | // void allocator, explicit stop token | |||||
| HITCBC | 401 | 2 | run_awaitable(Task inner, std::stop_token st) | 430 | 5 | run_awaitable(Task inner, std::stop_token st) | ||
| 402 | requires (!InheritStopToken && std::is_void_v<Alloc>) | 431 | requires (!InheritStopToken && std::is_void_v<Alloc>) | |||||
| HITCBC | 403 | 2 | : st_(std::move(st)) | 432 | 5 | : st_(std::move(st)) | ||
| HITCBC | 404 | 2 | , inner_(std::move(inner)) | 433 | 5 | , inner_(std::move(inner)) | ||
| 405 | { | 434 | { | |||||
| HITCBC | 406 | 2 | } | 435 | 5 | } | ||
| 407 | 436 | |||||||
| 408 | // with allocator, inherit stop token (use template to avoid void parameter) | 437 | // with allocator, inherit stop token (use template to avoid void parameter) | |||||
| 409 | template<class A> | 438 | template<class A> | |||||
| 410 | requires (InheritStopToken && !std::is_void_v<Alloc> && std::same_as<A, Alloc>) | 439 | requires (InheritStopToken && !std::is_void_v<Alloc> && std::same_as<A, Alloc>) | |||||
| HITCBC | 411 | 14 | run_awaitable(A alloc, Task inner) | 440 | 14 | run_awaitable(A alloc, Task inner) | ||
| HITCBC | 412 | 14 | : resource_(std::move(alloc)) | 441 | 14 | : resource_(std::move(alloc)) | ||
| HITCBC | 413 | 14 | , inner_(std::move(inner)) | 442 | 14 | , inner_(std::move(inner)) | ||
| 414 | { | 443 | { | |||||
| HITCBC | 415 | 14 | } | 444 | 14 | } | ||
| 416 | 445 | |||||||
| 417 | // with allocator, explicit stop token (use template to avoid void parameter) | 446 | // with allocator, explicit stop token (use template to avoid void parameter) | |||||
| 418 | template<class A> | 447 | template<class A> | |||||
| 419 | requires (!InheritStopToken && !std::is_void_v<Alloc> && std::same_as<A, Alloc>) | 448 | requires (!InheritStopToken && !std::is_void_v<Alloc> && std::same_as<A, Alloc>) | |||||
| HITCBC | 420 | 2 | run_awaitable(A alloc, Task inner, std::stop_token st) | 449 | 2 | run_awaitable(A alloc, Task inner, std::stop_token st) | ||
| HITCBC | 421 | 2 | : resource_(std::move(alloc)) | 450 | 2 | : resource_(std::move(alloc)) | ||
| HITCBC | 422 | 2 | , st_(std::move(st)) | 451 | 2 | , st_(std::move(st)) | ||
| HITCBC | 423 | 2 | , inner_(std::move(inner)) | 452 | 2 | , inner_(std::move(inner)) | ||
| 424 | { | 453 | { | |||||
| HITCBC | 425 | 2 | } | 454 | 2 | } | ||
| 426 | 455 | |||||||
| HITCBC | 427 | 18 | bool await_ready() const noexcept | 456 | 21 | bool await_ready() const noexcept | ||
| 428 | { | 457 | { | |||||
| HITCBC | 429 | 18 | return inner_.await_ready(); | 458 | 21 | return inner_.await_ready(); | ||
| 430 | } | 459 | } | |||||
| 431 | 460 | |||||||
| HITCBC | 432 | 8 | decltype(auto) await_resume() | 461 | 11 | decltype(auto) await_resume() | ||
| 433 | { | 462 | { | |||||
| HITCBC | 434 | 8 | lent_ = false; | 463 | 11 | lent_ = false; | ||
| HITCBC | 435 | 8 | return inner_.await_resume(); | 464 | 11 | return inner_.await_resume(); | ||
| 436 | } | 465 | } | |||||
| 437 | 466 | |||||||
| HITCBC | 438 | 18 | std::coroutine_handle<> await_suspend(std::coroutine_handle<> cont, await_context const* caller_env) | 467 | 21 | std::coroutine_handle<> await_suspend(std::coroutine_handle<> cont, await_context const* caller_env) | ||
| 439 | - | auto h = inner_.handle(); | ||||||
| DCB | 440 | - | 18 | auto& p = h.promise(); | ||||
| DCB | 441 | - | 18 | p.set_continuation(cont); | ||||
| DCB | 442 | - | 18 | lent_ = true; | ||||
| DCB | 443 | - | 18 | |||||
| 444 | { | 468 | { | |||||
| HITCBC | 445 | 18 | env_.owner = caller_env->owner; | 469 | 21 | env_.owner = caller_env->owner; | ||
| HITCBC | 446 | 18 | env_.executor = caller_env->executor; | 470 | 21 | env_.executor = caller_env->executor; | ||
| 447 | if constexpr (InheritStopToken) | 471 | if constexpr (InheritStopToken) | |||||
| HITCBC | 448 | 14 | env_.stop_token = caller_env->stop_token; | 472 | 14 | env_.stop_token = caller_env->stop_token; | ||
| 449 | else | 473 | else | |||||
| HITCBC | 450 | 4 | env_.stop_token = st_; | 474 | 7 | env_.stop_token = st_; | ||
| 451 | 475 | |||||||
| 452 | if constexpr (!std::is_void_v<Alloc>) | 476 | if constexpr (!std::is_void_v<Alloc>) | |||||
| HITCBC | 453 | 16 | env_.frame_allocator = resource_.get(); | 477 | 16 | env_.frame_allocator = resource_.get(); | ||
| 454 | else | 478 | else | |||||
| HITCBC | 455 | 2 | env_.frame_allocator = caller_env->frame_allocator; | 479 | 5 | env_.frame_allocator = caller_env->frame_allocator; | ||
| 456 | 480 | |||||||
| ECB | 457 | - | 18 | p.set_environment(&env_); | 481 | + | // A quitter opens a segment of its own, so a stop on this run's | |
| ECB | 458 | - | 18 | return h; | 482 | + | // token never abandons the caller's segment. | |
| 483 | + | if constexpr(stops_by_abandonment<Task>) | ||||||
| 484 | + | { | ||||||
| 485 | + | // Lent only once the segment is open: opening it can throw | ||||||
| 486 | + | // into a quitter's body, and a lent inner task terminates | ||||||
| 487 | + | // in our destructor. | ||||||
| HITGNC | 488 | + | 3 | auto h = inner_.await_suspend(cont, &env_); | ||||
| HITGNC | 489 | + | 3 | lent_ = true; | ||||
| HITGNC | 490 | + | 3 | return h; | ||||
| 491 | + | } | ||||||
| 492 | + | else | ||||||
| 493 | + | { | ||||||
| HITGNC | 494 | + | 18 | auto h = inner_.handle(); | ||||
| HITGNC | 495 | + | 18 | auto& p = h.promise(); | ||||
| HITGNC | 496 | + | 18 | p.set_continuation(cont); | ||||
| HITGNC | 497 | + | 18 | p.set_environment(&env_); | ||||
| HITGNC | 498 | + | 18 | lent_ = true; | ||||
| HITGNC | 499 | + | 18 | return h; | ||||
| 500 | + | } | ||||||
| 459 | } | 501 | } | |||||
| 460 | 502 | |||||||
| 461 | // Non-copyable | 503 | // Non-copyable | |||||
| 462 | run_awaitable(run_awaitable const&) = delete; | 504 | run_awaitable(run_awaitable const&) = delete; | |||||
| 463 | run_awaitable& operator=(run_awaitable const&) = delete; | 505 | run_awaitable& operator=(run_awaitable const&) = delete; | |||||
| 464 | 506 | |||||||
| 465 | // Movable (no noexcept - Task may throw) | 507 | // Movable (no noexcept - Task may throw) | |||||
| HITCBC | 466 | 18 | run_awaitable(run_awaitable&&) = default; | 508 | 21 | run_awaitable(run_awaitable&&) = default; | ||
| 467 | run_awaitable& operator=(run_awaitable&&) = default; | 509 | run_awaitable& operator=(run_awaitable&&) = default; | |||||
| 468 | }; | 510 | }; | |||||
| 469 | 511 | |||||||
| 470 | /** Wrapper returned by run(ex, ...) that accepts a task for execution. | 512 | /** Wrapper returned by run(ex, ...) that accepts a task for execution. | |||||
| 471 | 513 | |||||||
| 472 | @tparam Ex The executor type. | 514 | @tparam Ex The executor type. | |||||
| 473 | @tparam InheritStopToken If true, inherit caller's stop token. | 515 | @tparam InheritStopToken If true, inherit caller's stop token. | |||||
| 474 | @tparam Alloc The allocator type (void for no allocator). | 516 | @tparam Alloc The allocator type (void for no allocator). | |||||
| 475 | */ | 517 | */ | |||||
| 476 | template<Executor Ex, bool InheritStopToken, class Alloc> | 518 | template<Executor Ex, bool InheritStopToken, class Alloc> | |||||
| 477 | class [[nodiscard]] run_wrapper_ex | 519 | class [[nodiscard]] run_wrapper_ex | |||||
| 478 | { | 520 | { | |||||
| 479 | Ex ex_; | 521 | Ex ex_; | |||||
| 480 | std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_; | 522 | std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_; | |||||
| 481 | frame_memory_resource<Alloc> resource_; | 523 | frame_memory_resource<Alloc> resource_; | |||||
| 482 | Alloc alloc_; // Copy to pass to awaitable | 524 | Alloc alloc_; // Copy to pass to awaitable | |||||
| 483 | 525 | |||||||
| 484 | std::pmr::memory_resource* saved_tls_ = | 526 | std::pmr::memory_resource* saved_tls_ = | |||||
| 485 | get_current_frame_allocator(); | 527 | get_current_frame_allocator(); | |||||
| 486 | bool restored_ = false; | 528 | bool restored_ = false; | |||||
| 487 | 529 | |||||||
| 488 | public: | 530 | public: | |||||
| HITCBC | 489 | 3 | run_wrapper_ex(Ex ex, Alloc alloc) | 531 | 3 | run_wrapper_ex(Ex ex, Alloc alloc) | ||
| 490 | requires InheritStopToken | 532 | requires InheritStopToken | |||||
| HITCBC | 491 | 3 | : ex_(std::move(ex)) | 533 | 3 | : ex_(std::move(ex)) | ||
| HITCBC | 492 | 3 | , resource_(alloc) | 534 | 3 | , resource_(alloc) | ||
| HITCBC | 493 | 3 | , alloc_(std::move(alloc)) | 535 | 3 | , alloc_(std::move(alloc)) | ||
| 494 | { | 536 | { | |||||
| HITCBC | 495 | 3 | set_current_frame_allocator(&resource_); | 537 | 3 | set_current_frame_allocator(&resource_); | ||
| HITCBC | 496 | 3 | } | 538 | 3 | } | ||
| 497 | 539 | |||||||
| HITCBC | 498 | 1 | run_wrapper_ex(Ex ex, std::stop_token st, Alloc alloc) | 540 | 1 | run_wrapper_ex(Ex ex, std::stop_token st, Alloc alloc) | ||
| 499 | requires (!InheritStopToken) | 541 | requires (!InheritStopToken) | |||||
| HITCBC | 500 | 1 | : ex_(std::move(ex)) | 542 | 1 | : ex_(std::move(ex)) | ||
| HITCBC | 501 | 1 | , st_(std::move(st)) | 543 | 1 | , st_(std::move(st)) | ||
| HITCBC | 502 | 1 | , resource_(alloc) | 544 | 1 | , resource_(alloc) | ||
| HITCBC | 503 | 1 | , alloc_(std::move(alloc)) | 545 | 1 | , alloc_(std::move(alloc)) | ||
| 504 | { | 546 | { | |||||
| HITCBC | 505 | 1 | set_current_frame_allocator(&resource_); | 547 | 1 | set_current_frame_allocator(&resource_); | ||
| HITCBC | 506 | 1 | } | 548 | 1 | } | ||
| 507 | 549 | |||||||
| 508 | // Restores the caller's allocator when evaluating the task argument | 550 | // Restores the caller's allocator when evaluating the task argument | |||||
| 509 | // threw. Not afterwards: a wrapper destroyed with a torn-down parent | 551 | // threw. Not afterwards: a wrapper destroyed with a torn-down parent | |||||
| 510 | // frame must not touch the tearing thread's allocator. | 552 | // frame must not touch the tearing thread's allocator. | |||||
| HITCBC | 511 | 4 | ~run_wrapper_ex() | 553 | 4 | ~run_wrapper_ex() | ||
| 512 | { | 554 | { | |||||
| HITCBC | 513 | 4 | if(!restored_) | 555 | 4 | if(!restored_) | ||
| HITCBC | 514 | 1 | set_current_frame_allocator(saved_tls_); | 556 | 1 | set_current_frame_allocator(saved_tls_); | ||
| HITCBC | 515 | 4 | } | 557 | 4 | } | ||
| 516 | 558 | |||||||
| 517 | // Non-copyable, non-movable (must be used immediately) | 559 | // Non-copyable, non-movable (must be used immediately) | |||||
| 518 | run_wrapper_ex(run_wrapper_ex const&) = delete; | 560 | run_wrapper_ex(run_wrapper_ex const&) = delete; | |||||
| 519 | run_wrapper_ex(run_wrapper_ex&&) = delete; | 561 | run_wrapper_ex(run_wrapper_ex&&) = delete; | |||||
| 520 | run_wrapper_ex& operator=(run_wrapper_ex const&) = delete; | 562 | run_wrapper_ex& operator=(run_wrapper_ex const&) = delete; | |||||
| 521 | run_wrapper_ex& operator=(run_wrapper_ex&&) = delete; | 563 | run_wrapper_ex& operator=(run_wrapper_ex&&) = delete; | |||||
| 522 | 564 | |||||||
| 523 | template<ContextRunnable Task> | 565 | template<ContextRunnable Task> | |||||
| HITCBC | 524 | 3 | [[nodiscard]] auto operator()(Task t) && | 566 | 3 | [[nodiscard]] auto operator()(Task t) && | ||
| 525 | { | 567 | { | |||||
| 526 | // The task's frame is made; the caller's own frames are not. | 568 | // The task's frame is made; the caller's own frames are not. | |||||
| HITCBC | 527 | 3 | set_current_frame_allocator(saved_tls_); | 569 | 3 | set_current_frame_allocator(saved_tls_); | ||
| HITCBC | 528 | 3 | restored_ = true; | 570 | 3 | restored_ = true; | ||
| 529 | if constexpr (InheritStopToken) | 571 | if constexpr (InheritStopToken) | |||||
| 530 | return run_awaitable_ex<Task, Ex, true, Alloc>{ | 572 | return run_awaitable_ex<Task, Ex, true, Alloc>{ | |||||
| HITCBC | 531 | 2 | std::move(ex_), std::move(alloc_), std::move(t)}; | 573 | 2 | std::move(ex_), std::move(alloc_), std::move(t)}; | ||
| 532 | else | 574 | else | |||||
| 533 | return run_awaitable_ex<Task, Ex, false, Alloc>{ | 575 | return run_awaitable_ex<Task, Ex, false, Alloc>{ | |||||
| HITCBC | 534 | 1 | std::move(ex_), std::move(alloc_), std::move(t), std::move(st_)}; | 576 | 1 | std::move(ex_), std::move(alloc_), std::move(t), std::move(st_)}; | ||
| 535 | } | 577 | } | |||||
| 536 | }; | 578 | }; | |||||
| 537 | 579 | |||||||
| 538 | /// Specialization for memory_resource* - stores pointer directly. | 580 | /// Specialization for memory_resource* - stores pointer directly. | |||||
| 539 | template<Executor Ex, bool InheritStopToken> | 581 | template<Executor Ex, bool InheritStopToken> | |||||
| 540 | class [[nodiscard]] run_wrapper_ex<Ex, InheritStopToken, std::pmr::memory_resource*> | 582 | class [[nodiscard]] run_wrapper_ex<Ex, InheritStopToken, std::pmr::memory_resource*> | |||||
| 541 | { | 583 | { | |||||
| 542 | Ex ex_; | 584 | Ex ex_; | |||||
| 543 | std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_; | 585 | std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_; | |||||
| 544 | std::pmr::memory_resource* mr_; | 586 | std::pmr::memory_resource* mr_; | |||||
| 545 | 587 | |||||||
| 546 | std::pmr::memory_resource* saved_tls_ = | 588 | std::pmr::memory_resource* saved_tls_ = | |||||
| 547 | get_current_frame_allocator(); | 589 | get_current_frame_allocator(); | |||||
| 548 | bool restored_ = false; | 590 | bool restored_ = false; | |||||
| 549 | 591 | |||||||
| 550 | public: | 592 | public: | |||||
| HITCBC | 551 | 4 | run_wrapper_ex(Ex ex, std::pmr::memory_resource* mr) | 593 | 4 | run_wrapper_ex(Ex ex, std::pmr::memory_resource* mr) | ||
| 552 | requires InheritStopToken | 594 | requires InheritStopToken | |||||
| HITCBC | 553 | 4 | : ex_(std::move(ex)) | 595 | 4 | : ex_(std::move(ex)) | ||
| HITCBC | 554 | 4 | , mr_(mr) | 596 | 4 | , mr_(mr) | ||
| 555 | { | 597 | { | |||||
| HITCBC | 556 | 4 | set_current_frame_allocator(mr_); | 598 | 4 | set_current_frame_allocator(mr_); | ||
| HITCBC | 557 | 4 | } | 599 | 4 | } | ||
| 558 | 600 | |||||||
| HITCBC | 559 | 1 | run_wrapper_ex(Ex ex, std::stop_token st, std::pmr::memory_resource* mr) | 601 | 1 | run_wrapper_ex(Ex ex, std::stop_token st, std::pmr::memory_resource* mr) | ||
| 560 | requires (!InheritStopToken) | 602 | requires (!InheritStopToken) | |||||
| HITCBC | 561 | 1 | : ex_(std::move(ex)) | 603 | 1 | : ex_(std::move(ex)) | ||
| HITCBC | 562 | 1 | , st_(std::move(st)) | 604 | 1 | , st_(std::move(st)) | ||
| HITCBC | 563 | 1 | , mr_(mr) | 605 | 1 | , mr_(mr) | ||
| 564 | { | 606 | { | |||||
| HITCBC | 565 | 1 | set_current_frame_allocator(mr_); | 607 | 1 | set_current_frame_allocator(mr_); | ||
| HITCBC | 566 | 1 | } | 608 | 1 | } | ||
| 567 | 609 | |||||||
| 568 | // Restores the caller's allocator when evaluating the task argument | 610 | // Restores the caller's allocator when evaluating the task argument | |||||
| 569 | // threw. Not afterwards: a wrapper destroyed with a torn-down parent | 611 | // threw. Not afterwards: a wrapper destroyed with a torn-down parent | |||||
| 570 | // frame must not touch the tearing thread's allocator. | 612 | // frame must not touch the tearing thread's allocator. | |||||
| HITCBC | 571 | 5 | ~run_wrapper_ex() | 613 | 5 | ~run_wrapper_ex() | ||
| 572 | { | 614 | { | |||||
| HITCBC | 573 | 5 | if(!restored_) | 615 | 5 | if(!restored_) | ||
| HITCBC | 574 | 1 | set_current_frame_allocator(saved_tls_); | 616 | 1 | set_current_frame_allocator(saved_tls_); | ||
| HITCBC | 575 | 5 | } | 617 | 5 | } | ||
| 576 | 618 | |||||||
| 577 | // Non-copyable, non-movable (must be used immediately) | 619 | // Non-copyable, non-movable (must be used immediately) | |||||
| 578 | run_wrapper_ex(run_wrapper_ex const&) = delete; | 620 | run_wrapper_ex(run_wrapper_ex const&) = delete; | |||||
| 579 | run_wrapper_ex(run_wrapper_ex&&) = delete; | 621 | run_wrapper_ex(run_wrapper_ex&&) = delete; | |||||
| 580 | run_wrapper_ex& operator=(run_wrapper_ex const&) = delete; | 622 | run_wrapper_ex& operator=(run_wrapper_ex const&) = delete; | |||||
| 581 | run_wrapper_ex& operator=(run_wrapper_ex&&) = delete; | 623 | run_wrapper_ex& operator=(run_wrapper_ex&&) = delete; | |||||
| 582 | 624 | |||||||
| 583 | template<ContextRunnable Task> | 625 | template<ContextRunnable Task> | |||||
| HITCBC | 584 | 4 | [[nodiscard]] auto operator()(Task t) && | 626 | 4 | [[nodiscard]] auto operator()(Task t) && | ||
| 585 | { | 627 | { | |||||
| 586 | // The task's frame is made; the caller's own frames are not. | 628 | // The task's frame is made; the caller's own frames are not. | |||||
| HITCBC | 587 | 4 | set_current_frame_allocator(saved_tls_); | 629 | 4 | set_current_frame_allocator(saved_tls_); | ||
| HITCBC | 588 | 4 | restored_ = true; | 630 | 4 | restored_ = true; | ||
| 589 | if constexpr (InheritStopToken) | 631 | if constexpr (InheritStopToken) | |||||
| 590 | return run_awaitable_ex<Task, Ex, true, std::pmr::memory_resource*>{ | 632 | return run_awaitable_ex<Task, Ex, true, std::pmr::memory_resource*>{ | |||||
| HITCBC | 591 | 3 | std::move(ex_), mr_, std::move(t)}; | 633 | 3 | std::move(ex_), mr_, std::move(t)}; | ||
| 592 | else | 634 | else | |||||
| 593 | return run_awaitable_ex<Task, Ex, false, std::pmr::memory_resource*>{ | 635 | return run_awaitable_ex<Task, Ex, false, std::pmr::memory_resource*>{ | |||||
| HITCBC | 594 | 1 | std::move(ex_), mr_, std::move(t), std::move(st_)}; | 636 | 1 | std::move(ex_), mr_, std::move(t), std::move(st_)}; | ||
| 595 | } | 637 | } | |||||
| 596 | }; | 638 | }; | |||||
| 597 | 639 | |||||||
| 598 | /// Specialization for no allocator (void). | 640 | /// Specialization for no allocator (void). | |||||
| 599 | template<Executor Ex, bool InheritStopToken> | 641 | template<Executor Ex, bool InheritStopToken> | |||||
| 600 | class [[nodiscard]] run_wrapper_ex<Ex, InheritStopToken, void> | 642 | class [[nodiscard]] run_wrapper_ex<Ex, InheritStopToken, void> | |||||
| 601 | { | 643 | { | |||||
| 602 | Ex ex_; | 644 | Ex ex_; | |||||
| 603 | std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_; | 645 | std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_; | |||||
| 604 | 646 | |||||||
| 605 | public: | 647 | public: | |||||
| HITCBC | 606 | 75 | explicit run_wrapper_ex(Ex ex) | 648 | 82 | explicit run_wrapper_ex(Ex ex) | ||
| 607 | requires InheritStopToken | 649 | requires InheritStopToken | |||||
| HITCBC | 608 | 75 | : ex_(std::move(ex)) | 650 | 82 | : ex_(std::move(ex)) | ||
| 609 | { | 651 | { | |||||
| HITCBC | 610 | 75 | } | 652 | 82 | } | ||
| 611 | 653 | |||||||
| HITCBC | 612 | 5 | run_wrapper_ex(Ex ex, std::stop_token st) | 654 | 6 | run_wrapper_ex(Ex ex, std::stop_token st) | ||
| 613 | requires (!InheritStopToken) | 655 | requires (!InheritStopToken) | |||||
| HITCBC | 614 | 5 | : ex_(std::move(ex)) | 656 | 6 | : ex_(std::move(ex)) | ||
| HITCBC | 615 | 5 | , st_(std::move(st)) | 657 | 6 | , st_(std::move(st)) | ||
| 616 | { | 658 | { | |||||
| HITCBC | 617 | 5 | } | 659 | 6 | } | ||
| 618 | 660 | |||||||
| 619 | // Non-copyable, non-movable (must be used immediately) | 661 | // Non-copyable, non-movable (must be used immediately) | |||||
| 620 | run_wrapper_ex(run_wrapper_ex const&) = delete; | 662 | run_wrapper_ex(run_wrapper_ex const&) = delete; | |||||
| 621 | run_wrapper_ex(run_wrapper_ex&&) = delete; | 663 | run_wrapper_ex(run_wrapper_ex&&) = delete; | |||||
| 622 | run_wrapper_ex& operator=(run_wrapper_ex const&) = delete; | 664 | run_wrapper_ex& operator=(run_wrapper_ex const&) = delete; | |||||
| 623 | run_wrapper_ex& operator=(run_wrapper_ex&&) = delete; | 665 | run_wrapper_ex& operator=(run_wrapper_ex&&) = delete; | |||||
| 624 | 666 | |||||||
| 625 | template<ContextRunnable Task> | 667 | template<ContextRunnable Task> | |||||
| HITCBC | 626 | 80 | [[nodiscard]] auto operator()(Task t) && | 668 | 88 | [[nodiscard]] auto operator()(Task t) && | ||
| 627 | { | 669 | { | |||||
| 628 | if constexpr (InheritStopToken) | 670 | if constexpr (InheritStopToken) | |||||
| 629 | return run_awaitable_ex<Task, Ex, true>{ | 671 | return run_awaitable_ex<Task, Ex, true>{ | |||||
| HITCBC | 630 | 75 | std::move(ex_), std::move(t)}; | 672 | 82 | std::move(ex_), std::move(t)}; | ||
| 631 | else | 673 | else | |||||
| 632 | return run_awaitable_ex<Task, Ex, false>{ | 674 | return run_awaitable_ex<Task, Ex, false>{ | |||||
| HITCBC | 633 | 5 | std::move(ex_), std::move(t), std::move(st_)}; | 675 | 6 | std::move(ex_), std::move(t), std::move(st_)}; | ||
| 634 | } | 676 | } | |||||
| 635 | }; | 677 | }; | |||||
| 636 | 678 | |||||||
| 637 | /** Wrapper returned by run(st) or run(alloc) that accepts a task. | 679 | /** Wrapper returned by run(st) or run(alloc) that accepts a task. | |||||
| 638 | 680 | |||||||
| 639 | @tparam InheritStopToken If true, inherit caller's stop token. | 681 | @tparam InheritStopToken If true, inherit caller's stop token. | |||||
| 640 | @tparam Alloc The allocator type (void for no allocator). | 682 | @tparam Alloc The allocator type (void for no allocator). | |||||
| 641 | */ | 683 | */ | |||||
| 642 | template<bool InheritStopToken, class Alloc> | 684 | template<bool InheritStopToken, class Alloc> | |||||
| 643 | class [[nodiscard]] run_wrapper | 685 | class [[nodiscard]] run_wrapper | |||||
| 644 | { | 686 | { | |||||
| 645 | std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_; | 687 | std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_; | |||||
| 646 | frame_memory_resource<Alloc> resource_; | 688 | frame_memory_resource<Alloc> resource_; | |||||
| 647 | Alloc alloc_; // Copy to pass to awaitable | 689 | Alloc alloc_; // Copy to pass to awaitable | |||||
| 648 | 690 | |||||||
| 649 | std::pmr::memory_resource* saved_tls_ = | 691 | std::pmr::memory_resource* saved_tls_ = | |||||
| 650 | get_current_frame_allocator(); | 692 | get_current_frame_allocator(); | |||||
| 651 | bool restored_ = false; | 693 | bool restored_ = false; | |||||
| 652 | 694 | |||||||
| 653 | public: | 695 | public: | |||||
| HITCBC | 654 | 3 | explicit run_wrapper(Alloc alloc) | 696 | 3 | explicit run_wrapper(Alloc alloc) | ||
| 655 | requires InheritStopToken | 697 | requires InheritStopToken | |||||
| HITCBC | 656 | 3 | : resource_(alloc) | 698 | 3 | : resource_(alloc) | ||
| HITCBC | 657 | 3 | , alloc_(std::move(alloc)) | 699 | 3 | , alloc_(std::move(alloc)) | ||
| 658 | { | 700 | { | |||||
| HITCBC | 659 | 3 | set_current_frame_allocator(&resource_); | 701 | 3 | set_current_frame_allocator(&resource_); | ||
| HITCBC | 660 | 3 | } | 702 | 3 | } | ||
| 661 | 703 | |||||||
| HITCBC | 662 | 1 | run_wrapper(std::stop_token st, Alloc alloc) | 704 | 1 | run_wrapper(std::stop_token st, Alloc alloc) | ||
| 663 | requires (!InheritStopToken) | 705 | requires (!InheritStopToken) | |||||
| HITCBC | 664 | 1 | : st_(std::move(st)) | 706 | 1 | : st_(std::move(st)) | ||
| HITCBC | 665 | 1 | , resource_(alloc) | 707 | 1 | , resource_(alloc) | ||
| HITCBC | 666 | 1 | , alloc_(std::move(alloc)) | 708 | 1 | , alloc_(std::move(alloc)) | ||
| 667 | { | 709 | { | |||||
| HITCBC | 668 | 1 | set_current_frame_allocator(&resource_); | 710 | 1 | set_current_frame_allocator(&resource_); | ||
| HITCBC | 669 | 1 | } | 711 | 1 | } | ||
| 670 | 712 | |||||||
| 671 | // Restores the caller's allocator when evaluating the task argument | 713 | // Restores the caller's allocator when evaluating the task argument | |||||
| 672 | // threw. Not afterwards: a wrapper destroyed with a torn-down parent | 714 | // threw. Not afterwards: a wrapper destroyed with a torn-down parent | |||||
| 673 | // frame must not touch the tearing thread's allocator. | 715 | // frame must not touch the tearing thread's allocator. | |||||
| HITCBC | 674 | 4 | ~run_wrapper() | 716 | 4 | ~run_wrapper() | ||
| 675 | { | 717 | { | |||||
| HITCBC | 676 | 4 | if(!restored_) | 718 | 4 | if(!restored_) | ||
| HITCBC | 677 | 1 | set_current_frame_allocator(saved_tls_); | 719 | 1 | set_current_frame_allocator(saved_tls_); | ||
| HITCBC | 678 | 4 | } | 720 | 4 | } | ||
| 679 | 721 | |||||||
| 680 | // Non-copyable, non-movable (must be used immediately) | 722 | // Non-copyable, non-movable (must be used immediately) | |||||
| 681 | run_wrapper(run_wrapper const&) = delete; | 723 | run_wrapper(run_wrapper const&) = delete; | |||||
| 682 | run_wrapper(run_wrapper&&) = delete; | 724 | run_wrapper(run_wrapper&&) = delete; | |||||
| 683 | run_wrapper& operator=(run_wrapper const&) = delete; | 725 | run_wrapper& operator=(run_wrapper const&) = delete; | |||||
| 684 | run_wrapper& operator=(run_wrapper&&) = delete; | 726 | run_wrapper& operator=(run_wrapper&&) = delete; | |||||
| 685 | 727 | |||||||
| 686 | template<ContextRunnable Task> | 728 | template<ContextRunnable Task> | |||||
| HITCBC | 687 | 3 | [[nodiscard]] auto operator()(Task t) && | 729 | 3 | [[nodiscard]] auto operator()(Task t) && | ||
| 688 | { | 730 | { | |||||
| 689 | // The task's frame is made; the caller's own frames are not. | 731 | // The task's frame is made; the caller's own frames are not. | |||||
| HITCBC | 690 | 3 | set_current_frame_allocator(saved_tls_); | 732 | 3 | set_current_frame_allocator(saved_tls_); | ||
| HITCBC | 691 | 3 | restored_ = true; | 733 | 3 | restored_ = true; | ||
| 692 | if constexpr (InheritStopToken) | 734 | if constexpr (InheritStopToken) | |||||
| 693 | return run_awaitable<Task, true, Alloc>{ | 735 | return run_awaitable<Task, true, Alloc>{ | |||||
| HITCBC | 694 | 2 | std::move(alloc_), std::move(t)}; | 736 | 2 | std::move(alloc_), std::move(t)}; | ||
| 695 | else | 737 | else | |||||
| 696 | return run_awaitable<Task, false, Alloc>{ | 738 | return run_awaitable<Task, false, Alloc>{ | |||||
| HITCBC | 697 | 1 | std::move(alloc_), std::move(t), std::move(st_)}; | 739 | 1 | std::move(alloc_), std::move(t), std::move(st_)}; | ||
| 698 | } | 740 | } | |||||
| 699 | }; | 741 | }; | |||||
| 700 | 742 | |||||||
| 701 | /// Specialization for memory_resource* - stores pointer directly. | 743 | /// Specialization for memory_resource* - stores pointer directly. | |||||
| 702 | template<bool InheritStopToken> | 744 | template<bool InheritStopToken> | |||||
| 703 | class [[nodiscard]] run_wrapper<InheritStopToken, std::pmr::memory_resource*> | 745 | class [[nodiscard]] run_wrapper<InheritStopToken, std::pmr::memory_resource*> | |||||
| 704 | { | 746 | { | |||||
| 705 | std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_; | 747 | std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_; | |||||
| 706 | std::pmr::memory_resource* mr_; | 748 | std::pmr::memory_resource* mr_; | |||||
| 707 | 749 | |||||||
| 708 | std::pmr::memory_resource* saved_tls_ = | 750 | std::pmr::memory_resource* saved_tls_ = | |||||
| 709 | get_current_frame_allocator(); | 751 | get_current_frame_allocator(); | |||||
| 710 | bool restored_ = false; | 752 | bool restored_ = false; | |||||
| 711 | 753 | |||||||
| 712 | public: | 754 | public: | |||||
| HITCBC | 713 | 13 | explicit run_wrapper(std::pmr::memory_resource* mr) | 755 | 13 | explicit run_wrapper(std::pmr::memory_resource* mr) | ||
| 714 | requires InheritStopToken | 756 | requires InheritStopToken | |||||
| HITCBC | 715 | 13 | : mr_(mr) | 757 | 13 | : mr_(mr) | ||
| 716 | { | 758 | { | |||||
| HITCBC | 717 | 13 | set_current_frame_allocator(mr_); | 759 | 13 | set_current_frame_allocator(mr_); | ||
| HITCBC | 718 | 13 | } | 760 | 13 | } | ||
| 719 | 761 | |||||||
| HITCBC | 720 | 1 | run_wrapper(std::stop_token st, std::pmr::memory_resource* mr) | 762 | 1 | run_wrapper(std::stop_token st, std::pmr::memory_resource* mr) | ||
| 721 | requires (!InheritStopToken) | 763 | requires (!InheritStopToken) | |||||
| HITCBC | 722 | 1 | : st_(std::move(st)) | 764 | 1 | : st_(std::move(st)) | ||
| HITCBC | 723 | 1 | , mr_(mr) | 765 | 1 | , mr_(mr) | ||
| 724 | { | 766 | { | |||||
| HITCBC | 725 | 1 | set_current_frame_allocator(mr_); | 767 | 1 | set_current_frame_allocator(mr_); | ||
| HITCBC | 726 | 1 | } | 768 | 1 | } | ||
| 727 | 769 | |||||||
| 728 | // Restores the caller's allocator when evaluating the task argument | 770 | // Restores the caller's allocator when evaluating the task argument | |||||
| 729 | // threw. Not afterwards: a wrapper destroyed with a torn-down parent | 771 | // threw. Not afterwards: a wrapper destroyed with a torn-down parent | |||||
| 730 | // frame must not touch the tearing thread's allocator. | 772 | // frame must not touch the tearing thread's allocator. | |||||
| HITCBC | 731 | 14 | ~run_wrapper() | 773 | 14 | ~run_wrapper() | ||
| 732 | { | 774 | { | |||||
| HITCBC | 733 | 14 | if(!restored_) | 775 | 14 | if(!restored_) | ||
| HITCBC | 734 | 1 | set_current_frame_allocator(saved_tls_); | 776 | 1 | set_current_frame_allocator(saved_tls_); | ||
| HITCBC | 735 | 14 | } | 777 | 14 | } | ||
| 736 | 778 | |||||||
| 737 | // Non-copyable, non-movable (must be used immediately) | 779 | // Non-copyable, non-movable (must be used immediately) | |||||
| 738 | run_wrapper(run_wrapper const&) = delete; | 780 | run_wrapper(run_wrapper const&) = delete; | |||||
| 739 | run_wrapper(run_wrapper&&) = delete; | 781 | run_wrapper(run_wrapper&&) = delete; | |||||
| 740 | run_wrapper& operator=(run_wrapper const&) = delete; | 782 | run_wrapper& operator=(run_wrapper const&) = delete; | |||||
| 741 | run_wrapper& operator=(run_wrapper&&) = delete; | 783 | run_wrapper& operator=(run_wrapper&&) = delete; | |||||
| 742 | 784 | |||||||
| 743 | template<ContextRunnable Task> | 785 | template<ContextRunnable Task> | |||||
| HITCBC | 744 | 13 | [[nodiscard]] auto operator()(Task t) && | 786 | 13 | [[nodiscard]] auto operator()(Task t) && | ||
| 745 | { | 787 | { | |||||
| 746 | // The task's frame is made; the caller's own frames are not. | 788 | // The task's frame is made; the caller's own frames are not. | |||||
| HITCBC | 747 | 13 | set_current_frame_allocator(saved_tls_); | 789 | 13 | set_current_frame_allocator(saved_tls_); | ||
| HITCBC | 748 | 13 | restored_ = true; | 790 | 13 | restored_ = true; | ||
| 749 | if constexpr (InheritStopToken) | 791 | if constexpr (InheritStopToken) | |||||
| 750 | return run_awaitable<Task, true, std::pmr::memory_resource*>{ | 792 | return run_awaitable<Task, true, std::pmr::memory_resource*>{ | |||||
| HITCBC | 751 | 12 | mr_, std::move(t)}; | 793 | 12 | mr_, std::move(t)}; | ||
| 752 | else | 794 | else | |||||
| 753 | return run_awaitable<Task, false, std::pmr::memory_resource*>{ | 795 | return run_awaitable<Task, false, std::pmr::memory_resource*>{ | |||||
| HITCBC | 754 | 1 | mr_, std::move(t), std::move(st_)}; | 796 | 1 | mr_, std::move(t), std::move(st_)}; | ||
| 755 | } | 797 | } | |||||
| 756 | }; | 798 | }; | |||||
| 757 | 799 | |||||||
| 758 | /// Specialization for stop_token only (no allocator). | 800 | /// Specialization for stop_token only (no allocator). | |||||
| 759 | template<> | 801 | template<> | |||||
| 760 | class [[nodiscard]] run_wrapper<false, void> | 802 | class [[nodiscard]] run_wrapper<false, void> | |||||
| 761 | { | 803 | { | |||||
| 762 | std::stop_token st_; | 804 | std::stop_token st_; | |||||
| 763 | 805 | |||||||
| 764 | public: | 806 | public: | |||||
| HITCBC | 765 | 2 | explicit run_wrapper(std::stop_token st) | 807 | 5 | explicit run_wrapper(std::stop_token st) | ||
| HITCBC | 766 | 2 | : st_(std::move(st)) | 808 | 5 | : st_(std::move(st)) | ||
| 767 | { | 809 | { | |||||
| HITCBC | 768 | 2 | } | 810 | 5 | } | ||
| 769 | 811 | |||||||
| 770 | // Non-copyable, non-movable (must be used immediately) | 812 | // Non-copyable, non-movable (must be used immediately) | |||||
| 771 | run_wrapper(run_wrapper const&) = delete; | 813 | run_wrapper(run_wrapper const&) = delete; | |||||
| 772 | run_wrapper(run_wrapper&&) = delete; | 814 | run_wrapper(run_wrapper&&) = delete; | |||||
| 773 | run_wrapper& operator=(run_wrapper const&) = delete; | 815 | run_wrapper& operator=(run_wrapper const&) = delete; | |||||
| 774 | run_wrapper& operator=(run_wrapper&&) = delete; | 816 | run_wrapper& operator=(run_wrapper&&) = delete; | |||||
| 775 | 817 | |||||||
| 776 | template<ContextRunnable Task> | 818 | template<ContextRunnable Task> | |||||
| HITCBC | 777 | 2 | [[nodiscard]] auto operator()(Task t) && | 819 | 5 | [[nodiscard]] auto operator()(Task t) && | ||
| 778 | { | 820 | { | |||||
| HITCBC | 779 | 2 | return run_awaitable<Task, false, void>{std::move(t), std::move(st_)}; | 821 | 5 | return run_awaitable<Task, false, void>{std::move(t), std::move(st_)}; | ||
| 780 | } | 822 | } | |||||
| 781 | }; | 823 | }; | |||||
| 782 | 824 | |||||||
| 783 | } // namespace boost::capy::detail | 825 | } // namespace boost::capy::detail | |||||
| 784 | 826 | |||||||
| 785 | namespace boost::capy { | 827 | namespace boost::capy { | |||||
| 786 | 828 | |||||||
| 787 | /** Bind a task to execute on a specific executor. | 829 | /** Bind a task to execute on a specific executor. | |||||
| 788 | 830 | |||||||
| 789 | Returns a wrapper that accepts a task and produces an awaitable. | 831 | Returns a wrapper that accepts a task and produces an awaitable. | |||||
| 790 | When co_awaited, the task runs on the specified executor. | 832 | When co_awaited, the task runs on the specified executor. | |||||
| 791 | 833 | |||||||
| 792 | If the inner task is abandoned before it completes, for example | 834 | If the inner task is abandoned before it completes, for example | |||||
| 793 | because its execution context is torn down, the caller's chain is | 835 | because its execution context is torn down, the caller's chain is | |||||
| 794 | abandoned too: it is unwound by its owner, on the owner's executor, | 836 | abandoned too: it is unwound by its owner, on the owner's executor, | |||||
| 795 | without the caller resuming. The awaitable holds work on the | 837 | without the caller resuming. The awaitable holds work on the | |||||
| 796 | caller's executor until the caller resumes or is unwound. | 838 | caller's executor until the caller resumes or is unwound. | |||||
| 839 | + | |||||||
| 840 | + | A @ref quitter's stop is not such an abandon. When the inner task | ||||||
| 841 | + | is a quitter and a stop ends it, the caller resumes on its own | ||||||
| 842 | + | executor, and the `co_await` yields the quitter's stopped value, as | ||||||
| 843 | + | described in @ref quitter's Await-returns. | ||||||
| 797 | 844 | |||||||
| 798 | @par Preconditions | 845 | @par Preconditions | |||||
| 799 | The caller's execution context outlives the inner task and the | 846 | The caller's execution context outlives the inner task and the | |||||
| 800 | unwind of the caller's chain. This is not detected. | 847 | unwind of the caller's chain. This is not detected. | |||||
| 801 | 848 | |||||||
| 802 | @par Example | 849 | @par Example | |||||
| 803 | @par !example example_2 | 850 | @par !example example_2 | |||||
| 804 | 851 | |||||||
| 805 | 852 | |||||||
| 806 | @param ex The executor on which the task should run. | 853 | @param ex The executor on which the task should run. | |||||
| 807 | 854 | |||||||
| 808 | @return A wrapper that accepts a task for execution. | 855 | @return A wrapper that accepts a task for execution. | |||||
| 809 | 856 | |||||||
| 810 | @see @ref task | 857 | @see @ref task | |||||
| 811 | @see @ref Executor | 858 | @see @ref Executor | |||||
| 812 | */ | 859 | */ | |||||
| 813 | template<Executor Ex> | 860 | template<Executor Ex> | |||||
| 814 | [[nodiscard]] auto | 861 | [[nodiscard]] auto | |||||
| HITCBC | 815 | 75 | run(Ex ex) | 862 | 82 | run(Ex ex) | ||
| 816 | { | 863 | { | |||||
| HITCBC | 817 | 75 | return detail::run_wrapper_ex<Ex, true, void>{std::move(ex)}; | 864 | 82 | return detail::run_wrapper_ex<Ex, true, void>{std::move(ex)}; | ||
| 818 | } | 865 | } | |||||
| 819 | 866 | |||||||
| 820 | /** Bind a task to an executor with a stop token. | 867 | /** Bind a task to an executor with a stop token. | |||||
| 821 | 868 | |||||||
| 822 | @ref run(Ex) describes teardown and preconditions. | 869 | @ref run(Ex) describes teardown and preconditions. | |||||
| 823 | 870 | |||||||
| 824 | @param ex The executor on which the task should run. | 871 | @param ex The executor on which the task should run. | |||||
| 825 | @param st The stop token for cooperative cancellation. | 872 | @param st The stop token for cooperative cancellation. | |||||
| 826 | 873 | |||||||
| 827 | @return A wrapper that accepts a task for execution. | 874 | @return A wrapper that accepts a task for execution. | |||||
| 828 | 875 | |||||||
| 829 | @see @ref run(Ex) | 876 | @see @ref run(Ex) | |||||
| 830 | */ | 877 | */ | |||||
| 831 | template<Executor Ex> | 878 | template<Executor Ex> | |||||
| 832 | [[nodiscard]] auto | 879 | [[nodiscard]] auto | |||||
| HITCBC | 833 | 5 | run(Ex ex, std::stop_token st) | 880 | 6 | run(Ex ex, std::stop_token st) | ||
| 834 | { | 881 | { | |||||
| 835 | return detail::run_wrapper_ex<Ex, false, void>{ | 882 | return detail::run_wrapper_ex<Ex, false, void>{ | |||||
| HITCBC | 836 | 5 | std::move(ex), std::move(st)}; | 883 | 6 | std::move(ex), std::move(st)}; | ||
| 837 | } | 884 | } | |||||
| 838 | 885 | |||||||
| 839 | /** Bind a task to an executor with a memory resource. | 886 | /** Bind a task to an executor with a memory resource. | |||||
| 840 | 887 | |||||||
| 841 | @ref run(Ex) describes teardown and preconditions. | 888 | @ref run(Ex) describes teardown and preconditions. | |||||
| 842 | 889 | |||||||
| 843 | @param ex The executor on which the task should run. | 890 | @param ex The executor on which the task should run. | |||||
| 844 | @param mr The memory resource for frame allocation. | 891 | @param mr The memory resource for frame allocation. | |||||
| 845 | 892 | |||||||
| 846 | @return A wrapper that accepts a task for execution. | 893 | @return A wrapper that accepts a task for execution. | |||||
| 847 | 894 | |||||||
| 848 | @see @ref run(Ex) | 895 | @see @ref run(Ex) | |||||
| 849 | */ | 896 | */ | |||||
| 850 | template<Executor Ex> | 897 | template<Executor Ex> | |||||
| 851 | [[nodiscard]] auto | 898 | [[nodiscard]] auto | |||||
| HITCBC | 852 | 4 | run(Ex ex, std::pmr::memory_resource* mr) | 899 | 4 | run(Ex ex, std::pmr::memory_resource* mr) | ||
| 853 | { | 900 | { | |||||
| 854 | return detail::run_wrapper_ex<Ex, true, std::pmr::memory_resource*>{ | 901 | return detail::run_wrapper_ex<Ex, true, std::pmr::memory_resource*>{ | |||||
| HITCBC | 855 | 4 | std::move(ex), mr}; | 902 | 4 | std::move(ex), mr}; | ||
| 856 | } | 903 | } | |||||
| 857 | 904 | |||||||
| 858 | /** Bind a task to an executor with a standard allocator. | 905 | /** Bind a task to an executor with a standard allocator. | |||||
| 859 | 906 | |||||||
| 860 | Await the awaitable in the expression that creates it, as in | 907 | Await the awaitable in the expression that creates it, as in | |||||
| 861 | `co_await run(ex, alloc)(task())`: the task's frame is allocated from | 908 | `co_await run(ex, alloc)(task())`: the task's frame is allocated from | |||||
| 862 | memory the wrapper holds until the end of that expression. | 909 | memory the wrapper holds until the end of that expression. | |||||
| 863 | 910 | |||||||
| 864 | @ref run(Ex) describes teardown and preconditions. | 911 | @ref run(Ex) describes teardown and preconditions. | |||||
| 865 | 912 | |||||||
| 866 | @param ex The executor on which the task should run. | 913 | @param ex The executor on which the task should run. | |||||
| 867 | @param alloc The allocator for frame allocation. | 914 | @param alloc The allocator for frame allocation. | |||||
| 868 | 915 | |||||||
| 869 | @return A wrapper that accepts a task for execution. | 916 | @return A wrapper that accepts a task for execution. | |||||
| 870 | 917 | |||||||
| 871 | @see @ref run(Ex) | 918 | @see @ref run(Ex) | |||||
| 872 | */ | 919 | */ | |||||
| 873 | template<Executor Ex, detail::Allocator Alloc> | 920 | template<Executor Ex, detail::Allocator Alloc> | |||||
| 874 | [[nodiscard]] auto | 921 | [[nodiscard]] auto | |||||
| HITCBC | 875 | 3 | run(Ex ex, Alloc alloc) | 922 | 3 | run(Ex ex, Alloc alloc) | ||
| 876 | { | 923 | { | |||||
| 877 | return detail::run_wrapper_ex<Ex, true, Alloc>{ | 924 | return detail::run_wrapper_ex<Ex, true, Alloc>{ | |||||
| HITCBC | 878 | 3 | std::move(ex), std::move(alloc)}; | 925 | 3 | std::move(ex), std::move(alloc)}; | ||
| 879 | } | 926 | } | |||||
| 880 | 927 | |||||||
| 881 | /** Bind a task to an executor with stop token and memory resource. | 928 | /** Bind a task to an executor with stop token and memory resource. | |||||
| 882 | 929 | |||||||
| 883 | @ref run(Ex) describes teardown and preconditions. | 930 | @ref run(Ex) describes teardown and preconditions. | |||||
| 884 | 931 | |||||||
| 885 | @param ex The executor on which the task should run. | 932 | @param ex The executor on which the task should run. | |||||
| 886 | @param st The stop token for cooperative cancellation. | 933 | @param st The stop token for cooperative cancellation. | |||||
| 887 | @param mr The memory resource for frame allocation. | 934 | @param mr The memory resource for frame allocation. | |||||
| 888 | 935 | |||||||
| 889 | @return A wrapper that accepts a task for execution. | 936 | @return A wrapper that accepts a task for execution. | |||||
| 890 | 937 | |||||||
| 891 | @see @ref run(Ex) | 938 | @see @ref run(Ex) | |||||
| 892 | */ | 939 | */ | |||||
| 893 | template<Executor Ex> | 940 | template<Executor Ex> | |||||
| 894 | [[nodiscard]] auto | 941 | [[nodiscard]] auto | |||||
| HITCBC | 895 | 1 | run(Ex ex, std::stop_token st, std::pmr::memory_resource* mr) | 942 | 1 | run(Ex ex, std::stop_token st, std::pmr::memory_resource* mr) | ||
| 896 | { | 943 | { | |||||
| 897 | return detail::run_wrapper_ex<Ex, false, std::pmr::memory_resource*>{ | 944 | return detail::run_wrapper_ex<Ex, false, std::pmr::memory_resource*>{ | |||||
| HITCBC | 898 | 1 | std::move(ex), std::move(st), mr}; | 945 | 1 | std::move(ex), std::move(st), mr}; | ||
| 899 | } | 946 | } | |||||
| 900 | 947 | |||||||
| 901 | /** Bind a task to an executor with stop token and standard allocator. | 948 | /** Bind a task to an executor with stop token and standard allocator. | |||||
| 902 | 949 | |||||||
| 903 | Await the awaitable in the expression that creates it, as in | 950 | Await the awaitable in the expression that creates it, as in | |||||
| 904 | `co_await run(ex, alloc)(task())`: the task's frame is allocated from | 951 | `co_await run(ex, alloc)(task())`: the task's frame is allocated from | |||||
| 905 | memory the wrapper holds until the end of that expression. | 952 | memory the wrapper holds until the end of that expression. | |||||
| 906 | 953 | |||||||
| 907 | @ref run(Ex) describes teardown and preconditions. | 954 | @ref run(Ex) describes teardown and preconditions. | |||||
| 908 | 955 | |||||||
| 909 | @param ex The executor on which the task should run. | 956 | @param ex The executor on which the task should run. | |||||
| 910 | @param st The stop token for cooperative cancellation. | 957 | @param st The stop token for cooperative cancellation. | |||||
| 911 | @param alloc The allocator for frame allocation. | 958 | @param alloc The allocator for frame allocation. | |||||
| 912 | 959 | |||||||
| 913 | @return A wrapper that accepts a task for execution. | 960 | @return A wrapper that accepts a task for execution. | |||||
| 914 | 961 | |||||||
| 915 | @see @ref run(Ex) | 962 | @see @ref run(Ex) | |||||
| 916 | */ | 963 | */ | |||||
| 917 | template<Executor Ex, detail::Allocator Alloc> | 964 | template<Executor Ex, detail::Allocator Alloc> | |||||
| 918 | [[nodiscard]] auto | 965 | [[nodiscard]] auto | |||||
| HITCBC | 919 | 1 | run(Ex ex, std::stop_token st, Alloc alloc) | 966 | 1 | run(Ex ex, std::stop_token st, Alloc alloc) | ||
| 920 | { | 967 | { | |||||
| 921 | return detail::run_wrapper_ex<Ex, false, Alloc>{ | 968 | return detail::run_wrapper_ex<Ex, false, Alloc>{ | |||||
| HITCBC | 922 | 1 | std::move(ex), std::move(st), std::move(alloc)}; | 969 | 1 | std::move(ex), std::move(st), std::move(alloc)}; | ||
| 923 | } | 970 | } | |||||
| 924 | 971 | |||||||
| 925 | /** Run a task with a custom stop token. | 972 | /** Run a task with a custom stop token. | |||||
| 926 | 973 | |||||||
| 927 | The task inherits the caller's executor. Only the stop token | 974 | The task inherits the caller's executor. Only the stop token | |||||
| 928 | is overridden. | 975 | is overridden. | |||||
| 929 | 976 | |||||||
| 930 | @par Example | 977 | @par Example | |||||
| 931 | @par !example example_1 | 978 | @par !example example_1 | |||||
| 932 | 979 | |||||||
| 933 | 980 | |||||||
| 934 | @param st The stop token for cooperative cancellation. | 981 | @param st The stop token for cooperative cancellation. | |||||
| 935 | 982 | |||||||
| 936 | @return A wrapper that accepts a task for execution. | 983 | @return A wrapper that accepts a task for execution. | |||||
| 937 | */ | 984 | */ | |||||
| 938 | [[nodiscard]] inline auto | 985 | [[nodiscard]] inline auto | |||||
| HITCBC | 939 | 2 | run(std::stop_token st) | 986 | 5 | run(std::stop_token st) | ||
| 940 | { | 987 | { | |||||
| HITCBC | 941 | 2 | return detail::run_wrapper<false, void>{std::move(st)}; | 988 | 5 | return detail::run_wrapper<false, void>{std::move(st)}; | ||
| 942 | } | 989 | } | |||||
| 943 | 990 | |||||||
| 944 | /** Run a task with a custom memory resource. | 991 | /** Run a task with a custom memory resource. | |||||
| 945 | 992 | |||||||
| 946 | The task inherits the caller's executor. The memory resource | 993 | The task inherits the caller's executor. The memory resource | |||||
| 947 | is used for nested frame allocations. | 994 | is used for nested frame allocations. | |||||
| 948 | 995 | |||||||
| 949 | @param mr The memory resource for frame allocation. | 996 | @param mr The memory resource for frame allocation. | |||||
| 950 | 997 | |||||||
| 951 | @return A wrapper that accepts a task for execution. | 998 | @return A wrapper that accepts a task for execution. | |||||
| 952 | */ | 999 | */ | |||||
| 953 | [[nodiscard]] inline auto | 1000 | [[nodiscard]] inline auto | |||||
| HITCBC | 954 | 13 | run(std::pmr::memory_resource* mr) | 1001 | 13 | run(std::pmr::memory_resource* mr) | ||
| 955 | { | 1002 | { | |||||
| HITCBC | 956 | 13 | return detail::run_wrapper<true, std::pmr::memory_resource*>{mr}; | 1003 | 13 | return detail::run_wrapper<true, std::pmr::memory_resource*>{mr}; | ||
| 957 | } | 1004 | } | |||||
| 958 | 1005 | |||||||
| 959 | // MrDocs 0.8.0 reports `run: Documented parameter 'alloc' does not exist` and | 1006 | // MrDocs 0.8.0 reports `run: Documented parameter 'alloc' does not exist` and | |||||
| 960 | // pins it to the run(Ex) overload near the top of this namespace: | 1007 | // pins it to the run(Ex) overload near the top of this namespace: | |||||
| 961 | // `detail::Allocator` is implementation-defined, so the constraint is erased | 1008 | // `detail::Allocator` is implementation-defined, so the constraint is erased | |||||
| 962 | // and the two single-parameter `run` overloads collapse onto one page. The | 1009 | // and the two single-parameter `run` overloads collapse onto one page. The | |||||
| 963 | // finding is grandfathered in doc/lint/baseline.json. Deleting the `@param | 1010 | // finding is grandfathered in doc/lint/baseline.json. Deleting the `@param | |||||
| 964 | // alloc` below clears it and produces no replacement finding, because MrDocs | 1011 | // alloc` below clears it and produces no replacement finding, because MrDocs | |||||
| 965 | // has no model of this overload at all -- so it would silently drop real | 1012 | // has no model of this overload at all -- so it would silently drop real | |||||
| 966 | // documentation. Keep the `@param`. | 1013 | // documentation. Keep the `@param`. | |||||
| 967 | /** Run a task with a custom standard allocator. | 1014 | /** Run a task with a custom standard allocator. | |||||
| 968 | 1015 | |||||||
| 969 | Await the awaitable in the expression that creates it, as in | 1016 | Await the awaitable in the expression that creates it, as in | |||||
| 970 | `co_await run(alloc)(task())`: the task's frame is allocated from | 1017 | `co_await run(alloc)(task())`: the task's frame is allocated from | |||||
| 971 | memory the wrapper holds until the end of that expression. | 1018 | memory the wrapper holds until the end of that expression. | |||||
| 972 | 1019 | |||||||
| 973 | The task inherits the caller's executor. The allocator is used | 1020 | The task inherits the caller's executor. The allocator is used | |||||
| 974 | for nested frame allocations. | 1021 | for nested frame allocations. | |||||
| 975 | 1022 | |||||||
| 976 | @param alloc The allocator for frame allocation. | 1023 | @param alloc The allocator for frame allocation. | |||||
| 977 | 1024 | |||||||
| 978 | @return A wrapper that accepts a task for execution. | 1025 | @return A wrapper that accepts a task for execution. | |||||
| 979 | */ | 1026 | */ | |||||
| 980 | template<detail::Allocator Alloc> | 1027 | template<detail::Allocator Alloc> | |||||
| 981 | [[nodiscard]] auto | 1028 | [[nodiscard]] auto | |||||
| HITCBC | 982 | 3 | run(Alloc alloc) | 1029 | 3 | run(Alloc alloc) | ||
| 983 | { | 1030 | { | |||||
| HITCBC | 984 | 3 | return detail::run_wrapper<true, Alloc>{std::move(alloc)}; | 1031 | 3 | return detail::run_wrapper<true, Alloc>{std::move(alloc)}; | ||
| 985 | } | 1032 | } | |||||
| 986 | 1033 | |||||||
| 987 | /** Run a task with stop token and memory resource. | 1034 | /** Run a task with stop token and memory resource. | |||||
| 988 | 1035 | |||||||
| 989 | The task inherits the caller's executor. | 1036 | The task inherits the caller's executor. | |||||
| 990 | 1037 | |||||||
| 991 | @param st The stop token for cooperative cancellation. | 1038 | @param st The stop token for cooperative cancellation. | |||||
| 992 | @param mr The memory resource for frame allocation. | 1039 | @param mr The memory resource for frame allocation. | |||||
| 993 | 1040 | |||||||
| 994 | @return A wrapper that accepts a task for execution. | 1041 | @return A wrapper that accepts a task for execution. | |||||
| 995 | */ | 1042 | */ | |||||
| 996 | [[nodiscard]] inline auto | 1043 | [[nodiscard]] inline auto | |||||
| HITCBC | 997 | 1 | run(std::stop_token st, std::pmr::memory_resource* mr) | 1044 | 1 | run(std::stop_token st, std::pmr::memory_resource* mr) | ||
| 998 | { | 1045 | { | |||||
| 999 | return detail::run_wrapper<false, std::pmr::memory_resource*>{ | 1046 | return detail::run_wrapper<false, std::pmr::memory_resource*>{ | |||||
| HITCBC | 1000 | 1 | std::move(st), mr}; | 1047 | 1 | std::move(st), mr}; | ||
| 1001 | } | 1048 | } | |||||
| 1002 | 1049 | |||||||
| 1003 | /** Run a task with stop token and standard allocator. | 1050 | /** Run a task with stop token and standard allocator. | |||||
| 1004 | 1051 | |||||||
| 1005 | Await the awaitable in the expression that creates it, as in | 1052 | Await the awaitable in the expression that creates it, as in | |||||
| 1006 | `co_await run(alloc)(task())`: the task's frame is allocated from | 1053 | `co_await run(alloc)(task())`: the task's frame is allocated from | |||||
| 1007 | memory the wrapper holds until the end of that expression. | 1054 | memory the wrapper holds until the end of that expression. | |||||
| 1008 | 1055 | |||||||
| 1009 | The task inherits the caller's executor. | 1056 | The task inherits the caller's executor. | |||||
| 1010 | 1057 | |||||||
| 1011 | @param st The stop token for cooperative cancellation. | 1058 | @param st The stop token for cooperative cancellation. | |||||
| 1012 | @param alloc The allocator for frame allocation. | 1059 | @param alloc The allocator for frame allocation. | |||||
| 1013 | 1060 | |||||||
| 1014 | @return A wrapper that accepts a task for execution. | 1061 | @return A wrapper that accepts a task for execution. | |||||
| 1015 | */ | 1062 | */ | |||||
| 1016 | template<detail::Allocator Alloc> | 1063 | template<detail::Allocator Alloc> | |||||
| 1017 | [[nodiscard]] auto | 1064 | [[nodiscard]] auto | |||||
| HITCBC | 1018 | 1 | run(std::stop_token st, Alloc alloc) | 1065 | 1 | run(std::stop_token st, Alloc alloc) | ||
| 1019 | { | 1066 | { | |||||
| 1020 | return detail::run_wrapper<false, Alloc>{ | 1067 | return detail::run_wrapper<false, Alloc>{ | |||||
| HITCBC | 1021 | 1 | std::move(st), std::move(alloc)}; | 1068 | 1 | std::move(st), std::move(alloc)}; | ||
| 1022 | } | 1069 | } | |||||
| 1023 | 1070 | |||||||
| 1024 | } // namespace boost::capy | 1071 | } // namespace boost::capy | |||||
| 1025 | 1072 | |||||||
| 1026 | #endif | 1073 | #endif | |||||