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