99.47% Lines (189/190) 98.21% Functions (55/56)
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/run.hpp> 15   #include <boost/capy/detail/run.hpp>
15   #include <boost/capy/concept/executor.hpp> 16   #include <boost/capy/concept/executor.hpp>
16   #include <boost/capy/concept/io_runnable.hpp> 17   #include <boost/capy/concept/io_runnable.hpp>
17   #include <boost/capy/ex/executor_ref.hpp> 18   #include <boost/capy/ex/executor_ref.hpp>
18   #include <coroutine> 19   #include <coroutine>
  20 + #include <boost/capy/ex/frame_alloc_mixin.hpp>
19   #include <boost/capy/ex/frame_allocator.hpp> 21   #include <boost/capy/ex/frame_allocator.hpp>
20   #include <boost/capy/ex/io_env.hpp> 22   #include <boost/capy/ex/io_env.hpp>
21   23  
22   #include <memory_resource> 24   #include <memory_resource>
23   #include <stop_token> 25   #include <stop_token>
24   #include <type_traits> 26   #include <type_traits>
25   #include <utility> 27   #include <utility>
26   #include <variant> 28   #include <variant>
27   29  
28   /* 30   /*
29   Allocator Lifetime Strategy 31   Allocator Lifetime Strategy
30   =========================== 32   ===========================
31   33  
32   When using run() with a custom allocator: 34   When using run() with a custom allocator:
33   35  
34   co_await run(ex, alloc)(my_task()); 36   co_await run(ex, alloc)(my_task());
35   37  
36   The evaluation order is: 38   The evaluation order is:
37   1. run(ex, alloc) creates a temporary wrapper 39   1. run(ex, alloc) creates a temporary wrapper
38   2. my_task() allocates its coroutine frame using TLS 40   2. my_task() allocates its coroutine frame using TLS
39   3. operator() returns an awaitable 41   3. operator() returns an awaitable
40   4. Wrapper temporary is DESTROYED 42   4. Wrapper temporary is DESTROYED
41   5. co_await suspends caller, resumes task 43   5. co_await suspends caller, resumes task
42   6. Task body executes (wrapper is already dead!) 44   6. Task body executes (wrapper is already dead!)
43   45  
44   Problem: The wrapper's frame_memory_resource dies before the task 46   Problem: The wrapper's frame_memory_resource dies before the task
45   body runs. When initial_suspend::await_resume() restores TLS from 47   body runs. When initial_suspend::await_resume() restores TLS from
46   the saved pointer, it would point to dead memory. 48   the saved pointer, it would point to dead memory.
47   49  
48   Solution: Store a COPY of the allocator in the awaitable (not just 50   Solution: Store a COPY of the allocator in the awaitable (not just
49   the wrapper). The co_await mechanism extends the awaitable's lifetime 51   the wrapper). The co_await mechanism extends the awaitable's lifetime
50   until the await completes. In await_suspend, we overwrite the promise's 52   until the await completes. In await_suspend, we overwrite the promise's
51   saved frame_allocator pointer to point to the awaitable's resource. 53   saved frame_allocator pointer to point to the awaitable's resource.
52   54  
53   This works because standard allocator copies are equivalent - memory 55   This works because standard allocator copies are equivalent - memory
54   allocated with one copy can be deallocated with another copy. The 56   allocated with one copy can be deallocated with another copy. The
55   task's own frame uses the footer-stored pointer (safe), while nested 57   task's own frame uses the footer-stored pointer (safe), while nested
56   task creation uses TLS pointing to the awaitable's resource (also safe). 58   task creation uses TLS pointing to the awaitable's resource (also safe).
57   */ 59   */
58   60  
59   namespace boost::capy::detail { 61   namespace boost::capy::detail {
60 - //----------------------------------------------------------  
61 - //  
62 - // dispatch_trampoline - cross-executor dispatch  
63 - //  
64 - //----------------------------------------------------------  
65 -  
66   62  
67   /** Minimal coroutine that dispatches through the caller's executor. 63   /** Minimal coroutine that dispatches through the caller's executor.
68   64  
69   Sits between the inner task and the parent when executors 65   Sits between the inner task and the parent when executors
70   diverge. The inner task's `final_suspend` resumes this 66   diverge. The inner task's `final_suspend` resumes this
71   trampoline via symmetric transfer. The trampoline's own 67   trampoline via symmetric transfer. The trampoline's own
72   `final_suspend` dispatches the parent through the caller's 68   `final_suspend` dispatches the parent through the caller's
73   executor to restore the correct execution context. 69   executor to restore the correct execution context.
74   70  
75   The trampoline never touches the task's result. 71   The trampoline never touches the task's result.
76   */ 72   */
77 - struct dispatch_trampoline 73 + struct BOOST_CAPY_CORO_DESTROY_WHEN_COMPLETE boundary_trampoline
78   { 74   {
79   struct promise_type 75   struct promise_type
  76 + : frame_alloc_mixin
80   { 77   {
81   executor_ref caller_ex_; 78   executor_ref caller_ex_;
82 - std::coroutine_handle<> parent_; 79 + continuation parent_;
83   80  
HITCBC 84 - 9 dispatch_trampoline get_return_object() noexcept 81 + 18 boundary_trampoline get_return_object() noexcept
85   { 82   {
86 - return dispatch_trampoline{ 83 + return boundary_trampoline{
HITCBC 87   9 std::coroutine_handle<promise_type>::from_promise(*this)}; 84   18 std::coroutine_handle<promise_type>::from_promise(*this)};
88   } 85   }
89   86  
HITCBC 90   9 std::suspend_always initial_suspend() noexcept { return {}; } 87   18 std::suspend_always initial_suspend() noexcept { return {}; }
91   88  
HITCBC 92   9 auto final_suspend() noexcept 89   18 auto final_suspend() noexcept
93   { 90   {
94   struct awaiter 91   struct awaiter
95   { 92   {
96   promise_type* p_; 93   promise_type* p_;
HITCBC 97   9 bool await_ready() const noexcept { return false; } 94   18 bool await_ready() const noexcept { return false; }
98   95  
HITCBC 99 - 9 std::coroutine_handle<> await_suspend( 96 + 18 auto await_suspend(
100   std::coroutine_handle<>) noexcept 97   std::coroutine_handle<>) noexcept
101   { 98   {
HITCBC 102 - 9 return p_->caller_ex_.dispatch(p_->parent_); 99 + 18 p_->caller_ex_.post(p_->parent_);
HITGNC   100 + 18 return detail::symmetric_transfer(
HITGNC   101 + 36 std::noop_coroutine());
103   } 102   }
104   103  
MISUBC 105   void await_resume() const noexcept {} 104   void await_resume() const noexcept {}
106   }; 105   };
HITCBC 107   9 return awaiter{this}; 106   18 return awaiter{this};
108   } 107   }
109   108  
HITCBC 110   9 void return_void() noexcept {} 109   18 void return_void() noexcept {}
111   void unhandled_exception() noexcept {} 110   void unhandled_exception() noexcept {}
112   }; 111   };
113   112  
114   std::coroutine_handle<promise_type> h_{nullptr}; 113   std::coroutine_handle<promise_type> h_{nullptr};
115   114  
HITCBC 116 - 9 dispatch_trampoline() noexcept = default; 115 + 18 boundary_trampoline() noexcept = default;
117   116  
HITCBC 118 - 27 ~dispatch_trampoline() 117 + 54 ~boundary_trampoline()
119   { 118   {
HITCBC 120   27 if(h_) h_.destroy(); 119   54 if(h_) h_.destroy();
HITCBC 121   27 } 120   54 }
122   121  
123 - dispatch_trampoline(dispatch_trampoline const&) = delete; 122 + boundary_trampoline(boundary_trampoline const&) = delete;
124 - dispatch_trampoline& operator=(dispatch_trampoline const&) = delete; 123 + boundary_trampoline& operator=(boundary_trampoline const&) = delete;
125   124  
HITCBC 126 - 9 dispatch_trampoline(dispatch_trampoline&& o) noexcept 125 + 18 boundary_trampoline(boundary_trampoline&& o) noexcept
HITCBC 127   9 : h_(std::exchange(o.h_, nullptr)) {} 126   18 : h_(std::exchange(o.h_, nullptr)) {}
128   127  
HITCBC 129 - 9 dispatch_trampoline& operator=(dispatch_trampoline&& o) noexcept 128 + 18 boundary_trampoline& operator=(boundary_trampoline&& o) noexcept
130   { 129   {
HITCBC 131   9 if(this != &o) 130   18 if(this != &o)
132   { 131   {
HITCBC 133   9 if(h_) h_.destroy(); 132   18 if(h_) h_.destroy();
HITCBC 134   9 h_ = std::exchange(o.h_, nullptr); 133   18 h_ = std::exchange(o.h_, nullptr);
135   } 134   }
HITCBC 136   9 return *this; 135   18 return *this;
137   } 136   }
138   137  
139   private: 138   private:
HITCBC 140 - 9 explicit dispatch_trampoline(std::coroutine_handle<promise_type> h) noexcept 139 + 18 explicit boundary_trampoline(std::coroutine_handle<promise_type> h) noexcept
HITCBC 141   9 : h_(h) {} 140   18 : h_(h) {}
142   }; 141   };
143   142  
HITCBC 144 - 9 inline dispatch_trampoline make_dispatch_trampoline() 143 + 18 inline boundary_trampoline make_boundary_trampoline()
145   { 144   {
146   co_return; 145   co_return;
HITCBC 147   18 } 146   36 }
148 - //----------------------------------------------------------  
149 - //  
150 - // run_awaitable_ex - with executor (executor switch)  
151 - //  
152 - //----------------------------------------------------------  
153 -  
154   147  
155   /** Awaitable that binds an IoRunnable to a specific executor. 148   /** Awaitable that binds an IoRunnable to a specific executor.
156   149  
157   Stores the executor and inner task by value. When co_awaited, the 150   Stores the executor and inner task by value. When co_awaited, the
158   co_await expression's lifetime extension keeps both alive for the 151   co_await expression's lifetime extension keeps both alive for the
159   duration of the operation. 152   duration of the operation.
160   153  
161   A dispatch trampoline handles the executor switch on completion: 154   A dispatch trampoline handles the executor switch on completion:
162   the inner task's `final_suspend` resumes the trampoline, which 155   the inner task's `final_suspend` resumes the trampoline, which
163   dispatches back through the caller's executor. 156   dispatches back through the caller's executor.
164   157  
165   The `io_env` is owned by this awaitable and is guaranteed to 158   The `io_env` is owned by this awaitable and is guaranteed to
166   outlive the inner task and all awaitables in its chain. Awaitables 159   outlive the inner task and all awaitables in its chain. Awaitables
167   may store `io_env const*` without concern for dangling references. 160   may store `io_env const*` without concern for dangling references.
168   161  
169   @tparam Task The IoRunnable type 162   @tparam Task The IoRunnable type
170   @tparam Ex The executor type 163   @tparam Ex The executor type
171   @tparam InheritStopToken If true, inherit caller's stop token 164   @tparam InheritStopToken If true, inherit caller's stop token
172   @tparam Alloc The allocator type (void for no allocator) 165   @tparam Alloc The allocator type (void for no allocator)
173   */ 166   */
174   template<IoRunnable Task, Executor Ex, bool InheritStopToken, class Alloc = void> 167   template<IoRunnable Task, Executor Ex, bool InheritStopToken, class Alloc = void>
175   struct [[nodiscard]] run_awaitable_ex 168   struct [[nodiscard]] run_awaitable_ex
176   { 169   {
177   Ex ex_; 170   Ex ex_;
178   frame_memory_resource<Alloc> resource_; 171   frame_memory_resource<Alloc> resource_;
179   std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_; 172   std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_;
180   io_env env_; 173   io_env env_;
181 - dispatch_trampoline tr_; 174 + boundary_trampoline tr_;
  175 + continuation task_cont_;
182   Task inner_; // Last: destroyed first, while env_ is still valid 176   Task inner_; // Last: destroyed first, while env_ is still valid
183   177  
184   // void allocator, inherit stop token 178   // void allocator, inherit stop token
HITCBC 185   3 run_awaitable_ex(Ex ex, Task inner) 179   10 run_awaitable_ex(Ex ex, Task inner)
186   requires (InheritStopToken && std::is_void_v<Alloc>) 180   requires (InheritStopToken && std::is_void_v<Alloc>)
HITCBC 187   3 : ex_(std::move(ex)) 181   10 : ex_(std::move(ex))
HITCBC 188   3 , inner_(std::move(inner)) 182   10 , inner_(std::move(inner))
189   { 183   {
HITCBC 190   3 } 184   10 }
191   185  
192   // void allocator, explicit stop token 186   // void allocator, explicit stop token
HITCBC 193   2 run_awaitable_ex(Ex ex, Task inner, std::stop_token st) 187   4 run_awaitable_ex(Ex ex, Task inner, std::stop_token st)
194   requires (!InheritStopToken && std::is_void_v<Alloc>) 188   requires (!InheritStopToken && std::is_void_v<Alloc>)
HITCBC 195   2 : ex_(std::move(ex)) 189   4 : ex_(std::move(ex))
HITCBC 196   2 , st_(std::move(st)) 190   4 , st_(std::move(st))
HITCBC 197   2 , inner_(std::move(inner)) 191   4 , inner_(std::move(inner))
198   { 192   {
HITCBC 199   2 } 193   4 }
200   194  
201   // with allocator, inherit stop token (use template to avoid void parameter) 195   // with allocator, inherit stop token (use template to avoid void parameter)
202   template<class A> 196   template<class A>
203   requires (InheritStopToken && !std::is_void_v<Alloc> && std::same_as<A, Alloc>) 197   requires (InheritStopToken && !std::is_void_v<Alloc> && std::same_as<A, Alloc>)
HITCBC 204   2 run_awaitable_ex(Ex ex, A alloc, Task inner) 198   2 run_awaitable_ex(Ex ex, A alloc, Task inner)
HITCBC 205   2 : ex_(std::move(ex)) 199   2 : ex_(std::move(ex))
HITCBC 206   2 , resource_(std::move(alloc)) 200   2 , resource_(std::move(alloc))
HITCBC 207   2 , inner_(std::move(inner)) 201   2 , inner_(std::move(inner))
208   { 202   {
HITCBC 209   2 } 203   2 }
210   204  
211   // with allocator, explicit stop token (use template to avoid void parameter) 205   // with allocator, explicit stop token (use template to avoid void parameter)
212   template<class A> 206   template<class A>
213   requires (!InheritStopToken && !std::is_void_v<Alloc> && std::same_as<A, Alloc>) 207   requires (!InheritStopToken && !std::is_void_v<Alloc> && std::same_as<A, Alloc>)
HITCBC 214   2 run_awaitable_ex(Ex ex, A alloc, Task inner, std::stop_token st) 208   2 run_awaitable_ex(Ex ex, A alloc, Task inner, std::stop_token st)
HITCBC 215   2 : ex_(std::move(ex)) 209   2 : ex_(std::move(ex))
HITCBC 216   2 , resource_(std::move(alloc)) 210   2 , resource_(std::move(alloc))
HITCBC 217   2 , st_(std::move(st)) 211   2 , st_(std::move(st))
HITCBC 218   2 , inner_(std::move(inner)) 212   2 , inner_(std::move(inner))
219   { 213   {
HITCBC 220   2 } 214   2 }
221   215  
HITCBC 222   9 bool await_ready() const noexcept 216   18 bool await_ready() const noexcept
223   { 217   {
HITCBC 224   9 return inner_.await_ready(); 218   18 return inner_.await_ready();
225   } 219   }
226   220  
HITCBC 227   9 decltype(auto) await_resume() 221   18 decltype(auto) await_resume()
228   { 222   {
HITCBC 229   9 return inner_.await_resume(); 223   18 return inner_.await_resume();
230   } 224   }
231   225  
HITCBC 232   9 std::coroutine_handle<> await_suspend(std::coroutine_handle<> cont, io_env const* caller_env) 226   18 std::coroutine_handle<> await_suspend(std::coroutine_handle<> cont, io_env const* caller_env)
233   { 227   {
HITCBC 234 - 9 tr_ = make_dispatch_trampoline(); 228 + 18 tr_ = make_boundary_trampoline();
HITCBC 235   9 tr_.h_.promise().caller_ex_ = caller_env->executor; 229   18 tr_.h_.promise().caller_ex_ = caller_env->executor;
HITCBC 236 - 9 tr_.h_.promise().parent_ = cont; 230 + 18 tr_.h_.promise().parent_.h = cont;
237   231  
HITCBC 238   9 auto h = inner_.handle(); 232   18 auto h = inner_.handle();
HITCBC 239   9 auto& p = h.promise(); 233   18 auto& p = h.promise();
HITCBC 240   9 p.set_continuation(tr_.h_); 234   18 p.set_continuation(tr_.h_);
241   235  
HITCBC 242   9 env_.executor = ex_; 236   18 env_.executor = ex_;
243   if constexpr (InheritStopToken) 237   if constexpr (InheritStopToken)
HITCBC 244   5 env_.stop_token = caller_env->stop_token; 238   12 env_.stop_token = caller_env->stop_token;
245   else 239   else
HITCBC 246   4 env_.stop_token = st_; 240   6 env_.stop_token = st_;
247   241  
248   if constexpr (!std::is_void_v<Alloc>) 242   if constexpr (!std::is_void_v<Alloc>)
HITCBC 249   4 env_.frame_allocator = resource_.get(); 243   4 env_.frame_allocator = resource_.get();
250   else 244   else
HITCBC 251   5 env_.frame_allocator = caller_env->frame_allocator; 245   14 env_.frame_allocator = caller_env->frame_allocator;
252   246  
HITCBC 253   9 p.set_environment(&env_); 247   18 p.set_environment(&env_);
HITCBC 254 - 18 return h; 248 + 18 task_cont_.h = h;
HITGNC   249 + 18 ex_.post(task_cont_);
HITGNC   250 + 18 return std::noop_coroutine();
255   } 251   }
256   252  
257   // Non-copyable 253   // Non-copyable
258   run_awaitable_ex(run_awaitable_ex const&) = delete; 254   run_awaitable_ex(run_awaitable_ex const&) = delete;
259   run_awaitable_ex& operator=(run_awaitable_ex const&) = delete; 255   run_awaitable_ex& operator=(run_awaitable_ex const&) = delete;
260   256  
261   // Movable (no noexcept - Task may throw) 257   // Movable (no noexcept - Task may throw)
HITCBC 262   9 run_awaitable_ex(run_awaitable_ex&&) = default; 258   18 run_awaitable_ex(run_awaitable_ex&&) = default;
263   run_awaitable_ex& operator=(run_awaitable_ex&&) = default; 259   run_awaitable_ex& operator=(run_awaitable_ex&&) = default;
264   }; 260   };
265 - //----------------------------------------------------------  
266 - //  
267 - // run_awaitable - no executor (inherits caller's executor)  
268 - //  
269 - //----------------------------------------------------------  
270 -  
271   261  
272   /** Awaitable that runs a task with optional stop_token override. 262   /** Awaitable that runs a task with optional stop_token override.
273   263  
274   Does NOT store an executor - the task inherits the caller's executor 264   Does NOT store an executor - the task inherits the caller's executor
275   directly. Executors always match, so no dispatch trampoline is needed. 265   directly. Executors always match, so no dispatch trampoline is needed.
276   The inner task's `final_suspend` resumes the parent directly via 266   The inner task's `final_suspend` resumes the parent directly via
277   unconditional symmetric transfer. 267   unconditional symmetric transfer.
278   268  
279   @tparam Task The IoRunnable type 269   @tparam Task The IoRunnable type
280   @tparam InheritStopToken If true, inherit caller's stop token 270   @tparam InheritStopToken If true, inherit caller's stop token
281   @tparam Alloc The allocator type (void for no allocator) 271   @tparam Alloc The allocator type (void for no allocator)
282   */ 272   */
283   template<IoRunnable Task, bool InheritStopToken, class Alloc = void> 273   template<IoRunnable Task, bool InheritStopToken, class Alloc = void>
284   struct [[nodiscard]] run_awaitable 274   struct [[nodiscard]] run_awaitable
285   { 275   {
286   frame_memory_resource<Alloc> resource_; 276   frame_memory_resource<Alloc> resource_;
287   std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_; 277   std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_;
288   io_env env_; 278   io_env env_;
289   Task inner_; // Last: destroyed first, while env_ is still valid 279   Task inner_; // Last: destroyed first, while env_ is still valid
290   280  
291   // void allocator, inherit stop token 281   // void allocator, inherit stop token
292   explicit run_awaitable(Task inner) 282   explicit run_awaitable(Task inner)
293   requires (InheritStopToken && std::is_void_v<Alloc>) 283   requires (InheritStopToken && std::is_void_v<Alloc>)
294   : inner_(std::move(inner)) 284   : inner_(std::move(inner))
295   { 285   {
296   } 286   }
297   287  
298   // void allocator, explicit stop token 288   // void allocator, explicit stop token
HITCBC 299   1 run_awaitable(Task inner, std::stop_token st) 289   1 run_awaitable(Task inner, std::stop_token st)
300   requires (!InheritStopToken && std::is_void_v<Alloc>) 290   requires (!InheritStopToken && std::is_void_v<Alloc>)
HITCBC 301   1 : st_(std::move(st)) 291   1 : st_(std::move(st))
HITCBC 302   1 , inner_(std::move(inner)) 292   1 , inner_(std::move(inner))
303   { 293   {
HITCBC 304   1 } 294   1 }
305   295  
306   // with allocator, inherit stop token (use template to avoid void parameter) 296   // with allocator, inherit stop token (use template to avoid void parameter)
307   template<class A> 297   template<class A>
308   requires (InheritStopToken && !std::is_void_v<Alloc> && std::same_as<A, Alloc>) 298   requires (InheritStopToken && !std::is_void_v<Alloc> && std::same_as<A, Alloc>)
HITCBC 309   3 run_awaitable(A alloc, Task inner) 299   3 run_awaitable(A alloc, Task inner)
HITCBC 310   3 : resource_(std::move(alloc)) 300   3 : resource_(std::move(alloc))
HITCBC 311   3 , inner_(std::move(inner)) 301   3 , inner_(std::move(inner))
312   { 302   {
HITCBC 313   3 } 303   3 }
314   304  
315   // with allocator, explicit stop token (use template to avoid void parameter) 305   // with allocator, explicit stop token (use template to avoid void parameter)
316   template<class A> 306   template<class A>
317   requires (!InheritStopToken && !std::is_void_v<Alloc> && std::same_as<A, Alloc>) 307   requires (!InheritStopToken && !std::is_void_v<Alloc> && std::same_as<A, Alloc>)
HITCBC 318   2 run_awaitable(A alloc, Task inner, std::stop_token st) 308   2 run_awaitable(A alloc, Task inner, std::stop_token st)
HITCBC 319   2 : resource_(std::move(alloc)) 309   2 : resource_(std::move(alloc))
HITCBC 320   2 , st_(std::move(st)) 310   2 , st_(std::move(st))
HITCBC 321   2 , inner_(std::move(inner)) 311   2 , inner_(std::move(inner))
322   { 312   {
HITCBC 323   2 } 313   2 }
324   314  
HITCBC 325   6 bool await_ready() const noexcept 315   6 bool await_ready() const noexcept
326   { 316   {
HITCBC 327   6 return inner_.await_ready(); 317   6 return inner_.await_ready();
328   } 318   }
329   319  
HITCBC 330   6 decltype(auto) await_resume() 320   6 decltype(auto) await_resume()
331   { 321   {
HITCBC 332   6 return inner_.await_resume(); 322   6 return inner_.await_resume();
333   } 323   }
334   324  
HITCBC 335   6 std::coroutine_handle<> await_suspend(std::coroutine_handle<> cont, io_env const* caller_env) 325   6 std::coroutine_handle<> await_suspend(std::coroutine_handle<> cont, io_env const* caller_env)
336   { 326   {
HITCBC 337   6 auto h = inner_.handle(); 327   6 auto h = inner_.handle();
HITCBC 338   6 auto& p = h.promise(); 328   6 auto& p = h.promise();
HITCBC 339   6 p.set_continuation(cont); 329   6 p.set_continuation(cont);
340   330  
HITCBC 341   6 env_.executor = caller_env->executor; 331   6 env_.executor = caller_env->executor;
342   if constexpr (InheritStopToken) 332   if constexpr (InheritStopToken)
HITCBC 343   3 env_.stop_token = caller_env->stop_token; 333   3 env_.stop_token = caller_env->stop_token;
344   else 334   else
HITCBC 345   3 env_.stop_token = st_; 335   3 env_.stop_token = st_;
346   336  
347   if constexpr (!std::is_void_v<Alloc>) 337   if constexpr (!std::is_void_v<Alloc>)
HITCBC 348   5 env_.frame_allocator = resource_.get(); 338   5 env_.frame_allocator = resource_.get();
349   else 339   else
HITCBC 350   1 env_.frame_allocator = caller_env->frame_allocator; 340   1 env_.frame_allocator = caller_env->frame_allocator;
351   341  
HITCBC 352   6 p.set_environment(&env_); 342   6 p.set_environment(&env_);
HITCBC 353   6 return h; 343   6 return h;
354   } 344   }
355   345  
356   // Non-copyable 346   // Non-copyable
357   run_awaitable(run_awaitable const&) = delete; 347   run_awaitable(run_awaitable const&) = delete;
358   run_awaitable& operator=(run_awaitable const&) = delete; 348   run_awaitable& operator=(run_awaitable const&) = delete;
359   349  
360   // Movable (no noexcept - Task may throw) 350   // Movable (no noexcept - Task may throw)
HITCBC 361   6 run_awaitable(run_awaitable&&) = default; 351   6 run_awaitable(run_awaitable&&) = default;
362   run_awaitable& operator=(run_awaitable&&) = default; 352   run_awaitable& operator=(run_awaitable&&) = default;
363   }; 353   };
364 - //----------------------------------------------------------  
365 - //  
366 - // run_wrapper_ex - with executor  
367 - //  
368 - //----------------------------------------------------------  
369 -  
370   354  
371   /** Wrapper returned by run(ex, ...) that accepts a task for execution. 355   /** Wrapper returned by run(ex, ...) that accepts a task for execution.
372   356  
373   @tparam Ex The executor type. 357   @tparam Ex The executor type.
374   @tparam InheritStopToken If true, inherit caller's stop token. 358   @tparam InheritStopToken If true, inherit caller's stop token.
375   @tparam Alloc The allocator type (void for no allocator). 359   @tparam Alloc The allocator type (void for no allocator).
376   */ 360   */
377   template<Executor Ex, bool InheritStopToken, class Alloc> 361   template<Executor Ex, bool InheritStopToken, class Alloc>
378   class [[nodiscard]] run_wrapper_ex 362   class [[nodiscard]] run_wrapper_ex
379   { 363   {
380   Ex ex_; 364   Ex ex_;
381   std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_; 365   std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_;
382   frame_memory_resource<Alloc> resource_; 366   frame_memory_resource<Alloc> resource_;
383   Alloc alloc_; // Copy to pass to awaitable 367   Alloc alloc_; // Copy to pass to awaitable
384   368  
385   public: 369   public:
HITCBC 386   1 run_wrapper_ex(Ex ex, Alloc alloc) 370   1 run_wrapper_ex(Ex ex, Alloc alloc)
387   requires InheritStopToken 371   requires InheritStopToken
HITCBC 388   1 : ex_(std::move(ex)) 372   1 : ex_(std::move(ex))
HITCBC 389   1 , resource_(alloc) 373   1 , resource_(alloc)
HITCBC 390   1 , alloc_(std::move(alloc)) 374   1 , alloc_(std::move(alloc))
391   { 375   {
HITCBC 392   1 set_current_frame_allocator(&resource_); 376   1 set_current_frame_allocator(&resource_);
HITCBC 393   1 } 377   1 }
394   378  
HITCBC 395   1 run_wrapper_ex(Ex ex, std::stop_token st, Alloc alloc) 379   1 run_wrapper_ex(Ex ex, std::stop_token st, Alloc alloc)
396   requires (!InheritStopToken) 380   requires (!InheritStopToken)
HITCBC 397   1 : ex_(std::move(ex)) 381   1 : ex_(std::move(ex))
HITCBC 398   1 , st_(std::move(st)) 382   1 , st_(std::move(st))
HITCBC 399   1 , resource_(alloc) 383   1 , resource_(alloc)
HITCBC 400   1 , alloc_(std::move(alloc)) 384   1 , alloc_(std::move(alloc))
401   { 385   {
HITCBC 402   1 set_current_frame_allocator(&resource_); 386   1 set_current_frame_allocator(&resource_);
HITCBC 403   1 } 387   1 }
404   388  
405   // Non-copyable, non-movable (must be used immediately) 389   // Non-copyable, non-movable (must be used immediately)
406   run_wrapper_ex(run_wrapper_ex const&) = delete; 390   run_wrapper_ex(run_wrapper_ex const&) = delete;
407   run_wrapper_ex(run_wrapper_ex&&) = delete; 391   run_wrapper_ex(run_wrapper_ex&&) = delete;
408   run_wrapper_ex& operator=(run_wrapper_ex const&) = delete; 392   run_wrapper_ex& operator=(run_wrapper_ex const&) = delete;
409   run_wrapper_ex& operator=(run_wrapper_ex&&) = delete; 393   run_wrapper_ex& operator=(run_wrapper_ex&&) = delete;
410   394  
411   template<IoRunnable Task> 395   template<IoRunnable Task>
HITCBC 412   2 [[nodiscard]] auto operator()(Task t) && 396   2 [[nodiscard]] auto operator()(Task t) &&
413   { 397   {
414   if constexpr (InheritStopToken) 398   if constexpr (InheritStopToken)
415   return run_awaitable_ex<Task, Ex, true, Alloc>{ 399   return run_awaitable_ex<Task, Ex, true, Alloc>{
HITCBC 416   1 std::move(ex_), std::move(alloc_), std::move(t)}; 400   1 std::move(ex_), std::move(alloc_), std::move(t)};
417   else 401   else
418   return run_awaitable_ex<Task, Ex, false, Alloc>{ 402   return run_awaitable_ex<Task, Ex, false, Alloc>{
HITCBC 419   1 std::move(ex_), std::move(alloc_), std::move(t), std::move(st_)}; 403   1 std::move(ex_), std::move(alloc_), std::move(t), std::move(st_)};
420   } 404   }
421   }; 405   };
422   406  
423   /// Specialization for memory_resource* - stores pointer directly. 407   /// Specialization for memory_resource* - stores pointer directly.
424   template<Executor Ex, bool InheritStopToken> 408   template<Executor Ex, bool InheritStopToken>
425   class [[nodiscard]] run_wrapper_ex<Ex, InheritStopToken, std::pmr::memory_resource*> 409   class [[nodiscard]] run_wrapper_ex<Ex, InheritStopToken, std::pmr::memory_resource*>
426   { 410   {
427   Ex ex_; 411   Ex ex_;
428   std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_; 412   std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_;
429   std::pmr::memory_resource* mr_; 413   std::pmr::memory_resource* mr_;
430   414  
431   public: 415   public:
HITCBC 432   1 run_wrapper_ex(Ex ex, std::pmr::memory_resource* mr) 416   1 run_wrapper_ex(Ex ex, std::pmr::memory_resource* mr)
433   requires InheritStopToken 417   requires InheritStopToken
HITCBC 434   1 : ex_(std::move(ex)) 418   1 : ex_(std::move(ex))
HITCBC 435   1 , mr_(mr) 419   1 , mr_(mr)
436   { 420   {
HITCBC 437   1 set_current_frame_allocator(mr_); 421   1 set_current_frame_allocator(mr_);
HITCBC 438   1 } 422   1 }
439   423  
HITCBC 440   1 run_wrapper_ex(Ex ex, std::stop_token st, std::pmr::memory_resource* mr) 424   1 run_wrapper_ex(Ex ex, std::stop_token st, std::pmr::memory_resource* mr)
441   requires (!InheritStopToken) 425   requires (!InheritStopToken)
HITCBC 442   1 : ex_(std::move(ex)) 426   1 : ex_(std::move(ex))
HITCBC 443   1 , st_(std::move(st)) 427   1 , st_(std::move(st))
HITCBC 444   1 , mr_(mr) 428   1 , mr_(mr)
445   { 429   {
HITCBC 446   1 set_current_frame_allocator(mr_); 430   1 set_current_frame_allocator(mr_);
HITCBC 447   1 } 431   1 }
448   432  
449   // Non-copyable, non-movable (must be used immediately) 433   // Non-copyable, non-movable (must be used immediately)
450   run_wrapper_ex(run_wrapper_ex const&) = delete; 434   run_wrapper_ex(run_wrapper_ex const&) = delete;
451   run_wrapper_ex(run_wrapper_ex&&) = delete; 435   run_wrapper_ex(run_wrapper_ex&&) = delete;
452   run_wrapper_ex& operator=(run_wrapper_ex const&) = delete; 436   run_wrapper_ex& operator=(run_wrapper_ex const&) = delete;
453   run_wrapper_ex& operator=(run_wrapper_ex&&) = delete; 437   run_wrapper_ex& operator=(run_wrapper_ex&&) = delete;
454   438  
455   template<IoRunnable Task> 439   template<IoRunnable Task>
HITCBC 456   2 [[nodiscard]] auto operator()(Task t) && 440   2 [[nodiscard]] auto operator()(Task t) &&
457   { 441   {
458   if constexpr (InheritStopToken) 442   if constexpr (InheritStopToken)
459   return run_awaitable_ex<Task, Ex, true, std::pmr::memory_resource*>{ 443   return run_awaitable_ex<Task, Ex, true, std::pmr::memory_resource*>{
HITCBC 460   1 std::move(ex_), mr_, std::move(t)}; 444   1 std::move(ex_), mr_, std::move(t)};
461   else 445   else
462   return run_awaitable_ex<Task, Ex, false, std::pmr::memory_resource*>{ 446   return run_awaitable_ex<Task, Ex, false, std::pmr::memory_resource*>{
HITCBC 463   1 std::move(ex_), mr_, std::move(t), std::move(st_)}; 447   1 std::move(ex_), mr_, std::move(t), std::move(st_)};
464   } 448   }
465   }; 449   };
466   450  
467   /// Specialization for no allocator (void). 451   /// Specialization for no allocator (void).
468   template<Executor Ex, bool InheritStopToken> 452   template<Executor Ex, bool InheritStopToken>
469   class [[nodiscard]] run_wrapper_ex<Ex, InheritStopToken, void> 453   class [[nodiscard]] run_wrapper_ex<Ex, InheritStopToken, void>
470   { 454   {
471   Ex ex_; 455   Ex ex_;
472   std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_; 456   std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_;
473   457  
474   public: 458   public:
HITCBC 475   3 explicit run_wrapper_ex(Ex ex) 459   10 explicit run_wrapper_ex(Ex ex)
476   requires InheritStopToken 460   requires InheritStopToken
HITCBC 477   3 : ex_(std::move(ex)) 461   10 : ex_(std::move(ex))
478   { 462   {
HITCBC 479   3 } 463   10 }
480   464  
HITCBC 481   2 run_wrapper_ex(Ex ex, std::stop_token st) 465   4 run_wrapper_ex(Ex ex, std::stop_token st)
482   requires (!InheritStopToken) 466   requires (!InheritStopToken)
HITCBC 483   2 : ex_(std::move(ex)) 467   4 : ex_(std::move(ex))
HITCBC 484   2 , st_(std::move(st)) 468   4 , st_(std::move(st))
485   { 469   {
HITCBC 486   2 } 470   4 }
487   471  
488   // Non-copyable, non-movable (must be used immediately) 472   // Non-copyable, non-movable (must be used immediately)
489   run_wrapper_ex(run_wrapper_ex const&) = delete; 473   run_wrapper_ex(run_wrapper_ex const&) = delete;
490   run_wrapper_ex(run_wrapper_ex&&) = delete; 474   run_wrapper_ex(run_wrapper_ex&&) = delete;
491   run_wrapper_ex& operator=(run_wrapper_ex const&) = delete; 475   run_wrapper_ex& operator=(run_wrapper_ex const&) = delete;
492   run_wrapper_ex& operator=(run_wrapper_ex&&) = delete; 476   run_wrapper_ex& operator=(run_wrapper_ex&&) = delete;
493   477  
494   template<IoRunnable Task> 478   template<IoRunnable Task>
HITCBC 495   5 [[nodiscard]] auto operator()(Task t) && 479   14 [[nodiscard]] auto operator()(Task t) &&
496   { 480   {
497   if constexpr (InheritStopToken) 481   if constexpr (InheritStopToken)
498   return run_awaitable_ex<Task, Ex, true>{ 482   return run_awaitable_ex<Task, Ex, true>{
HITCBC 499   3 std::move(ex_), std::move(t)}; 483   10 std::move(ex_), std::move(t)};
500   else 484   else
501   return run_awaitable_ex<Task, Ex, false>{ 485   return run_awaitable_ex<Task, Ex, false>{
HITCBC 502   2 std::move(ex_), std::move(t), std::move(st_)}; 486   4 std::move(ex_), std::move(t), std::move(st_)};
503   } 487   }
504   }; 488   };
505 - //----------------------------------------------------------  
506 - //  
507 - // run_wrapper - no executor (inherits caller's executor)  
508 - //  
509 - //----------------------------------------------------------  
510 -  
511   489  
512   /** Wrapper returned by run(st) or run(alloc) that accepts a task. 490   /** Wrapper returned by run(st) or run(alloc) that accepts a task.
513   491  
514   @tparam InheritStopToken If true, inherit caller's stop token. 492   @tparam InheritStopToken If true, inherit caller's stop token.
515   @tparam Alloc The allocator type (void for no allocator). 493   @tparam Alloc The allocator type (void for no allocator).
516   */ 494   */
517   template<bool InheritStopToken, class Alloc> 495   template<bool InheritStopToken, class Alloc>
518   class [[nodiscard]] run_wrapper 496   class [[nodiscard]] run_wrapper
519   { 497   {
520   std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_; 498   std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_;
521   frame_memory_resource<Alloc> resource_; 499   frame_memory_resource<Alloc> resource_;
522   Alloc alloc_; // Copy to pass to awaitable 500   Alloc alloc_; // Copy to pass to awaitable
523   501  
524   public: 502   public:
HITCBC 525   1 explicit run_wrapper(Alloc alloc) 503   1 explicit run_wrapper(Alloc alloc)
526   requires InheritStopToken 504   requires InheritStopToken
HITCBC 527   1 : resource_(alloc) 505   1 : resource_(alloc)
HITCBC 528   1 , alloc_(std::move(alloc)) 506   1 , alloc_(std::move(alloc))
529   { 507   {
HITCBC 530   1 set_current_frame_allocator(&resource_); 508   1 set_current_frame_allocator(&resource_);
HITCBC 531   1 } 509   1 }
532   510  
HITCBC 533   1 run_wrapper(std::stop_token st, Alloc alloc) 511   1 run_wrapper(std::stop_token st, Alloc alloc)
534   requires (!InheritStopToken) 512   requires (!InheritStopToken)
HITCBC 535   1 : st_(std::move(st)) 513   1 : st_(std::move(st))
HITCBC 536   1 , resource_(alloc) 514   1 , resource_(alloc)
HITCBC 537   1 , alloc_(std::move(alloc)) 515   1 , alloc_(std::move(alloc))
538   { 516   {
HITCBC 539   1 set_current_frame_allocator(&resource_); 517   1 set_current_frame_allocator(&resource_);
HITCBC 540   1 } 518   1 }
541   519  
542   // Non-copyable, non-movable (must be used immediately) 520   // Non-copyable, non-movable (must be used immediately)
543   run_wrapper(run_wrapper const&) = delete; 521   run_wrapper(run_wrapper const&) = delete;
544   run_wrapper(run_wrapper&&) = delete; 522   run_wrapper(run_wrapper&&) = delete;
545   run_wrapper& operator=(run_wrapper const&) = delete; 523   run_wrapper& operator=(run_wrapper const&) = delete;
546   run_wrapper& operator=(run_wrapper&&) = delete; 524   run_wrapper& operator=(run_wrapper&&) = delete;
547   525  
548   template<IoRunnable Task> 526   template<IoRunnable Task>
HITCBC 549   2 [[nodiscard]] auto operator()(Task t) && 527   2 [[nodiscard]] auto operator()(Task t) &&
550   { 528   {
551   if constexpr (InheritStopToken) 529   if constexpr (InheritStopToken)
552   return run_awaitable<Task, true, Alloc>{ 530   return run_awaitable<Task, true, Alloc>{
HITCBC 553   1 std::move(alloc_), std::move(t)}; 531   1 std::move(alloc_), std::move(t)};
554   else 532   else
555   return run_awaitable<Task, false, Alloc>{ 533   return run_awaitable<Task, false, Alloc>{
HITCBC 556   1 std::move(alloc_), std::move(t), std::move(st_)}; 534   1 std::move(alloc_), std::move(t), std::move(st_)};
557   } 535   }
558   }; 536   };
559   537  
560   /// Specialization for memory_resource* - stores pointer directly. 538   /// Specialization for memory_resource* - stores pointer directly.
561   template<bool InheritStopToken> 539   template<bool InheritStopToken>
562   class [[nodiscard]] run_wrapper<InheritStopToken, std::pmr::memory_resource*> 540   class [[nodiscard]] run_wrapper<InheritStopToken, std::pmr::memory_resource*>
563   { 541   {
564   std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_; 542   std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_;
565   std::pmr::memory_resource* mr_; 543   std::pmr::memory_resource* mr_;
566   544  
567   public: 545   public:
HITCBC 568   2 explicit run_wrapper(std::pmr::memory_resource* mr) 546   2 explicit run_wrapper(std::pmr::memory_resource* mr)
569   requires InheritStopToken 547   requires InheritStopToken
HITCBC 570   2 : mr_(mr) 548   2 : mr_(mr)
571   { 549   {
HITCBC 572   2 set_current_frame_allocator(mr_); 550   2 set_current_frame_allocator(mr_);
HITCBC 573   2 } 551   2 }
574   552  
HITCBC 575   1 run_wrapper(std::stop_token st, std::pmr::memory_resource* mr) 553   1 run_wrapper(std::stop_token st, std::pmr::memory_resource* mr)
576   requires (!InheritStopToken) 554   requires (!InheritStopToken)
HITCBC 577   1 : st_(std::move(st)) 555   1 : st_(std::move(st))
HITCBC 578   1 , mr_(mr) 556   1 , mr_(mr)
579   { 557   {
HITCBC 580   1 set_current_frame_allocator(mr_); 558   1 set_current_frame_allocator(mr_);
HITCBC 581   1 } 559   1 }
582   560  
583   // Non-copyable, non-movable (must be used immediately) 561   // Non-copyable, non-movable (must be used immediately)
584   run_wrapper(run_wrapper const&) = delete; 562   run_wrapper(run_wrapper const&) = delete;
585   run_wrapper(run_wrapper&&) = delete; 563   run_wrapper(run_wrapper&&) = delete;
586   run_wrapper& operator=(run_wrapper const&) = delete; 564   run_wrapper& operator=(run_wrapper const&) = delete;
587   run_wrapper& operator=(run_wrapper&&) = delete; 565   run_wrapper& operator=(run_wrapper&&) = delete;
588   566  
589   template<IoRunnable Task> 567   template<IoRunnable Task>
HITCBC 590   3 [[nodiscard]] auto operator()(Task t) && 568   3 [[nodiscard]] auto operator()(Task t) &&
591   { 569   {
592   if constexpr (InheritStopToken) 570   if constexpr (InheritStopToken)
593   return run_awaitable<Task, true, std::pmr::memory_resource*>{ 571   return run_awaitable<Task, true, std::pmr::memory_resource*>{
HITCBC 594   2 mr_, std::move(t)}; 572   2 mr_, std::move(t)};
595   else 573   else
596   return run_awaitable<Task, false, std::pmr::memory_resource*>{ 574   return run_awaitable<Task, false, std::pmr::memory_resource*>{
HITCBC 597   1 mr_, std::move(t), std::move(st_)}; 575   1 mr_, std::move(t), std::move(st_)};
598   } 576   }
599   }; 577   };
600   578  
601   /// Specialization for stop_token only (no allocator). 579   /// Specialization for stop_token only (no allocator).
602   template<> 580   template<>
603   class [[nodiscard]] run_wrapper<false, void> 581   class [[nodiscard]] run_wrapper<false, void>
604   { 582   {
605   std::stop_token st_; 583   std::stop_token st_;
606   584  
607   public: 585   public:
HITCBC 608   1 explicit run_wrapper(std::stop_token st) 586   1 explicit run_wrapper(std::stop_token st)
HITCBC 609   1 : st_(std::move(st)) 587   1 : st_(std::move(st))
610   { 588   {
HITCBC 611   1 } 589   1 }
612   590  
613   // Non-copyable, non-movable (must be used immediately) 591   // Non-copyable, non-movable (must be used immediately)
614   run_wrapper(run_wrapper const&) = delete; 592   run_wrapper(run_wrapper const&) = delete;
615   run_wrapper(run_wrapper&&) = delete; 593   run_wrapper(run_wrapper&&) = delete;
616   run_wrapper& operator=(run_wrapper const&) = delete; 594   run_wrapper& operator=(run_wrapper const&) = delete;
617   run_wrapper& operator=(run_wrapper&&) = delete; 595   run_wrapper& operator=(run_wrapper&&) = delete;
618   596  
619   template<IoRunnable Task> 597   template<IoRunnable Task>
HITCBC 620   1 [[nodiscard]] auto operator()(Task t) && 598   1 [[nodiscard]] auto operator()(Task t) &&
621   { 599   {
HITCBC 622   1 return run_awaitable<Task, false, void>{std::move(t), std::move(st_)}; 600   1 return run_awaitable<Task, false, void>{std::move(t), std::move(st_)};
623   } 601   }
624   }; 602   };
625   603  
626   } // namespace boost::capy::detail 604   } // namespace boost::capy::detail
627   605  
628   namespace boost::capy { 606   namespace boost::capy {
629 - //----------------------------------------------------------  
630 - //  
631 - // run() overloads - with executor  
632 - //  
633 - //----------------------------------------------------------  
634 -  
635   607  
636   /** Bind a task to execute on a specific executor. 608   /** Bind a task to execute on a specific executor.
637   609  
638   Returns a wrapper that accepts a task and produces an awaitable. 610   Returns a wrapper that accepts a task and produces an awaitable.
639   When co_awaited, the task runs on the specified executor. 611   When co_awaited, the task runs on the specified executor.
640   612  
641   @par Example 613   @par Example
642   @code 614   @code
643   co_await run(other_executor)(my_task()); 615   co_await run(other_executor)(my_task());
644   @endcode 616   @endcode
645   617  
646   @param ex The executor on which the task should run. 618   @param ex The executor on which the task should run.
647   619  
648   @return A wrapper that accepts a task for execution. 620   @return A wrapper that accepts a task for execution.
649   621  
650   @see task 622   @see task
651   @see executor 623   @see executor
652   */ 624   */
653   template<Executor Ex> 625   template<Executor Ex>
654   [[nodiscard]] auto 626   [[nodiscard]] auto
HITCBC 655   3 run(Ex ex) 627   10 run(Ex ex)
656   { 628   {
HITCBC 657   3 return detail::run_wrapper_ex<Ex, true, void>{std::move(ex)}; 629   10 return detail::run_wrapper_ex<Ex, true, void>{std::move(ex)};
658   } 630   }
659   631  
660   /** Bind a task to an executor with a stop token. 632   /** Bind a task to an executor with a stop token.
661   633  
662   @param ex The executor on which the task should run. 634   @param ex The executor on which the task should run.
663   @param st The stop token for cooperative cancellation. 635   @param st The stop token for cooperative cancellation.
664   636  
665   @return A wrapper that accepts a task for execution. 637   @return A wrapper that accepts a task for execution.
666   */ 638   */
667   template<Executor Ex> 639   template<Executor Ex>
668   [[nodiscard]] auto 640   [[nodiscard]] auto
HITCBC 669   2 run(Ex ex, std::stop_token st) 641   4 run(Ex ex, std::stop_token st)
670   { 642   {
671   return detail::run_wrapper_ex<Ex, false, void>{ 643   return detail::run_wrapper_ex<Ex, false, void>{
HITCBC 672   2 std::move(ex), std::move(st)}; 644   4 std::move(ex), std::move(st)};
673   } 645   }
674   646  
675   /** Bind a task to an executor with a memory resource. 647   /** Bind a task to an executor with a memory resource.
676   648  
677   @param ex The executor on which the task should run. 649   @param ex The executor on which the task should run.
678   @param mr The memory resource for frame allocation. 650   @param mr The memory resource for frame allocation.
679   651  
680   @return A wrapper that accepts a task for execution. 652   @return A wrapper that accepts a task for execution.
681   */ 653   */
682   template<Executor Ex> 654   template<Executor Ex>
683   [[nodiscard]] auto 655   [[nodiscard]] auto
HITCBC 684   1 run(Ex ex, std::pmr::memory_resource* mr) 656   1 run(Ex ex, std::pmr::memory_resource* mr)
685   { 657   {
686   return detail::run_wrapper_ex<Ex, true, std::pmr::memory_resource*>{ 658   return detail::run_wrapper_ex<Ex, true, std::pmr::memory_resource*>{
HITCBC 687   1 std::move(ex), mr}; 659   1 std::move(ex), mr};
688   } 660   }
689   661  
690   /** Bind a task to an executor with a standard allocator. 662   /** Bind a task to an executor with a standard allocator.
691   663  
692   @param ex The executor on which the task should run. 664   @param ex The executor on which the task should run.
693   @param alloc The allocator for frame allocation. 665   @param alloc The allocator for frame allocation.
694   666  
695   @return A wrapper that accepts a task for execution. 667   @return A wrapper that accepts a task for execution.
696   */ 668   */
697   template<Executor Ex, detail::Allocator Alloc> 669   template<Executor Ex, detail::Allocator Alloc>
698   [[nodiscard]] auto 670   [[nodiscard]] auto
HITCBC 699   1 run(Ex ex, Alloc alloc) 671   1 run(Ex ex, Alloc alloc)
700   { 672   {
701   return detail::run_wrapper_ex<Ex, true, Alloc>{ 673   return detail::run_wrapper_ex<Ex, true, Alloc>{
HITCBC 702   1 std::move(ex), std::move(alloc)}; 674   1 std::move(ex), std::move(alloc)};
703   } 675   }
704   676  
705   /** Bind a task to an executor with stop token and memory resource. 677   /** Bind a task to an executor with stop token and memory resource.
706   678  
707   @param ex The executor on which the task should run. 679   @param ex The executor on which the task should run.
708   @param st The stop token for cooperative cancellation. 680   @param st The stop token for cooperative cancellation.
709   @param mr The memory resource for frame allocation. 681   @param mr The memory resource for frame allocation.
710   682  
711   @return A wrapper that accepts a task for execution. 683   @return A wrapper that accepts a task for execution.
712   */ 684   */
713   template<Executor Ex> 685   template<Executor Ex>
714   [[nodiscard]] auto 686   [[nodiscard]] auto
HITCBC 715   1 run(Ex ex, std::stop_token st, std::pmr::memory_resource* mr) 687   1 run(Ex ex, std::stop_token st, std::pmr::memory_resource* mr)
716   { 688   {
717   return detail::run_wrapper_ex<Ex, false, std::pmr::memory_resource*>{ 689   return detail::run_wrapper_ex<Ex, false, std::pmr::memory_resource*>{
HITCBC 718   1 std::move(ex), std::move(st), mr}; 690   1 std::move(ex), std::move(st), mr};
719   } 691   }
720   692  
721   /** Bind a task to an executor with stop token and standard allocator. 693   /** Bind a task to an executor with stop token and standard allocator.
722   694  
723   @param ex The executor on which the task should run. 695   @param ex The executor on which the task should run.
724   @param st The stop token for cooperative cancellation. 696   @param st The stop token for cooperative cancellation.
725   @param alloc The allocator for frame allocation. 697   @param alloc The allocator for frame allocation.
726   698  
727   @return A wrapper that accepts a task for execution. 699   @return A wrapper that accepts a task for execution.
728   */ 700   */
729   template<Executor Ex, detail::Allocator Alloc> 701   template<Executor Ex, detail::Allocator Alloc>
730   [[nodiscard]] auto 702   [[nodiscard]] auto
HITCBC 731   1 run(Ex ex, std::stop_token st, Alloc alloc) 703   1 run(Ex ex, std::stop_token st, Alloc alloc)
732   { 704   {
733   return detail::run_wrapper_ex<Ex, false, Alloc>{ 705   return detail::run_wrapper_ex<Ex, false, Alloc>{
HITCBC 734   1 std::move(ex), std::move(st), std::move(alloc)}; 706   1 std::move(ex), std::move(st), std::move(alloc)};
735 -  
736 - //----------------------------------------------------------  
737 - //  
738 - // run() overloads - no executor (inherits caller's)  
739 - //  
740 - //----------------------------------------------------------  
741   } 707   }
742   708  
743   /** Run a task with a custom stop token. 709   /** Run a task with a custom stop token.
744   710  
745   The task inherits the caller's executor. Only the stop token 711   The task inherits the caller's executor. Only the stop token
746   is overridden. 712   is overridden.
747   713  
748   @par Example 714   @par Example
749   @code 715   @code
750   std::stop_source source; 716   std::stop_source source;
751   co_await run(source.get_token())(cancellable_task()); 717   co_await run(source.get_token())(cancellable_task());
752   @endcode 718   @endcode
753   719  
754   @param st The stop token for cooperative cancellation. 720   @param st The stop token for cooperative cancellation.
755   721  
756   @return A wrapper that accepts a task for execution. 722   @return A wrapper that accepts a task for execution.
757   */ 723   */
758   [[nodiscard]] inline auto 724   [[nodiscard]] inline auto
HITCBC 759   1 run(std::stop_token st) 725   1 run(std::stop_token st)
760   { 726   {
HITCBC 761   1 return detail::run_wrapper<false, void>{std::move(st)}; 727   1 return detail::run_wrapper<false, void>{std::move(st)};
762   } 728   }
763   729  
764   /** Run a task with a custom memory resource. 730   /** Run a task with a custom memory resource.
765   731  
766   The task inherits the caller's executor. The memory resource 732   The task inherits the caller's executor. The memory resource
767   is used for nested frame allocations. 733   is used for nested frame allocations.
768   734  
769   @param mr The memory resource for frame allocation. 735   @param mr The memory resource for frame allocation.
770   736  
771   @return A wrapper that accepts a task for execution. 737   @return A wrapper that accepts a task for execution.
772   */ 738   */
773   [[nodiscard]] inline auto 739   [[nodiscard]] inline auto
HITCBC 774   2 run(std::pmr::memory_resource* mr) 740   2 run(std::pmr::memory_resource* mr)
775   { 741   {
HITCBC 776   2 return detail::run_wrapper<true, std::pmr::memory_resource*>{mr}; 742   2 return detail::run_wrapper<true, std::pmr::memory_resource*>{mr};
777   } 743   }
778   744  
779   /** Run a task with a custom standard allocator. 745   /** Run a task with a custom standard allocator.
780   746  
781   The task inherits the caller's executor. The allocator is used 747   The task inherits the caller's executor. The allocator is used
782   for nested frame allocations. 748   for nested frame allocations.
783   749  
784   @param alloc The allocator for frame allocation. 750   @param alloc The allocator for frame allocation.
785   751  
786   @return A wrapper that accepts a task for execution. 752   @return A wrapper that accepts a task for execution.
787   */ 753   */
788   template<detail::Allocator Alloc> 754   template<detail::Allocator Alloc>
789   [[nodiscard]] auto 755   [[nodiscard]] auto
HITCBC 790   1 run(Alloc alloc) 756   1 run(Alloc alloc)
791   { 757   {
HITCBC 792   1 return detail::run_wrapper<true, Alloc>{std::move(alloc)}; 758   1 return detail::run_wrapper<true, Alloc>{std::move(alloc)};
793   } 759   }
794   760  
795   /** Run a task with stop token and memory resource. 761   /** Run a task with stop token and memory resource.
796   762  
797   The task inherits the caller's executor. 763   The task inherits the caller's executor.
798   764  
799   @param st The stop token for cooperative cancellation. 765   @param st The stop token for cooperative cancellation.
800   @param mr The memory resource for frame allocation. 766   @param mr The memory resource for frame allocation.
801   767  
802   @return A wrapper that accepts a task for execution. 768   @return A wrapper that accepts a task for execution.
803   */ 769   */
804   [[nodiscard]] inline auto 770   [[nodiscard]] inline auto
HITCBC 805   1 run(std::stop_token st, std::pmr::memory_resource* mr) 771   1 run(std::stop_token st, std::pmr::memory_resource* mr)
806   { 772   {
807   return detail::run_wrapper<false, std::pmr::memory_resource*>{ 773   return detail::run_wrapper<false, std::pmr::memory_resource*>{
HITCBC 808   1 std::move(st), mr}; 774   1 std::move(st), mr};
809   } 775   }
810   776  
811   /** Run a task with stop token and standard allocator. 777   /** Run a task with stop token and standard allocator.
812   778  
813   The task inherits the caller's executor. 779   The task inherits the caller's executor.
814   780  
815   @param st The stop token for cooperative cancellation. 781   @param st The stop token for cooperative cancellation.
816   @param alloc The allocator for frame allocation. 782   @param alloc The allocator for frame allocation.
817   783  
818   @return A wrapper that accepts a task for execution. 784   @return A wrapper that accepts a task for execution.
819   */ 785   */
820   template<detail::Allocator Alloc> 786   template<detail::Allocator Alloc>
821   [[nodiscard]] auto 787   [[nodiscard]] auto
HITCBC 822   1 run(std::stop_token st, Alloc alloc) 788   1 run(std::stop_token st, Alloc alloc)
823   { 789   {
824   return detail::run_wrapper<false, Alloc>{ 790   return detail::run_wrapper<false, Alloc>{
HITCBC 825   1 std::move(st), std::move(alloc)}; 791   1 std::move(st), std::move(alloc)};
826   } 792   }
827   793  
828   } // namespace boost::capy 794   } // namespace boost::capy
829   795  
830   #endif 796   #endif