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