100.00% Lines (204/204) 100.00% Functions (44/44)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com) 2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3   // Copyright (c) 2026 Michael Vandeberg 3   // Copyright (c) 2026 Michael Vandeberg
4   // 4   //
5   // Distributed under the Boost Software License, Version 1.0. (See accompanying 5   // Distributed under the Boost Software License, Version 1.0. (See accompanying
6   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7   // 7   //
8   // Official repository: https://github.com/cppalliance/capy 8   // Official repository: https://github.com/cppalliance/capy
9   // 9   //
10   10  
11   #ifndef BOOST_CAPY_RUN_ASYNC_HPP 11   #ifndef BOOST_CAPY_RUN_ASYNC_HPP
12   #define BOOST_CAPY_RUN_ASYNC_HPP 12   #define BOOST_CAPY_RUN_ASYNC_HPP
13   13  
14   #include <boost/capy/detail/config.hpp> 14   #include <boost/capy/detail/config.hpp>
15   #include <boost/capy/detail/run.hpp> 15   #include <boost/capy/detail/run.hpp>
16   #include <boost/capy/detail/run_callbacks.hpp> 16   #include <boost/capy/detail/run_callbacks.hpp>
17   #include <boost/capy/concept/executor.hpp> 17   #include <boost/capy/concept/executor.hpp>
18 - #include <boost/capy/concept/io_runnable.hpp> 18 + #include <boost/capy/concept/context_runnable.hpp>
19   #include <boost/capy/ex/abandon.hpp> 19   #include <boost/capy/ex/abandon.hpp>
20   #include <boost/capy/ex/execution_context.hpp> 20   #include <boost/capy/ex/execution_context.hpp>
21   #include <boost/capy/ex/frame_allocator.hpp> 21   #include <boost/capy/ex/frame_allocator.hpp>
22 - #include <boost/capy/ex/io_env.hpp> 22 + #include <boost/capy/ex/await_context.hpp>
23   #include <boost/capy/ex/recycling_memory_resource.hpp> 23   #include <boost/capy/ex/recycling_memory_resource.hpp>
24   #include <boost/capy/ex/work_guard.hpp> 24   #include <boost/capy/ex/work_guard.hpp>
25   25  
26   #include <algorithm> 26   #include <algorithm>
27   #include <coroutine> 27   #include <coroutine>
28   #include <cstring> 28   #include <cstring>
29   #include <exception> 29   #include <exception>
30   #include <memory_resource> 30   #include <memory_resource>
31   #include <new> 31   #include <new>
32   #include <stop_token> 32   #include <stop_token>
33   #include <type_traits> 33   #include <type_traits>
34   34  
35   namespace boost { 35   namespace boost {
36   namespace capy { 36   namespace capy {
37   namespace detail { 37   namespace detail {
38   38  
39   /** Match types usable as `run_async` completion handlers. 39   /** Match types usable as `run_async` completion handlers.
40   40  
41   Excludes the types meaningful to the other `run_async` parameters. 41   Excludes the types meaningful to the other `run_async` parameters.
42   A stop token, memory resource pointer, or allocator argument 42   A stop token, memory resource pointer, or allocator argument
43   therefore selects its dedicated overload by conversion. It does not 43   therefore selects its dedicated overload by conversion. It does not
44   deduce as an exact-match handler. 44   deduce as an exact-match handler.
45   */ 45   */
46   template<class H> 46   template<class H>
47   concept RunAsyncHandler = 47   concept RunAsyncHandler =
48   !std::is_convertible_v<H, std::pmr::memory_resource*> && 48   !std::is_convertible_v<H, std::pmr::memory_resource*> &&
49   !std::is_convertible_v<H, std::stop_token> && 49   !std::is_convertible_v<H, std::stop_token> &&
50   !Allocator<H>; 50   !Allocator<H>;
51   51  
52   /// Function pointer type for type-erased frame deallocation. 52   /// Function pointer type for type-erased frame deallocation.
53   using dealloc_fn = void(*)(void*, std::size_t); 53   using dealloc_fn = void(*)(void*, std::size_t);
54   54  
55   /// Type-erased deallocator implementation for trampoline frames. 55   /// Type-erased deallocator implementation for trampoline frames.
56   template<class Alloc> 56   template<class Alloc>
HITCBC 57   2325 void dealloc_impl(void* raw, std::size_t total) 57   2325 void dealloc_impl(void* raw, std::size_t total)
58   { 58   {
59   static_assert(std::is_same_v<typename Alloc::value_type, std::byte>); 59   static_assert(std::is_same_v<typename Alloc::value_type, std::byte>);
HITCBC 60   2325 auto* a = std::launder(reinterpret_cast<Alloc*>( 60   2325 auto* a = std::launder(reinterpret_cast<Alloc*>(
HITCBC 61   2325 static_cast<char*>(raw) + total - sizeof(Alloc))); 61   2325 static_cast<char*>(raw) + total - sizeof(Alloc)));
HITCBC 62   2325 Alloc ba(std::move(*a)); 62   2325 Alloc ba(std::move(*a));
HITCBC 63   16 a->~Alloc(); 63   16 a->~Alloc();
HITCBC 64   16 ba.deallocate(static_cast<std::byte*>(raw), total); 64   16 ba.deallocate(static_cast<std::byte*>(raw), total);
HITCBC 65   2325 } 65   2325 }
66   66  
67   /// Awaiter to access the promise from within the coroutine. 67   /// Awaiter to access the promise from within the coroutine.
68   template<class Promise> 68   template<class Promise>
69   struct get_promise_awaiter 69   struct get_promise_awaiter
70   { 70   {
71   Promise* p_ = nullptr; 71   Promise* p_ = nullptr;
72   72  
HITCBC 73   5319 bool await_ready() const noexcept { return false; } 73   5317 bool await_ready() const noexcept { return false; }
74   74  
HITCBC 75   5319 bool await_suspend(std::coroutine_handle<Promise> h) noexcept 75   5317 bool await_suspend(std::coroutine_handle<Promise> h) noexcept
76   { 76   {
HITCBC 77   5319 p_ = &h.promise(); 77   5317 p_ = &h.promise();
HITCBC 78   5319 return false; 78   5317 return false;
79   } 79   }
80   80  
HITCBC 81   5319 Promise& await_resume() const noexcept 81   5317 Promise& await_resume() const noexcept
82   { 82   {
HITCBC 83   5319 return *p_; 83   5317 return *p_;
84   } 84   }
85   }; 85   };
86   86  
87   /** Internal run_async_trampoline coroutine for run_async. 87   /** Internal run_async_trampoline coroutine for run_async.
88   88  
89   The run_async_trampoline is allocated BEFORE the task (via C++17 postfix evaluation 89   The run_async_trampoline is allocated BEFORE the task (via C++17 postfix evaluation
90   order) and serves as the task's continuation. When the task final_suspends, 90   order) and serves as the task's continuation. When the task final_suspends,
91   control returns to the run_async_trampoline which then invokes the appropriate handler. 91   control returns to the run_async_trampoline which then invokes the appropriate handler.
92   92  
93   For value-type allocators, the run_async_trampoline stores a frame_memory_resource 93   For value-type allocators, the run_async_trampoline stores a frame_memory_resource
94   that wraps the allocator. For memory_resource*, it stores the pointer directly. 94   that wraps the allocator. For memory_resource*, it stores the pointer directly.
95   95  
96   @tparam Ex The executor type. 96   @tparam Ex The executor type.
97   @tparam Handlers The handler type (default_handler or handler_pair). 97   @tparam Handlers The handler type (default_handler or handler_pair).
98   @tparam Alloc The allocator type (value type or memory_resource*). 98   @tparam Alloc The allocator type (value type or memory_resource*).
99   */ 99   */
100   template<class Ex, class Handlers, class Alloc> 100   template<class Ex, class Handlers, class Alloc>
101   struct run_async_trampoline 101   struct run_async_trampoline
102   { 102   {
103   using invoke_fn = void(*)(void*, Handlers&); 103   using invoke_fn = void(*)(void*, Handlers&);
104   104  
105   struct promise_type 105   struct promise_type
106   { 106   {
107   work_guard<Ex> wg_; 107   work_guard<Ex> wg_;
108   Handlers handlers_; 108   Handlers handlers_;
109   frame_memory_resource<Alloc> resource_; 109   frame_memory_resource<Alloc> resource_;
110 - io_env env_; 110 + await_context env_;
111   invoke_fn invoke_ = nullptr; 111   invoke_fn invoke_ = nullptr;
112   void* task_promise_ = nullptr; 112   void* task_promise_ = nullptr;
113   // task_h_: raw handle for frame_guard cleanup in make_trampoline. 113   // task_h_: raw handle for frame_guard cleanup in make_trampoline.
114   // task_cont_: continuation wrapping the same handle for executor dispatch. 114   // task_cont_: continuation wrapping the same handle for executor dispatch.
115   // Both must reference the same coroutine and be kept in sync. 115   // Both must reference the same coroutine and be kept in sync.
116   std::coroutine_handle<> task_h_; 116   std::coroutine_handle<> task_h_;
117   continuation task_cont_; 117   continuation task_cont_;
118   // Owns the task's segment: abandoning it resumes this 118   // Owns the task's segment: abandoning it resumes this
119   // trampoline, or has it destroyed, to unwind the task. 119   // trampoline, or has it destroyed, to unwind the task.
120   chain_owner owner_; 120   chain_owner owner_;
121   121  
HITCBC 122   2325 ~promise_type() 122   2325 ~promise_type()
123   { 123   {
124   // Still owned only when a closed executor destroyed this 124   // Still owned only when a closed executor destroyed this
125   // trampoline instead of resuming it to unwind the task. 125   // trampoline instead of resuming it to unwind the task.
HITCBC 126   2325 if(task_h_) 126   2325 if(task_h_)
HITCBC 127   7 task_h_.destroy(); 127   7 task_h_.destroy();
HITCBC 128   2325 } 128   2325 }
129   129  
HITCBC 130   2325 promise_type(Ex& ex, Handlers& h, Alloc& a) noexcept 130   2325 promise_type(Ex& ex, Handlers& h, Alloc& a) noexcept
HITCBC 131   2325 : wg_(std::move(ex)) 131   2325 : wg_(std::move(ex))
HITCBC 132   2325 , handlers_(std::move(h)) 132   2325 , handlers_(std::move(h))
HITCBC 133   2325 , resource_(std::move(a)) 133   2325 , resource_(std::move(a))
134   { 134   {
HITCBC 135   2325 } 135   2325 }
136   136  
HITCBC 137   2325 static void* operator new( 137   2325 static void* operator new(
138   std::size_t size, Ex const&, Handlers const&, Alloc a) 138   std::size_t size, Ex const&, Handlers const&, Alloc a)
139   { 139   {
140   using byte_alloc = typename std::allocator_traits<Alloc> 140   using byte_alloc = typename std::allocator_traits<Alloc>
141   ::template rebind_alloc<std::byte>; 141   ::template rebind_alloc<std::byte>;
142   142  
HITCBC 143   2325 constexpr auto footer_align = 143   2325 constexpr auto footer_align =
144   (std::max)(alignof(dealloc_fn), alignof(Alloc)); 144   (std::max)(alignof(dealloc_fn), alignof(Alloc));
HITCBC 145   2325 auto padded = (size + footer_align - 1) & ~(footer_align - 1); 145   2325 auto padded = (size + footer_align - 1) & ~(footer_align - 1);
HITCBC 146   2325 auto total = padded + sizeof(dealloc_fn) + sizeof(Alloc); 146   2325 auto total = padded + sizeof(dealloc_fn) + sizeof(Alloc);
147   147  
HITCBC 148   16 byte_alloc ba(std::move(a)); 148   16 byte_alloc ba(std::move(a));
HITCBC 149   2325 void* raw = ba.allocate(total); 149   2325 void* raw = ba.allocate(total);
150   150  
HITCBC 151   2325 auto* fn_loc = reinterpret_cast<dealloc_fn*>( 151   2325 auto* fn_loc = reinterpret_cast<dealloc_fn*>(
152   static_cast<char*>(raw) + padded); 152   static_cast<char*>(raw) + padded);
HITCBC 153   2325 *fn_loc = &dealloc_impl<byte_alloc>; 153   2325 *fn_loc = &dealloc_impl<byte_alloc>;
154   154  
HITCBC 155   2325 new (fn_loc + 1) byte_alloc(std::move(ba)); 155   2325 new (fn_loc + 1) byte_alloc(std::move(ba));
156   156  
HITCBC 157   4634 return raw; 157   4634 return raw;
HITCBC 158   15 } 158   15 }
159   159  
HITCBC 160   2325 static void operator delete(void* ptr, std::size_t size) 160   2325 static void operator delete(void* ptr, std::size_t size)
161   { 161   {
HITCBC 162   2325 constexpr auto footer_align = 162   2325 constexpr auto footer_align =
163   (std::max)(alignof(dealloc_fn), alignof(Alloc)); 163   (std::max)(alignof(dealloc_fn), alignof(Alloc));
HITCBC 164   2325 auto padded = (size + footer_align - 1) & ~(footer_align - 1); 164   2325 auto padded = (size + footer_align - 1) & ~(footer_align - 1);
HITCBC 165   2325 auto total = padded + sizeof(dealloc_fn) + sizeof(Alloc); 165   2325 auto total = padded + sizeof(dealloc_fn) + sizeof(Alloc);
166   166  
HITCBC 167   2325 auto* fn = reinterpret_cast<dealloc_fn*>( 167   2325 auto* fn = reinterpret_cast<dealloc_fn*>(
168   static_cast<char*>(ptr) + padded); 168   static_cast<char*>(ptr) + padded);
HITCBC 169   2325 (*fn)(ptr, total); 169   2325 (*fn)(ptr, total);
HITCBC 170   2325 } 170   2325 }
171   171  
HITCBC 172   4650 std::pmr::memory_resource* get_resource() noexcept 172   4650 std::pmr::memory_resource* get_resource() noexcept
173   { 173   {
HITCBC 174   4650 return &resource_; 174   4650 return &resource_;
175   } 175   }
176   176  
HITCBC 177   2325 run_async_trampoline get_return_object() noexcept 177   2325 run_async_trampoline get_return_object() noexcept
178   { 178   {
179   return run_async_trampoline{ 179   return run_async_trampoline{
HITCBC 180   2325 std::coroutine_handle<promise_type>::from_promise(*this)}; 180   2325 std::coroutine_handle<promise_type>::from_promise(*this)};
181   } 181   }
182   182  
HITCBC 183   2325 std::suspend_always initial_suspend() noexcept 183   2325 std::suspend_always initial_suspend() noexcept
184   { 184   {
HITCBC 185   2325 return {}; 185   2325 return {};
186   } 186   }
187   187  
HITCBC 188   2318 std::suspend_never final_suspend() noexcept 188   2318 std::suspend_never final_suspend() noexcept
189   { 189   {
HITCBC 190   2318 return {}; 190   2318 return {};
191   } 191   }
192   192  
HITCBC 193   2318 void return_void() noexcept 193   2318 void return_void() noexcept
194   { 194   {
HITCBC 195   2318 } 195   2318 }
196   196  
197   // An exception reaches here only by escaping a handler: a handler 197   // An exception reaches here only by escaping a handler: a handler
198   // that threw, or the default handler rethrowing an otherwise 198   // that threw, or the default handler rethrowing an otherwise
199   // unhandled task exception. Cancellation is filtered out earlier 199   // unhandled task exception. Cancellation is filtered out earlier
200   // by default_handler, so this is always a genuine error with no 200   // by default_handler, so this is always a genuine error with no
201   // owner to receive it: fail fast. 201   // owner to receive it: fail fast.
202   void unhandled_exception() noexcept { std::terminate(); } // LCOV_EXCL_LINE 202   void unhandled_exception() noexcept { std::terminate(); } // LCOV_EXCL_LINE
203   }; 203   };
204   204  
205   std::coroutine_handle<promise_type> h_; 205   std::coroutine_handle<promise_type> h_;
206   206  
207 - template<IoRunnable Task> 207 + template<ContextRunnable Task>
HITCBC 208   2304 static void invoke_impl(void* p, Handlers& h) 208   2304 static void invoke_impl(void* p, Handlers& h)
209   { 209   {
210   using R = decltype(std::declval<Task&>().await_resume()); 210   using R = decltype(std::declval<Task&>().await_resume());
HITCBC 211   2304 auto& promise = *static_cast<typename Task::promise_type*>(p); 211   2304 auto& promise = *static_cast<typename Task::promise_type*>(p);
HITCBC 212   2304 if(promise.exception()) 212   2304 if(promise.exception())
HITCBC 213   1 h(promise.exception()); 213   1 h(promise.exception());
214   else if constexpr(std::is_void_v<R>) 214   else if constexpr(std::is_void_v<R>)
HITCBC 215   2302 h(); 215   2302 h();
216   else 216   else
HITCBC 217   1 h(std::move(promise.result())); 217   1 h(std::move(promise.result()));
HITCBC 218   2304 } 218   2304 }
219   }; 219   };
220   220  
221   /** Specialization for memory_resource* - stores pointer directly. 221   /** Specialization for memory_resource* - stores pointer directly.
222   222  
223   This avoids double indirection when the user passes a memory_resource*. 223   This avoids double indirection when the user passes a memory_resource*.
224   */ 224   */
225   template<class Ex, class Handlers> 225   template<class Ex, class Handlers>
226   struct run_async_trampoline<Ex, Handlers, std::pmr::memory_resource*> 226   struct run_async_trampoline<Ex, Handlers, std::pmr::memory_resource*>
227   { 227   {
228   using invoke_fn = void(*)(void*, Handlers&); 228   using invoke_fn = void(*)(void*, Handlers&);
229   229  
230   struct promise_type 230   struct promise_type
231   { 231   {
232   work_guard<Ex> wg_; 232   work_guard<Ex> wg_;
233   Handlers handlers_; 233   Handlers handlers_;
234   std::pmr::memory_resource* mr_; 234   std::pmr::memory_resource* mr_;
235 - io_env env_; 235 + await_context env_;
236   invoke_fn invoke_ = nullptr; 236   invoke_fn invoke_ = nullptr;
237   void* task_promise_ = nullptr; 237   void* task_promise_ = nullptr;
238   // task_h_: raw handle for frame_guard cleanup in make_trampoline. 238   // task_h_: raw handle for frame_guard cleanup in make_trampoline.
239   // task_cont_: continuation wrapping the same handle for executor dispatch. 239   // task_cont_: continuation wrapping the same handle for executor dispatch.
240   // Both must reference the same coroutine and be kept in sync. 240   // Both must reference the same coroutine and be kept in sync.
241   std::coroutine_handle<> task_h_; 241   std::coroutine_handle<> task_h_;
242   continuation task_cont_; 242   continuation task_cont_;
243   // Owns the task's segment: abandoning it resumes this 243   // Owns the task's segment: abandoning it resumes this
244   // trampoline, or has it destroyed, to unwind the task. 244   // trampoline, or has it destroyed, to unwind the task.
245   chain_owner owner_; 245   chain_owner owner_;
246   246  
HITCBC 247   103168 ~promise_type() 247   103179 ~promise_type()
248   { 248   {
249   // Still owned only when a closed executor destroyed this 249   // Still owned only when a closed executor destroyed this
250   // trampoline instead of resuming it to unwind the task. 250   // trampoline instead of resuming it to unwind the task.
HITCBC 251   103168 if(task_h_) 251   103179 if(task_h_)
HITCBC 252   100166 task_h_.destroy(); 252   100179 task_h_.destroy();
HITCBC 253   103168 } 253   103179 }
254   254  
HITCBC 255   103168 promise_type( 255   103179 promise_type(
256   Ex& ex, Handlers& h, std::pmr::memory_resource* mr) noexcept 256   Ex& ex, Handlers& h, std::pmr::memory_resource* mr) noexcept
HITCBC 257   103168 : wg_(std::move(ex)) 257   103179 : wg_(std::move(ex))
HITCBC 258   103168 , handlers_(std::move(h)) 258   103179 , handlers_(std::move(h))
HITCBC 259   103168 , mr_(mr) 259   103179 , mr_(mr)
260   { 260   {
HITCBC 261   103168 } 261   103179 }
262   262  
HITCBC 263   103168 static void* operator new( 263   103179 static void* operator new(
264   std::size_t size, Ex const&, Handlers const&, 264   std::size_t size, Ex const&, Handlers const&,
265   std::pmr::memory_resource* mr) 265   std::pmr::memory_resource* mr)
266   { 266   {
HITCBC 267   103168 auto total = size + sizeof(mr); 267   103179 auto total = size + sizeof(mr);
HITCBC 268   103168 void* raw = mr->allocate(total, alignof(std::max_align_t)); 268   103179 void* raw = mr->allocate(total, alignof(std::max_align_t));
HITCBC 269   103168 std::memcpy(static_cast<char*>(raw) + size, &mr, sizeof(mr)); 269   103179 std::memcpy(static_cast<char*>(raw) + size, &mr, sizeof(mr));
HITCBC 270   103168 return raw; 270   103179 return raw;
271   } 271   }
272   272  
HITCBC 273   103168 static void operator delete(void* ptr, std::size_t size) 273   103179 static void operator delete(void* ptr, std::size_t size)
274   { 274   {
275   std::pmr::memory_resource* mr; 275   std::pmr::memory_resource* mr;
HITCBC 276   103168 std::memcpy(&mr, static_cast<char*>(ptr) + size, sizeof(mr)); 276   103179 std::memcpy(&mr, static_cast<char*>(ptr) + size, sizeof(mr));
HITCBC 277   103168 auto total = size + sizeof(mr); 277   103179 auto total = size + sizeof(mr);
HITCBC 278   103168 mr->deallocate(ptr, total, alignof(std::max_align_t)); 278   103179 mr->deallocate(ptr, total, alignof(std::max_align_t));
HITCBC 279   103168 } 279   103179 }
280   280  
HITCBC 281   206335 std::pmr::memory_resource* get_resource() noexcept 281   206357 std::pmr::memory_resource* get_resource() noexcept
282   { 282   {
HITCBC 283   206335 return mr_; 283   206357 return mr_;
284   } 284   }
285   285  
HITCBC 286   103168 run_async_trampoline get_return_object() noexcept 286   103179 run_async_trampoline get_return_object() noexcept
287   { 287   {
288   return run_async_trampoline{ 288   return run_async_trampoline{
HITCBC 289   103168 std::coroutine_handle<promise_type>::from_promise(*this)}; 289   103179 std::coroutine_handle<promise_type>::from_promise(*this)};
290   } 290   }
291   291  
HITCBC 292   103168 std::suspend_always initial_suspend() noexcept 292   103179 std::suspend_always initial_suspend() noexcept
293   { 293   {
HITCBC 294   103168 return {}; 294   103179 return {};
295   } 295   }
296   296  
HITCBC 297   3001 std::suspend_never final_suspend() noexcept 297   2999 std::suspend_never final_suspend() noexcept
298   { 298   {
HITCBC 299   3001 return {}; 299   2999 return {};
300   } 300   }
301   301  
HITCBC 302   3001 void return_void() noexcept 302   2999 void return_void() noexcept
303   { 303   {
HITCBC 304   3001 } 304   2999 }
305   305  
306   // See primary template: an escaping handler exception is fatal. 306   // See primary template: an escaping handler exception is fatal.
307   void unhandled_exception() noexcept { std::terminate(); } // LCOV_EXCL_LINE 307   void unhandled_exception() noexcept { std::terminate(); } // LCOV_EXCL_LINE
308   }; 308   };
309   309  
310   std::coroutine_handle<promise_type> h_; 310   std::coroutine_handle<promise_type> h_;
311   311  
312 - template<IoRunnable Task> 312 + template<ContextRunnable Task>
HITCBC 313   1873 static void invoke_impl(void* p, Handlers& h) 313   1871 static void invoke_impl(void* p, Handlers& h)
314   { 314   {
315   using R = decltype(std::declval<Task&>().await_resume()); 315   using R = decltype(std::declval<Task&>().await_resume());
HITCBC 316   1873 auto& promise = *static_cast<typename Task::promise_type*>(p); 316   1871 auto& promise = *static_cast<typename Task::promise_type*>(p);
HITCBC 317   1873 if(promise.exception()) 317   1871 if(promise.exception())
HITCBC 318   374 h(promise.exception()); 318   374 h(promise.exception());
319   else if constexpr(std::is_void_v<R>) 319   else if constexpr(std::is_void_v<R>)
HITCBC 320   1215 h(); 320   1213 h();
321   else 321   else
HITCBC 322   284 h(std::move(promise.result())); 322   284 h(std::move(promise.result()));
HITCBC 323   1873 } 323   1871 }
324   }; 324   };
325   325  
326   /// Coroutine body for run_async_trampoline - invokes handlers then destroys task. 326   /// Coroutine body for run_async_trampoline - invokes handlers then destroys task.
327   template<class Ex, class Handlers, class Alloc> 327   template<class Ex, class Handlers, class Alloc>
328   run_async_trampoline<Ex, Handlers, Alloc> 328   run_async_trampoline<Ex, Handlers, Alloc>
HITCBC 329   105493 make_trampoline(Ex, Handlers, Alloc) 329   105504 make_trampoline(Ex, Handlers, Alloc)
330   { 330   {
331   // promise_type ctor steals the parameters 331   // promise_type ctor steals the parameters
332   auto& p = co_await get_promise_awaiter< 332   auto& p = co_await get_promise_awaiter<
333   typename run_async_trampoline<Ex, Handlers, Alloc>::promise_type>{}; 333   typename run_async_trampoline<Ex, Handlers, Alloc>::promise_type>{};
334   334  
335   // Guard ensures the task frame is destroyed even when invoke_ 335   // Guard ensures the task frame is destroyed even when invoke_
336   // throws (e.g. default_handler rethrows an unhandled exception). 336   // throws (e.g. default_handler rethrows an unhandled exception).
337   struct frame_guard 337   struct frame_guard
338   { 338   {
339   std::coroutine_handle<>& h; 339   std::coroutine_handle<>& h;
HITCBC 340   5319 ~frame_guard() { std::exchange(h, nullptr).destroy(); } 340   5317 ~frame_guard() { std::exchange(h, nullptr).destroy(); }
341   } guard{p.task_h_}; 341   } guard{p.task_h_};
342   342  
343   // Resumed with the task unfinished: the segment was abandoned, and 343   // Resumed with the task unfinished: the segment was abandoned, and
344   // destroying the task unwinds it without calling a handler. 344   // destroying the task unwinds it without calling a handler.
345   if(!p.owner_.abandoned()) 345   if(!p.owner_.abandoned())
346   p.invoke_(p.task_promise_, p.handlers_); 346   p.invoke_(p.task_promise_, p.handlers_);
HITCBC 347   215604 } 347   215626 }
348   348  
349   } // namespace detail 349   } // namespace detail
350   350  
351   /** Installs the frame allocator, then starts the task on the executor when called once. 351   /** Installs the frame allocator, then starts the task on the executor when called once.
352   352  
353   This wrapper holds the run_async_trampoline coroutine, executor, stop token, 353   This wrapper holds the run_async_trampoline coroutine, executor, stop token,
354   and handlers. The run_async_trampoline is allocated when the wrapper is constructed 354   and handlers. The run_async_trampoline is allocated when the wrapper is constructed
355   (before the task due to C++17 postfix evaluation order). 355   (before the task due to C++17 postfix evaluation order).
356   356  
357   The rvalue ref-qualifier on `operator()` ensures the wrapper can only 357   The rvalue ref-qualifier on `operator()` ensures the wrapper can only
358   be used as a temporary, preventing misuse that would violate LIFO ordering. 358   be used as a temporary, preventing misuse that would violate LIFO ordering.
359   359  
360   @tparam Ex The executor type satisfying the `Executor` concept. 360   @tparam Ex The executor type satisfying the `Executor` concept.
361   @tparam Handlers The handler type (default_handler or handler_pair). 361   @tparam Handlers The handler type (default_handler or handler_pair).
362   @tparam Alloc The allocator type (value type or memory_resource*). 362   @tparam Alloc The allocator type (value type or memory_resource*).
363   363  
364   @par Thread Safety 364   @par Thread Safety
365   The wrapper itself should only be used from one thread. The handlers 365   The wrapper itself should only be used from one thread. The handlers
366   may be invoked from any thread where the executor schedules work. 366   may be invoked from any thread where the executor schedules work.
367   367  
368   @warning **Always construct the task as the direct argument of the 368   @warning **Always construct the task as the direct argument of the
369   two-call expression `run_async(ex)(task)`.** The wrapper's constructor 369   two-call expression `run_async(ex)(task)`.** The wrapper's constructor
370   installs the frame allocator in thread-local storage. The task's 370   installs the frame allocator in thread-local storage. The task's
371   `operator new` reads that thread-local state. Splitting the two calls 371   `operator new` reads that thread-local state. Splitting the two calls
372   apart in any of the following ways allocates the task's coroutine 372   apart in any of the following ways allocates the task's coroutine
373   frame under the wrong allocator. Each does so silently, with no 373   frame under the wrong allocator. Each does so silently, with no
374   compile error. 374   compile error.
375   @li *Stored wrapper.* Storing the wrapper itself 375   @li *Stored wrapper.* Storing the wrapper itself
376   (`auto w = run_async(ex);`) compiles fine. C++17 guaranteed copy 376   (`auto w = run_async(ex);`) compiles fine. C++17 guaranteed copy
377   elision constructs `w` directly from the prvalue. The deleted 377   elision constructs `w` directly from the prvalue. The deleted
378   copy/move constructors are never considered. What the rvalue 378   copy/move constructors are never considered. What the rvalue
379   ref-qualifier on `operator()` rejects is calling through that 379   ref-qualifier on `operator()` rejects is calling through that
380   stored lvalue: `w(my_task())` does not compile, and 380   stored lvalue: `w(my_task())` does not compile, and
381   `std::move(w)(my_task())` is required instead. The silent 381   `std::move(w)(my_task())` is required instead. The silent
382   variant is storing the *task* 382   variant is storing the *task*
383   (`auto t = my_task(); run_async(ex)(std::move(t));`): `t`'s frame 383   (`auto t = my_task(); run_async(ex)(std::move(t));`): `t`'s frame
384   is allocated before `run_async(ex)` ever runs. 384   is allocated before `run_async(ex)` ever runs.
385   @li *Preconstructed task.* Passing an already-constructed task object 385   @li *Preconstructed task.* Passing an already-constructed task object
386   has the same effect as the stored-wrapper case. So does passing a 386   has the same effect as the stored-wrapper case. So does passing a
387   moved-from local, or a task returned from an earlier statement. 387   moved-from local, or a task returned from an earlier statement.
388   The frame exists before the allocator is installed. 388   The frame exists before the allocator is installed.
389   @li *Wrapper function.* Forwarding the task through a helper that 389   @li *Wrapper function.* Forwarding the task through a helper that
390   itself performs the two-call pattern constructs the task as an 390   itself performs the two-call pattern constructs the task as an
391   argument to the helper. It is therefore constructed before the 391   argument to the helper. It is therefore constructed before the
392   helper's body runs, and so before `run_async` runs. An example is 392   helper's body runs, and so before `run_async` runs. An example is
393   `submit(ex, my_task())`, where `submit` calls 393   `submit(ex, my_task())`, where `submit` calls
394   `run_async(ex)(std::forward<Task>(t))` internally. 394   `run_async(ex)(std::forward<Task>(t))` internally.
395   395  
396   See the Frame Allocators guide 396   See the Frame Allocators guide
397   (`doc/modules/ROOT/pages/4.coroutines/4g.allocators.adoc`) for the full 397   (`doc/modules/ROOT/pages/4.coroutines/4g.allocators.adoc`) for the full
398   C++17-evaluation-order rationale behind this constraint. 398   C++17-evaluation-order rationale behind this constraint.
399   399  
400   @par Example 400   @par Example
401   @par !example example 401   @par !example example
402   402  
403   403  
404   @see run_async 404   @see run_async
405   */ 405   */
406   template<Executor Ex, class Handlers, class Alloc> 406   template<Executor Ex, class Handlers, class Alloc>
407   class [[nodiscard]] run_async_wrapper 407   class [[nodiscard]] run_async_wrapper
408   { 408   {
409   detail::run_async_trampoline<Ex, Handlers, Alloc> tr_; 409   detail::run_async_trampoline<Ex, Handlers, Alloc> tr_;
410   std::stop_token st_; 410   std::stop_token st_;
411   std::pmr::memory_resource* saved_tls_; 411   std::pmr::memory_resource* saved_tls_;
412   412  
413   public: 413   public:
414   /** Construct the wrapper and install the frame allocator. 414   /** Construct the wrapper and install the frame allocator.
415   415  
416   Builds the trampoline and saves the current thread-local frame 416   Builds the trampoline and saves the current thread-local frame
417   allocator. Then installs the trampoline's resource as the new 417   allocator. Then installs the trampoline's resource as the new
418   thread-local allocator. The task frame, evaluated as the argument 418   thread-local allocator. The task frame, evaluated as the argument
419   to @ref operator(), is therefore allocated from that resource. 419   to @ref operator(), is therefore allocated from that resource.
420   420  
421   @param ex The executor on which the task runs. 421   @param ex The executor on which the task runs.
422   @param st The stop token for cooperative cancellation. 422   @param st The stop token for cooperative cancellation.
423   @param h The completion handlers. 423   @param h The completion handlers.
424   @param a The allocator for frame allocation. 424   @param a The allocator for frame allocation.
425   425  
426   @note When `Alloc` is not `std::pmr::memory_resource*` it must be 426   @note When `Alloc` is not `std::pmr::memory_resource*` it must be
427   nothrow move constructible (enforced by a `static_assert`), which 427   nothrow move constructible (enforced by a `static_assert`), which
428   is what allows this constructor to be `noexcept`. If allocating 428   is what allows this constructor to be `noexcept`. If allocating
429   the launch state throws, `std::terminate` is called. 429   the launch state throws, `std::terminate` is called.
430   */ 430   */
HITCBC 431   105493 run_async_wrapper( 431   105504 run_async_wrapper(
432   Ex ex, 432   Ex ex,
433   std::stop_token st, 433   std::stop_token st,
434   Handlers h, 434   Handlers h,
435   Alloc a) noexcept 435   Alloc a) noexcept
HITCBC 436   106629 : tr_(detail::make_trampoline<Ex, Handlers, Alloc>( 436   106640 : tr_(detail::make_trampoline<Ex, Handlers, Alloc>(
HITCBC 437   108938 std::move(ex), std::move(h), std::move(a))) 437   108949 std::move(ex), std::move(h), std::move(a)))
HITCBC 438   105493 , st_(std::move(st)) 438   105504 , st_(std::move(st))
HITCBC 439   105493 , saved_tls_(get_current_frame_allocator()) 439   105504 , saved_tls_(get_current_frame_allocator())
440   { 440   {
441   if constexpr (!std::is_same_v<Alloc, std::pmr::memory_resource*>) 441   if constexpr (!std::is_same_v<Alloc, std::pmr::memory_resource*>)
442   { 442   {
443   static_assert( 443   static_assert(
444   std::is_nothrow_move_constructible_v<Alloc>, 444   std::is_nothrow_move_constructible_v<Alloc>,
445   "Allocator must be nothrow move constructible"); 445   "Allocator must be nothrow move constructible");
446   } 446   }
447   // Set TLS before task argument is evaluated 447   // Set TLS before task argument is evaluated
HITCBC 448   105493 set_current_frame_allocator(tr_.h_.promise().get_resource()); 448   105504 set_current_frame_allocator(tr_.h_.promise().get_resource());
HITCBC 449   105493 } 449   105504 }
450   450  
451   /** Restore the previously installed frame allocator. 451   /** Restore the previously installed frame allocator.
452   452  
453   Resets the thread-local frame allocator to the value saved at 453   Resets the thread-local frame allocator to the value saved at
454   construction. A stale pointer to the trampoline's resource 454   construction. A stale pointer to the trampoline's resource
455   therefore does not outlive the execution context that owns it. 455   therefore does not outlive the execution context that owns it.
456   If no task was started, for example because evaluating the task 456   If no task was started, for example because evaluating the task
457   argument threw, the unused launch state is destroyed, releasing 457   argument threw, the unused launch state is destroyed, releasing
458   the executor's work. 458   the executor's work.
459   */ 459   */
HITCBC 460   105493 ~run_async_wrapper() 460   105504 ~run_async_wrapper()
461   { 461   {
HITCBC 462   105493 if(tr_.h_) 462   105504 if(tr_.h_)
HITCBC 463   2 tr_.h_.destroy(); 463   2 tr_.h_.destroy();
HITCBC 464   105493 set_current_frame_allocator(saved_tls_); 464   105504 set_current_frame_allocator(saved_tls_);
HITCBC 465   105493 } 465   105504 }
466   466  
467   // Non-copyable, non-movable (must be used immediately) 467   // Non-copyable, non-movable (must be used immediately)
468   468  
469   /** Copy construction is disabled; the wrapper must be used immediately. 469   /** Copy construction is disabled; the wrapper must be used immediately.
470   470  
471   @param other The wrapper that would be copied. 471   @param other The wrapper that would be copied.
472   */ 472   */
473   run_async_wrapper(run_async_wrapper const& other) = delete; 473   run_async_wrapper(run_async_wrapper const& other) = delete;
474   474  
475   /** Move construction is disabled; the wrapper must be used immediately. 475   /** Move construction is disabled; the wrapper must be used immediately.
476   476  
477   @param other The wrapper that would be moved from. 477   @param other The wrapper that would be moved from.
478   */ 478   */
479   run_async_wrapper(run_async_wrapper&& other) = delete; 479   run_async_wrapper(run_async_wrapper&& other) = delete;
480   480  
481   /** Copy assignment is disabled; the wrapper must be used immediately. 481   /** Copy assignment is disabled; the wrapper must be used immediately.
482   482  
483   @param other The wrapper that would be assigned from. 483   @param other The wrapper that would be assigned from.
484   484  
485   @return A reference to `*this`. 485   @return A reference to `*this`.
486   */ 486   */
487   run_async_wrapper& operator=(run_async_wrapper const& other) = delete; 487   run_async_wrapper& operator=(run_async_wrapper const& other) = delete;
488   488  
489   /** Move assignment is disabled; the wrapper must be used immediately. 489   /** Move assignment is disabled; the wrapper must be used immediately.
490   490  
491   @param other The wrapper that would be moved from. 491   @param other The wrapper that would be moved from.
492   492  
493   @return A reference to `*this`. 493   @return A reference to `*this`.
494   */ 494   */
495   run_async_wrapper& operator=(run_async_wrapper&& other) = delete; 495   run_async_wrapper& operator=(run_async_wrapper&& other) = delete;
496   496  
497   /** Start the task for execution. 497   /** Start the task for execution.
498   498  
499   This operator accepts a task and starts it on the executor. 499   This operator accepts a task and starts it on the executor.
500   The rvalue ref-qualifier ensures the wrapper is consumed, enforcing 500   The rvalue ref-qualifier ensures the wrapper is consumed, enforcing
501   correct LIFO destruction order. 501   correct LIFO destruction order.
502   502  
503 - The `io_env` constructed for the task is owned by the trampoline 503 + The `await_context` constructed for the task is owned by the trampoline
504   coroutine and is guaranteed to outlive the task and all awaitables 504   coroutine and is guaranteed to outlive the task and all awaitables
505 - in its chain. Awaitables may store `io_env const*` without concern 505 + in its chain. Awaitables may store `await_context const*` without concern
506   for dangling references. 506   for dangling references.
507   507  
508   @throws Any exception thrown by the executor's `dispatch`. The 508   @throws Any exception thrown by the executor's `dispatch`. The
509   task is then destroyed without running, and the executor's 509   task is then destroyed without running, and the executor's
510   work is released. 510   work is released.
511   511  
512 - @tparam Task The IoRunnable type. 512 + @tparam Task The ContextRunnable type.
513   513  
514   @param t The task to execute. Ownership is transferred to the 514   @param t The task to execute. Ownership is transferred to the
515   run_async_trampoline which destroys it after completion. 515   run_async_trampoline which destroys it after completion.
516   */ 516   */
517 - template<IoRunnable Task> 517 + template<ContextRunnable Task>
HITCBC 518   105492 void operator()(Task t) && 518   105503 void operator()(Task t) &&
519   { 519   {
HITCBC 520   105492 auto task_h = t.handle(); 520   105503 auto task_h = t.handle();
HITCBC 521   105492 auto& task_promise = task_h.promise(); 521   105503 auto& task_promise = task_h.promise();
HITCBC 522   105492 t.release(); 522   105503 t.release();
523   523  
HITCBC 524   105492 auto& p = tr_.h_.promise(); 524   105503 auto& p = tr_.h_.promise();
525   525  
526   // Inject Task-specific invoke function 526   // Inject Task-specific invoke function
HITCBC 527   105492 p.invoke_ = detail::run_async_trampoline<Ex, Handlers, Alloc>::template invoke_impl<Task>; 527   105503 p.invoke_ = detail::run_async_trampoline<Ex, Handlers, Alloc>::template invoke_impl<Task>;
HITCBC 528   105492 p.task_promise_ = &task_promise; 528   105503 p.task_promise_ = &task_promise;
HITCBC 529   105492 p.task_h_ = task_h; 529   105503 p.task_h_ = task_h;
530   530  
531   // Setup task's continuation to return to run_async_trampoline 531   // Setup task's continuation to return to run_async_trampoline
HITCBC 532   105492 task_promise.set_continuation(tr_.h_); 532   105503 task_promise.set_continuation(tr_.h_);
HITCBC 533   105492 p.owner_.bind(tr_.h_, p.wg_.executor()); 533   105503 p.owner_.bind(tr_.h_, p.wg_.executor());
HITCBC 534   210984 p.env_ = {p.wg_.executor(), st_, p.get_resource(), &p.owner_}; 534   211006 p.env_ = {p.wg_.executor(), st_, p.get_resource(), &p.owner_};
HITCBC 535   105492 task_promise.set_environment(&p.env_); 535   105503 task_promise.set_environment(&p.env_);
536   536  
537   // Start task through executor. 537   // Start task through executor.
538   // safe_resume is not needed here: TLS is already saved in the 538   // safe_resume is not needed here: TLS is already saved in the
539   // constructor (saved_tls_) and restored in the destructor. 539   // constructor (saved_tls_) and restored in the destructor.
HITCBC 540   105492 p.task_cont_.h = task_h; 540   105503 p.task_cont_.h = task_h;
HITCBC 541   105492 p.task_cont_.owner = &p.owner_; 541   105503 p.task_cont_.owner = &p.owner_;
542   // If dispatch throws, the destructor destroys the trampoline 542   // If dispatch throws, the destructor destroys the trampoline
543   // and the unstarted task with it. 543   // and the unstarted task with it.
HITCBC 544   105492 auto next = p.wg_.executor().dispatch(p.task_cont_); 544   105503 auto next = p.wg_.executor().dispatch(p.task_cont_);
545   // From here the trampoline destroys itself when the task ends. 545   // From here the trampoline destroys itself when the task ends.
HITCBC 546   105491 tr_.h_ = nullptr; 546   105502 tr_.h_ = nullptr;
HITCBC 547   105491 next.resume(); 547   105502 next.resume();
HITCBC 548   210983 } 548   211005 }
549   }; 549   };
550   550  
551   // Executor only (uses default recycling allocator) 551   // Executor only (uses default recycling allocator)
552   552  
553   /** Bind an executor to produce a launcher. Invoke the launcher with a task to start it. 553   /** Bind an executor to produce a launcher. Invoke the launcher with a task to start it.
554   554  
555   Use this to start execution of a `task<T>` that was created lazily. 555   Use this to start execution of a `task<T>` that was created lazily.
556   The returned wrapper must be immediately invoked with the task; 556   The returned wrapper must be immediately invoked with the task;
557   storing the wrapper and calling it later violates LIFO ordering. 557   storing the wrapper and calling it later violates LIFO ordering.
558   558  
559   Uses the default recycling frame allocator for coroutine frames. 559   Uses the default recycling frame allocator for coroutine frames.
560   With no handlers, the result is discarded. An unhandled exception 560   With no handlers, the result is discarded. An unhandled exception
561   thrown by the task calls `std::terminate`. To catch it instead, pass 561   thrown by the task calls `std::terminate`. To catch it instead, pass
562   an error handler that receives it as an `exception_ptr`, or `co_await` 562   an error handler that receives it as an `exception_ptr`, or `co_await`
563   the work inside a coroutine. 563   the work inside a coroutine.
564   564  
565   Construct the task as the direct argument of the two-call expression 565   Construct the task as the direct argument of the two-call expression
566   `run_async(ex)(task)`. 566   `run_async(ex)(task)`.
567   567  
568   @par Thread Safety 568   @par Thread Safety
569   The wrapper itself should only be used from one thread. 569   The wrapper itself should only be used from one thread.
570   570  
571   @par Example 571   @par Example
572   @par !example example_1 572   @par !example example_1
573   573  
574   574  
575   @param ex The executor to execute the task on. 575   @param ex The executor to execute the task on.
576   576  
577   @return A wrapper that accepts a `task<T>` for immediate execution. 577   @return A wrapper that accepts a `task<T>` for immediate execution.
578   578  
579   @see task 579   @see task
580   @see Executor 580   @see Executor
581   @see run_async_wrapper 581   @see run_async_wrapper
582   */ 582   */
583   template<Executor Ex> 583   template<Executor Ex>
584   [[nodiscard]] auto 584   [[nodiscard]] auto
HITCBC 585   101423 run_async(Ex ex) 585   101434 run_async(Ex ex)
586   { 586   {
HITCBC 587   101423 auto* mr = ex.context().get_frame_allocator(); 587   101434 auto* mr = ex.context().get_frame_allocator();
588   return run_async_wrapper<Ex, detail::default_handler, std::pmr::memory_resource*>( 588   return run_async_wrapper<Ex, detail::default_handler, std::pmr::memory_resource*>(
HITCBC 589   101423 std::move(ex), 589   101434 std::move(ex),
HITCBC 590   202846 std::stop_token{}, 590   202868 std::stop_token{},
591   detail::default_handler{}, 591   detail::default_handler{},
HITCBC 592   101423 mr); 592   101434 mr);
593   } 593   }
594   594  
595   /** Bind an executor and a result handler to produce a launcher. Invoke the launcher with a task to start it. 595   /** Bind an executor and a result handler to produce a launcher. Invoke the launcher with a task to start it.
596   596  
597   The handler `h1` is called with the task's result on success. If `h1` 597   The handler `h1` is called with the task's result on success. If `h1`
598   is also invocable with `std::exception_ptr`, it handles exceptions too. 598   is also invocable with `std::exception_ptr`, it handles exceptions too.
599   Otherwise, an unhandled exception calls `std::terminate`. 599   Otherwise, an unhandled exception calls `std::terminate`.
600   If the task is abandoned before it completes, as a context's teardown 600   If the task is abandoned before it completes, as a context's teardown
601   does, no handler is called; the handlers are destroyed with it. 601   does, no handler is called; the handlers are destroyed with it.
602   602  
603   Construct the task as the direct argument of the two-call expression 603   Construct the task as the direct argument of the two-call expression
604   `run_async(ex)(task)`. 604   `run_async(ex)(task)`.
605   605  
606   @par Thread Safety 606   @par Thread Safety
607   The wrapper itself should only be used from one thread. The handlers 607   The wrapper itself should only be used from one thread. The handlers
608   may be invoked from any thread where the executor schedules work. 608   may be invoked from any thread where the executor schedules work.
609   609  
610   @par Example 610   @par Example
611   @par !example example_2 611   @par !example example_2
612   612  
613   613  
614   @param ex The executor to execute the task on. 614   @param ex The executor to execute the task on.
615   @param h1 The handler to invoke with the result (and optionally exception). 615   @param h1 The handler to invoke with the result (and optionally exception).
616   616  
617   @return A wrapper that accepts a `task<T>` for immediate execution. 617   @return A wrapper that accepts a `task<T>` for immediate execution.
618   618  
619   @see task 619   @see task
620   @see Executor 620   @see Executor
621   @see run_async_wrapper 621   @see run_async_wrapper
622   */ 622   */
623   template<Executor Ex, class H1> 623   template<Executor Ex, class H1>
624   requires detail::RunAsyncHandler<H1> 624   requires detail::RunAsyncHandler<H1>
625   [[nodiscard]] auto 625   [[nodiscard]] auto
HITCBC 626   111 run_async(Ex ex, H1 h1) 626   111 run_async(Ex ex, H1 h1)
627   { 627   {
HITCBC 628   111 auto* mr = ex.context().get_frame_allocator(); 628   111 auto* mr = ex.context().get_frame_allocator();
629   return run_async_wrapper<Ex, detail::handler_pair<H1, detail::default_handler>, std::pmr::memory_resource*>( 629   return run_async_wrapper<Ex, detail::handler_pair<H1, detail::default_handler>, std::pmr::memory_resource*>(
HITCBC 630   111 std::move(ex), 630   111 std::move(ex),
HITCBC 631   117 std::stop_token{}, 631   117 std::stop_token{},
HITCBC 632   105 detail::handler_pair<H1, detail::default_handler>{std::move(h1)}, 632   105 detail::handler_pair<H1, detail::default_handler>{std::move(h1)},
HITCBC 633   216 mr); 633   216 mr);
634   } 634   }
635   635  
636   /** Bind an executor and separate result and error handlers to produce a launcher. Invoke the launcher with a task to start it. 636   /** Bind an executor and separate result and error handlers to produce a launcher. Invoke the launcher with a task to start it.
637   637  
638   The handler `h1` is called with the task's result on success. 638   The handler `h1` is called with the task's result on success.
639   The handler `h2` is called with the exception_ptr on failure. 639   The handler `h2` is called with the exception_ptr on failure.
640   If the task is abandoned before it completes, as a context's teardown 640   If the task is abandoned before it completes, as a context's teardown
641   does, no handler is called; the handlers are destroyed with it. 641   does, no handler is called; the handlers are destroyed with it.
642   642  
643   Construct the task as the direct argument of the two-call expression 643   Construct the task as the direct argument of the two-call expression
644   `run_async(ex)(task)`. 644   `run_async(ex)(task)`.
645   645  
646   @par Thread Safety 646   @par Thread Safety
647   The wrapper itself should only be used from one thread. The handlers 647   The wrapper itself should only be used from one thread. The handlers
648   may be invoked from any thread where the executor schedules work. 648   may be invoked from any thread where the executor schedules work.
649   649  
650   @par Example 650   @par Example
651   @par !example example_3 651   @par !example example_3
652   652  
653   653  
654   @param ex The executor to execute the task on. 654   @param ex The executor to execute the task on.
655   @param h1 The handler to invoke with the result on success. 655   @param h1 The handler to invoke with the result on success.
656   @param h2 The handler to invoke with the exception on failure. 656   @param h2 The handler to invoke with the exception on failure.
657   657  
658   @return A wrapper that accepts a `task<T>` for immediate execution. 658   @return A wrapper that accepts a `task<T>` for immediate execution.
659   659  
660   @see task 660   @see task
661   @see Executor 661   @see Executor
662   @see run_async_wrapper 662   @see run_async_wrapper
663   */ 663   */
664   template<Executor Ex, class H1, class H2> 664   template<Executor Ex, class H1, class H2>
665   requires (detail::RunAsyncHandler<H1> && detail::RunAsyncHandler<H2>) 665   requires (detail::RunAsyncHandler<H1> && detail::RunAsyncHandler<H2>)
666   [[nodiscard]] auto 666   [[nodiscard]] auto
HITCBC 667   105 run_async(Ex ex, H1 h1, H2 h2) 667   105 run_async(Ex ex, H1 h1, H2 h2)
668   { 668   {
HITCBC 669   105 auto* mr = ex.context().get_frame_allocator(); 669   105 auto* mr = ex.context().get_frame_allocator();
670   return run_async_wrapper<Ex, detail::handler_pair<H1, H2>, std::pmr::memory_resource*>( 670   return run_async_wrapper<Ex, detail::handler_pair<H1, H2>, std::pmr::memory_resource*>(
HITCBC 671   105 std::move(ex), 671   105 std::move(ex),
HITCBC 672   108 std::stop_token{}, 672   108 std::stop_token{},
HITCBC 673   102 detail::handler_pair<H1, H2>{std::move(h1), std::move(h2)}, 673   102 detail::handler_pair<H1, H2>{std::move(h1), std::move(h2)},
HITCBC 674   207 mr); 674   207 mr);
HITCBC 675   3 } 675   3 }
676   676  
677   // Ex + stop_token 677   // Ex + stop_token
678   678  
679   /** Bind an executor and a stop token to produce a launcher. Invoke the launcher with a task to start it. 679   /** Bind an executor and a stop token to produce a launcher. Invoke the launcher with a task to start it.
680   680  
681   The stop token is propagated to the task, enabling cooperative 681   The stop token is propagated to the task, enabling cooperative
682   cancellation. With no handlers, the result is discarded and an 682   cancellation. With no handlers, the result is discarded and an
683   unhandled exception calls `std::terminate`. 683   unhandled exception calls `std::terminate`.
684   684  
685   Construct the task as the direct argument of the two-call expression 685   Construct the task as the direct argument of the two-call expression
686   `run_async(ex)(task)`. 686   `run_async(ex)(task)`.
687   687  
688   @par Thread Safety 688   @par Thread Safety
689   The wrapper itself should only be used from one thread. 689   The wrapper itself should only be used from one thread.
690   690  
691   @par Example 691   @par Example
692   @par !example example_4 692   @par !example example_4
693   693  
694   694  
695   @param ex The executor to execute the task on. 695   @param ex The executor to execute the task on.
696   @param st The stop token for cooperative cancellation. 696   @param st The stop token for cooperative cancellation.
697   697  
698   @return A wrapper that accepts a `task<T>` for immediate execution. 698   @return A wrapper that accepts a `task<T>` for immediate execution.
699   699  
700   @see task 700   @see task
701   @see Executor 701   @see Executor
702   @see run_async_wrapper 702   @see run_async_wrapper
703   */ 703   */
704   template<Executor Ex> 704   template<Executor Ex>
705   [[nodiscard]] auto 705   [[nodiscard]] auto
HITCBC 706   378 run_async(Ex ex, std::stop_token st) 706   378 run_async(Ex ex, std::stop_token st)
707   { 707   {
HITCBC 708   378 auto* mr = ex.context().get_frame_allocator(); 708   378 auto* mr = ex.context().get_frame_allocator();
709   return run_async_wrapper<Ex, detail::default_handler, std::pmr::memory_resource*>( 709   return run_async_wrapper<Ex, detail::default_handler, std::pmr::memory_resource*>(
HITCBC 710   378 std::move(ex), 710   378 std::move(ex),
HITCBC 711   378 std::move(st), 711   378 std::move(st),
712   detail::default_handler{}, 712   detail::default_handler{},
HITCBC 713   756 mr); 713   756 mr);
714   } 714   }
715   715  
716   /** Bind an executor, a stop token, and a result handler to produce a launcher. Invoke the launcher with a task to start it. 716   /** Bind an executor, a stop token, and a result handler to produce a launcher. Invoke the launcher with a task to start it.
717   717  
718   The stop token is propagated to the task for cooperative cancellation. 718   The stop token is propagated to the task for cooperative cancellation.
719   The handler `h1` is called with the result on success, and optionally 719   The handler `h1` is called with the result on success, and optionally
720   with exception_ptr if it accepts that type. 720   with exception_ptr if it accepts that type.
721   721  
722   Construct the task as the direct argument of the two-call expression 722   Construct the task as the direct argument of the two-call expression
723   `run_async(ex)(task)`. 723   `run_async(ex)(task)`.
724   724  
725   @par Thread Safety 725   @par Thread Safety
726   The wrapper itself should only be used from one thread. The handlers 726   The wrapper itself should only be used from one thread. The handlers
727   may be invoked from any thread where the executor schedules work. 727   may be invoked from any thread where the executor schedules work.
728   728  
729   @param ex The executor to execute the task on. 729   @param ex The executor to execute the task on.
730   @param st The stop token for cooperative cancellation. 730   @param st The stop token for cooperative cancellation.
731   @param h1 The handler to invoke with the result (and optionally exception). 731   @param h1 The handler to invoke with the result (and optionally exception).
732   732  
733   @return A wrapper that accepts a `task<T>` for immediate execution. 733   @return A wrapper that accepts a `task<T>` for immediate execution.
734   734  
735   @see task 735   @see task
736   @see Executor 736   @see Executor
737   @see run_async_wrapper 737   @see run_async_wrapper
738   */ 738   */
739   template<Executor Ex, class H1> 739   template<Executor Ex, class H1>
740   requires detail::RunAsyncHandler<H1> 740   requires detail::RunAsyncHandler<H1>
741   [[nodiscard]] auto 741   [[nodiscard]] auto
HITCBC 742   1118 run_async(Ex ex, std::stop_token st, H1 h1) 742   1118 run_async(Ex ex, std::stop_token st, H1 h1)
743   { 743   {
HITCBC 744   1118 auto* mr = ex.context().get_frame_allocator(); 744   1118 auto* mr = ex.context().get_frame_allocator();
745   return run_async_wrapper<Ex, detail::handler_pair<H1, detail::default_handler>, std::pmr::memory_resource*>( 745   return run_async_wrapper<Ex, detail::handler_pair<H1, detail::default_handler>, std::pmr::memory_resource*>(
HITCBC 746   1118 std::move(ex), 746   1118 std::move(ex),
HITCBC 747   1118 std::move(st), 747   1118 std::move(st),
HITCBC 748   1118 detail::handler_pair<H1, detail::default_handler>{std::move(h1)}, 748   1118 detail::handler_pair<H1, detail::default_handler>{std::move(h1)},
HITCBC 749   2236 mr); 749   2236 mr);
HITCBC 750   1113 } 750   1113 }
751   751  
752   /** Bind an executor, a stop token, and separate result and error handlers to produce a launcher. Invoke the launcher with a task to start it. 752   /** Bind an executor, a stop token, and separate result and error handlers to produce a launcher. Invoke the launcher with a task to start it.
753   753  
754   The stop token is propagated to the task for cooperative cancellation. 754   The stop token is propagated to the task for cooperative cancellation.
755   The handler `h1` is called on success, `h2` on failure. 755   The handler `h1` is called on success, `h2` on failure.
756   756  
757   Construct the task as the direct argument of the two-call expression 757   Construct the task as the direct argument of the two-call expression
758   `run_async(ex)(task)`. 758   `run_async(ex)(task)`.
759   759  
760   @par Thread Safety 760   @par Thread Safety
761   The wrapper itself should only be used from one thread. The handlers 761   The wrapper itself should only be used from one thread. The handlers
762   may be invoked from any thread where the executor schedules work. 762   may be invoked from any thread where the executor schedules work.
763   763  
764   @param ex The executor to execute the task on. 764   @param ex The executor to execute the task on.
765   @param st The stop token for cooperative cancellation. 765   @param st The stop token for cooperative cancellation.
766   @param h1 The handler to invoke with the result on success. 766   @param h1 The handler to invoke with the result on success.
767   @param h2 The handler to invoke with the exception on failure. 767   @param h2 The handler to invoke with the exception on failure.
768   768  
769   @return A wrapper that accepts a `task<T>` for immediate execution. 769   @return A wrapper that accepts a `task<T>` for immediate execution.
770   770  
771   @see task 771   @see task
772   @see Executor 772   @see Executor
773   @see run_async_wrapper 773   @see run_async_wrapper
774   */ 774   */
775   template<Executor Ex, class H1, class H2> 775   template<Executor Ex, class H1, class H2>
776   requires (detail::RunAsyncHandler<H1> && detail::RunAsyncHandler<H2>) 776   requires (detail::RunAsyncHandler<H1> && detail::RunAsyncHandler<H2>)
777   [[nodiscard]] auto 777   [[nodiscard]] auto
HITCBC 778   12 run_async(Ex ex, std::stop_token st, H1 h1, H2 h2) 778   12 run_async(Ex ex, std::stop_token st, H1 h1, H2 h2)
779   { 779   {
HITCBC 780   12 auto* mr = ex.context().get_frame_allocator(); 780   12 auto* mr = ex.context().get_frame_allocator();
781   return run_async_wrapper<Ex, detail::handler_pair<H1, H2>, std::pmr::memory_resource*>( 781   return run_async_wrapper<Ex, detail::handler_pair<H1, H2>, std::pmr::memory_resource*>(
HITCBC 782   12 std::move(ex), 782   12 std::move(ex),
HITCBC 783   12 std::move(st), 783   12 std::move(st),
HITCBC 784   12 detail::handler_pair<H1, H2>{std::move(h1), std::move(h2)}, 784   12 detail::handler_pair<H1, H2>{std::move(h1), std::move(h2)},
HITCBC 785   24 mr); 785   24 mr);
786   } 786   }
787   787  
788   // Ex + memory_resource* 788   // Ex + memory_resource*
789   789  
790   /** Bind an executor and a memory resource to produce a launcher. Invoke the launcher with a task to start it. 790   /** Bind an executor and a memory resource to produce a launcher. Invoke the launcher with a task to start it.
791   791  
792   The memory resource is used for coroutine frame allocation. 792   The memory resource is used for coroutine frame allocation.
793   793  
794   Construct the task as the direct argument of the two-call expression 794   Construct the task as the direct argument of the two-call expression
795   `run_async(ex)(task)`. 795   `run_async(ex)(task)`.
796   796  
797   @par Thread Safety 797   @par Thread Safety
798   The wrapper itself should only be used from one thread. 798   The wrapper itself should only be used from one thread.
799   799  
800   @pre `mr` outlives every task started through the returned wrapper. 800   @pre `mr` outlives every task started through the returned wrapper.
801   801  
802   @param ex The executor to execute the task on. 802   @param ex The executor to execute the task on.
803   @param mr The memory resource for frame allocation. 803   @param mr The memory resource for frame allocation.
804   804  
805   @return A wrapper that accepts a `task<T>` for immediate execution. 805   @return A wrapper that accepts a `task<T>` for immediate execution.
806   806  
807   @see task 807   @see task
808   @see Executor 808   @see Executor
809   @see run_async_wrapper 809   @see run_async_wrapper
810   */ 810   */
811   template<Executor Ex> 811   template<Executor Ex>
812   [[nodiscard]] auto 812   [[nodiscard]] auto
HITCBC 813   16 run_async(Ex ex, std::pmr::memory_resource* mr) 813   16 run_async(Ex ex, std::pmr::memory_resource* mr)
814   { 814   {
815   return run_async_wrapper<Ex, detail::default_handler, std::pmr::memory_resource*>( 815   return run_async_wrapper<Ex, detail::default_handler, std::pmr::memory_resource*>(
HITCBC 816   16 std::move(ex), 816   16 std::move(ex),
HITCBC 817   32 std::stop_token{}, 817   32 std::stop_token{},
818   detail::default_handler{}, 818   detail::default_handler{},
HITCBC 819   16 mr); 819   16 mr);
820   } 820   }
821   821  
822   /** Bind an executor, a memory resource, and a result handler to produce a launcher. Invoke the launcher with a task to start it. 822   /** Bind an executor, a memory resource, and a result handler to produce a launcher. Invoke the launcher with a task to start it.
823   823  
824   Construct the task as the direct argument of the two-call expression 824   Construct the task as the direct argument of the two-call expression
825   `run_async(ex)(task)`. 825   `run_async(ex)(task)`.
826   826  
827   @par Thread Safety 827   @par Thread Safety
828   The wrapper itself should only be used from one thread. The handlers 828   The wrapper itself should only be used from one thread. The handlers
829   may be invoked from any thread where the executor schedules work. 829   may be invoked from any thread where the executor schedules work.
830   830  
831   @pre `mr` outlives every task started through the returned wrapper. 831   @pre `mr` outlives every task started through the returned wrapper.
832   832  
833   @param ex The executor to execute the task on. 833   @param ex The executor to execute the task on.
834   @param mr The memory resource for frame allocation. 834   @param mr The memory resource for frame allocation.
835   @param h1 The handler to invoke with the result (and optionally exception). 835   @param h1 The handler to invoke with the result (and optionally exception).
836   836  
837   @return A wrapper that accepts a `task<T>` for immediate execution. 837   @return A wrapper that accepts a `task<T>` for immediate execution.
838   838  
839   @see task 839   @see task
840   @see Executor 840   @see Executor
841   @see run_async_wrapper 841   @see run_async_wrapper
842   */ 842   */
843   template<Executor Ex, class H1> 843   template<Executor Ex, class H1>
844   [[nodiscard]] auto 844   [[nodiscard]] auto
HITCBC 845   1 run_async(Ex ex, std::pmr::memory_resource* mr, H1 h1) 845   1 run_async(Ex ex, std::pmr::memory_resource* mr, H1 h1)
846   { 846   {
847   return run_async_wrapper<Ex, detail::handler_pair<H1, detail::default_handler>, std::pmr::memory_resource*>( 847   return run_async_wrapper<Ex, detail::handler_pair<H1, detail::default_handler>, std::pmr::memory_resource*>(
HITCBC 848   1 std::move(ex), 848   1 std::move(ex),
HITCBC 849   1 std::stop_token{}, 849   1 std::stop_token{},
HITCBC 850   1 detail::handler_pair<H1, detail::default_handler>{std::move(h1)}, 850   1 detail::handler_pair<H1, detail::default_handler>{std::move(h1)},
HITCBC 851   2 mr); 851   2 mr);
852   } 852   }
853   853  
854   /** Bind an executor, a memory resource, and separate result and error handlers to produce a launcher. Invoke the launcher with a task to start it. 854   /** Bind an executor, a memory resource, and separate result and error handlers to produce a launcher. Invoke the launcher with a task to start it.
855   855  
856   Construct the task as the direct argument of the two-call expression 856   Construct the task as the direct argument of the two-call expression
857   `run_async(ex)(task)`. 857   `run_async(ex)(task)`.
858   858  
859   @par Thread Safety 859   @par Thread Safety
860   The wrapper itself should only be used from one thread. The handlers 860   The wrapper itself should only be used from one thread. The handlers
861   may be invoked from any thread where the executor schedules work. 861   may be invoked from any thread where the executor schedules work.
862   862  
863   @pre `mr` outlives every task started through the returned wrapper. 863   @pre `mr` outlives every task started through the returned wrapper.
864   864  
865   @param ex The executor to execute the task on. 865   @param ex The executor to execute the task on.
866   @param mr The memory resource for frame allocation. 866   @param mr The memory resource for frame allocation.
867   @param h1 The handler to invoke with the result on success. 867   @param h1 The handler to invoke with the result on success.
868   @param h2 The handler to invoke with the exception on failure. 868   @param h2 The handler to invoke with the exception on failure.
869   869  
870   @return A wrapper that accepts a `task<T>` for immediate execution. 870   @return A wrapper that accepts a `task<T>` for immediate execution.
871   871  
872   @see task 872   @see task
873   @see Executor 873   @see Executor
874   @see run_async_wrapper 874   @see run_async_wrapper
875   */ 875   */
876   template<Executor Ex, class H1, class H2> 876   template<Executor Ex, class H1, class H2>
877   [[nodiscard]] auto 877   [[nodiscard]] auto
HITCBC 878   2 run_async(Ex ex, std::pmr::memory_resource* mr, H1 h1, H2 h2) 878   2 run_async(Ex ex, std::pmr::memory_resource* mr, H1 h1, H2 h2)
879   { 879   {
880   return run_async_wrapper<Ex, detail::handler_pair<H1, H2>, std::pmr::memory_resource*>( 880   return run_async_wrapper<Ex, detail::handler_pair<H1, H2>, std::pmr::memory_resource*>(
HITCBC 881   2 std::move(ex), 881   2 std::move(ex),
HITCBC 882   2 std::stop_token{}, 882   2 std::stop_token{},
HITCBC 883   2 detail::handler_pair<H1, H2>{std::move(h1), std::move(h2)}, 883   2 detail::handler_pair<H1, H2>{std::move(h1), std::move(h2)},
HITCBC 884   4 mr); 884   4 mr);
HITCBC 885   2 } 885   2 }
886   886  
887   // Ex + stop_token + memory_resource* 887   // Ex + stop_token + memory_resource*
888   888  
889   /** Bind an executor, a stop token, and a memory resource to produce a launcher. Invoke the launcher with a task to start it. 889   /** Bind an executor, a stop token, and a memory resource to produce a launcher. Invoke the launcher with a task to start it.
890   890  
891   Construct the task as the direct argument of the two-call expression 891   Construct the task as the direct argument of the two-call expression
892   `run_async(ex)(task)`. 892   `run_async(ex)(task)`.
893   893  
894   @par Thread Safety 894   @par Thread Safety
895   The wrapper itself should only be used from one thread. 895   The wrapper itself should only be used from one thread.
896   896  
897   @pre `mr` outlives every task started through the returned wrapper. 897   @pre `mr` outlives every task started through the returned wrapper.
898   898  
899   @param ex The executor to execute the task on. 899   @param ex The executor to execute the task on.
900   @param st The stop token for cooperative cancellation. 900   @param st The stop token for cooperative cancellation.
901   @param mr The memory resource for frame allocation. 901   @param mr The memory resource for frame allocation.
902   902  
903   @return A wrapper that accepts a `task<T>` for immediate execution. 903   @return A wrapper that accepts a `task<T>` for immediate execution.
904   904  
905   @see task 905   @see task
906   @see Executor 906   @see Executor
907   @see run_async_wrapper 907   @see run_async_wrapper
908   */ 908   */
909   template<Executor Ex> 909   template<Executor Ex>
910   [[nodiscard]] auto 910   [[nodiscard]] auto
HITCBC 911   1 run_async(Ex ex, std::stop_token st, std::pmr::memory_resource* mr) 911   1 run_async(Ex ex, std::stop_token st, std::pmr::memory_resource* mr)
912   { 912   {
913   return run_async_wrapper<Ex, detail::default_handler, std::pmr::memory_resource*>( 913   return run_async_wrapper<Ex, detail::default_handler, std::pmr::memory_resource*>(
HITCBC 914   1 std::move(ex), 914   1 std::move(ex),
HITCBC 915   1 std::move(st), 915   1 std::move(st),
916   detail::default_handler{}, 916   detail::default_handler{},
HITCBC 917   2 mr); 917   2 mr);
918   } 918   }
919   919  
920   /** Bind an executor, a stop token, a memory resource, and a result handler to produce a launcher. Invoke the launcher with a task to start it. 920   /** Bind an executor, a stop token, a memory resource, and a result handler to produce a launcher. Invoke the launcher with a task to start it.
921   921  
922   Construct the task as the direct argument of the two-call expression 922   Construct the task as the direct argument of the two-call expression
923   `run_async(ex)(task)`. 923   `run_async(ex)(task)`.
924   924  
925   @par Thread Safety 925   @par Thread Safety
926   The wrapper itself should only be used from one thread. The handlers 926   The wrapper itself should only be used from one thread. The handlers
927   may be invoked from any thread where the executor schedules work. 927   may be invoked from any thread where the executor schedules work.
928   928  
929   @pre `mr` outlives every task started through the returned wrapper. 929   @pre `mr` outlives every task started through the returned wrapper.
930   930  
931   @param ex The executor to execute the task on. 931   @param ex The executor to execute the task on.
932   @param st The stop token for cooperative cancellation. 932   @param st The stop token for cooperative cancellation.
933   @param mr The memory resource for frame allocation. 933   @param mr The memory resource for frame allocation.
934   @param h1 The handler to invoke with the result (and optionally exception). 934   @param h1 The handler to invoke with the result (and optionally exception).
935   935  
936   @return A wrapper that accepts a `task<T>` for immediate execution. 936   @return A wrapper that accepts a `task<T>` for immediate execution.
937   937  
938   @see task 938   @see task
939   @see Executor 939   @see Executor
940   @see run_async_wrapper 940   @see run_async_wrapper
941   */ 941   */
942   template<Executor Ex, class H1> 942   template<Executor Ex, class H1>
943   [[nodiscard]] auto 943   [[nodiscard]] auto
944   run_async(Ex ex, std::stop_token st, std::pmr::memory_resource* mr, H1 h1) 944   run_async(Ex ex, std::stop_token st, std::pmr::memory_resource* mr, H1 h1)
945   { 945   {
946   return run_async_wrapper<Ex, detail::handler_pair<H1, detail::default_handler>, std::pmr::memory_resource*>( 946   return run_async_wrapper<Ex, detail::handler_pair<H1, detail::default_handler>, std::pmr::memory_resource*>(
947   std::move(ex), 947   std::move(ex),
948   std::move(st), 948   std::move(st),
949   detail::handler_pair<H1, detail::default_handler>{std::move(h1)}, 949   detail::handler_pair<H1, detail::default_handler>{std::move(h1)},
950   mr); 950   mr);
951   } 951   }
952   952  
953   /** Bind an executor, a stop token, a memory resource, and separate result and error handlers to produce a launcher. Invoke the launcher with a task to start it. 953   /** Bind an executor, a stop token, a memory resource, and separate result and error handlers to produce a launcher. Invoke the launcher with a task to start it.
954   954  
955   Construct the task as the direct argument of the two-call expression 955   Construct the task as the direct argument of the two-call expression
956   `run_async(ex)(task)`. 956   `run_async(ex)(task)`.
957   957  
958   @par Thread Safety 958   @par Thread Safety
959   The wrapper itself should only be used from one thread. The handlers 959   The wrapper itself should only be used from one thread. The handlers
960   may be invoked from any thread where the executor schedules work. 960   may be invoked from any thread where the executor schedules work.
961   961  
962   @pre `mr` outlives every task started through the returned wrapper. 962   @pre `mr` outlives every task started through the returned wrapper.
963   963  
964   @param ex The executor to execute the task on. 964   @param ex The executor to execute the task on.
965   @param st The stop token for cooperative cancellation. 965   @param st The stop token for cooperative cancellation.
966   @param mr The memory resource for frame allocation. 966   @param mr The memory resource for frame allocation.
967   @param h1 The handler to invoke with the result on success. 967   @param h1 The handler to invoke with the result on success.
968   @param h2 The handler to invoke with the exception on failure. 968   @param h2 The handler to invoke with the exception on failure.
969   969  
970   @return A wrapper that accepts a `task<T>` for immediate execution. 970   @return A wrapper that accepts a `task<T>` for immediate execution.
971   971  
972   @see task 972   @see task
973   @see Executor 973   @see Executor
974   @see run_async_wrapper 974   @see run_async_wrapper
975   */ 975   */
976   template<Executor Ex, class H1, class H2> 976   template<Executor Ex, class H1, class H2>
977   [[nodiscard]] auto 977   [[nodiscard]] auto
HITCBC 978   1 run_async(Ex ex, std::stop_token st, std::pmr::memory_resource* mr, H1 h1, H2 h2) 978   1 run_async(Ex ex, std::stop_token st, std::pmr::memory_resource* mr, H1 h1, H2 h2)
979   { 979   {
980   return run_async_wrapper<Ex, detail::handler_pair<H1, H2>, std::pmr::memory_resource*>( 980   return run_async_wrapper<Ex, detail::handler_pair<H1, H2>, std::pmr::memory_resource*>(
HITCBC 981   1 std::move(ex), 981   1 std::move(ex),
HITCBC 982   1 std::move(st), 982   1 std::move(st),
983   detail::handler_pair<H1, H2>{std::move(h1), std::move(h2)}, 983   detail::handler_pair<H1, H2>{std::move(h1), std::move(h2)},
HITCBC 984   2 mr); 984   2 mr);
985   } 985   }
986   986  
987   // Ex + standard Allocator (value type) 987   // Ex + standard Allocator (value type)
988   988  
989   /** Bind an executor and an allocator to produce a launcher. Invoke the launcher with a task to start it. 989   /** Bind an executor and an allocator to produce a launcher. Invoke the launcher with a task to start it.
990   990  
991   The allocator is wrapped in a frame_memory_resource and stored in the 991   The allocator is wrapped in a frame_memory_resource and stored in the
992   run_async_trampoline, ensuring it outlives all coroutine frames. 992   run_async_trampoline, ensuring it outlives all coroutine frames.
993   993  
994   Construct the task as the direct argument of the two-call expression 994   Construct the task as the direct argument of the two-call expression
995   `run_async(ex)(task)`. 995   `run_async(ex)(task)`.
996   996  
997   @par Thread Safety 997   @par Thread Safety
998   The wrapper itself should only be used from one thread. 998   The wrapper itself should only be used from one thread.
999   999  
1000   @param ex The executor to execute the task on. 1000   @param ex The executor to execute the task on.
1001   @param alloc The allocator for frame allocation (copied and stored). 1001   @param alloc The allocator for frame allocation (copied and stored).
1002   1002  
1003   @return A wrapper that accepts a `task<T>` for immediate execution. 1003   @return A wrapper that accepts a `task<T>` for immediate execution.
1004   1004  
1005   @see task 1005   @see task
1006   @see Executor 1006   @see Executor
1007   @see run_async_wrapper 1007   @see run_async_wrapper
1008   */ 1008   */
1009   template<Executor Ex, detail::Allocator Alloc> 1009   template<Executor Ex, detail::Allocator Alloc>
1010   [[nodiscard]] auto 1010   [[nodiscard]] auto
HITCBC 1011   319 run_async(Ex ex, Alloc alloc) 1011   319 run_async(Ex ex, Alloc alloc)
1012   { 1012   {
1013   return run_async_wrapper<Ex, detail::default_handler, Alloc>( 1013   return run_async_wrapper<Ex, detail::default_handler, Alloc>(
HITCBC 1014   319 std::move(ex), 1014   319 std::move(ex),
HITCBC 1015   637 std::stop_token{}, 1015   637 std::stop_token{},
1016   detail::default_handler{}, 1016   detail::default_handler{},
HITCBC 1017   955 std::move(alloc)); 1017   955 std::move(alloc));
1018   } 1018   }
1019   1019  
1020   /** Bind an executor, an allocator, and a result handler to produce a launcher. Invoke the launcher with a task to start it. 1020   /** Bind an executor, an allocator, and a result handler to produce a launcher. Invoke the launcher with a task to start it.
1021   1021  
1022   Construct the task as the direct argument of the two-call expression 1022   Construct the task as the direct argument of the two-call expression
1023   `run_async(ex)(task)`. 1023   `run_async(ex)(task)`.
1024   1024  
1025   @par Thread Safety 1025   @par Thread Safety
1026   The wrapper itself should only be used from one thread. The handlers 1026   The wrapper itself should only be used from one thread. The handlers
1027   may be invoked from any thread where the executor schedules work. 1027   may be invoked from any thread where the executor schedules work.
1028   1028  
1029   @param ex The executor to execute the task on. 1029   @param ex The executor to execute the task on.
1030   @param alloc The allocator for frame allocation (copied and stored). 1030   @param alloc The allocator for frame allocation (copied and stored).
1031   @param h1 The handler to invoke with the result (and optionally exception). 1031   @param h1 The handler to invoke with the result (and optionally exception).
1032   1032  
1033   @return A wrapper that accepts a `task<T>` for immediate execution. 1033   @return A wrapper that accepts a `task<T>` for immediate execution.
1034   1034  
1035   @see task 1035   @see task
1036   @see Executor 1036   @see Executor
1037   @see run_async_wrapper 1037   @see run_async_wrapper
1038   */ 1038   */
1039   template<Executor Ex, detail::Allocator Alloc, class H1> 1039   template<Executor Ex, detail::Allocator Alloc, class H1>
1040   [[nodiscard]] auto 1040   [[nodiscard]] auto
HITCBC 1041   1 run_async(Ex ex, Alloc alloc, H1 h1) 1041   1 run_async(Ex ex, Alloc alloc, H1 h1)
1042   { 1042   {
1043   return run_async_wrapper<Ex, detail::handler_pair<H1, detail::default_handler>, Alloc>( 1043   return run_async_wrapper<Ex, detail::handler_pair<H1, detail::default_handler>, Alloc>(
HITCBC 1044   1 std::move(ex), 1044   1 std::move(ex),
HITCBC 1045   1 std::stop_token{}, 1045   1 std::stop_token{},
HITCBC 1046   1 detail::handler_pair<H1, detail::default_handler>{std::move(h1)}, 1046   1 detail::handler_pair<H1, detail::default_handler>{std::move(h1)},
HITCBC 1047   4 std::move(alloc)); 1047   4 std::move(alloc));
1048   } 1048   }
1049   1049  
1050   /** Bind an executor, an allocator, and separate result and error handlers to produce a launcher. Invoke the launcher with a task to start it. 1050   /** Bind an executor, an allocator, and separate result and error handlers to produce a launcher. Invoke the launcher with a task to start it.
1051   1051  
1052   Construct the task as the direct argument of the two-call expression 1052   Construct the task as the direct argument of the two-call expression
1053   `run_async(ex)(task)`. 1053   `run_async(ex)(task)`.
1054   1054  
1055   @par Thread Safety 1055   @par Thread Safety
1056   The wrapper itself should only be used from one thread. The handlers 1056   The wrapper itself should only be used from one thread. The handlers
1057   may be invoked from any thread where the executor schedules work. 1057   may be invoked from any thread where the executor schedules work.
1058   1058  
1059   @param ex The executor to execute the task on. 1059   @param ex The executor to execute the task on.
1060   @param alloc The allocator for frame allocation (copied and stored). 1060   @param alloc The allocator for frame allocation (copied and stored).
1061   @param h1 The handler to invoke with the result on success. 1061   @param h1 The handler to invoke with the result on success.
1062   @param h2 The handler to invoke with the exception on failure. 1062   @param h2 The handler to invoke with the exception on failure.
1063   1063  
1064   @return A wrapper that accepts a `task<T>` for immediate execution. 1064   @return A wrapper that accepts a `task<T>` for immediate execution.
1065   1065  
1066   @see task 1066   @see task
1067   @see Executor 1067   @see Executor
1068   @see run_async_wrapper 1068   @see run_async_wrapper
1069   */ 1069   */
1070   template<Executor Ex, detail::Allocator Alloc, class H1, class H2> 1070   template<Executor Ex, detail::Allocator Alloc, class H1, class H2>
1071   [[nodiscard]] auto 1071   [[nodiscard]] auto
HITCBC 1072   4 run_async(Ex ex, Alloc alloc, H1 h1, H2 h2) 1072   4 run_async(Ex ex, Alloc alloc, H1 h1, H2 h2)
1073   { 1073   {
1074   return run_async_wrapper<Ex, detail::handler_pair<H1, H2>, Alloc>( 1074   return run_async_wrapper<Ex, detail::handler_pair<H1, H2>, Alloc>(
HITCBC 1075   4 std::move(ex), 1075   4 std::move(ex),
HITCBC 1076   4 std::stop_token{}, 1076   4 std::stop_token{},
HITCBC 1077   4 detail::handler_pair<H1, H2>{std::move(h1), std::move(h2)}, 1077   4 detail::handler_pair<H1, H2>{std::move(h1), std::move(h2)},
HITCBC 1078   13 std::move(alloc)); 1078   13 std::move(alloc));
HITCBC 1079   3 } 1079   3 }
1080   1080  
1081   // Ex + stop_token + standard Allocator 1081   // Ex + stop_token + standard Allocator
1082   1082  
1083   /** Bind an executor, a stop token, and an allocator to produce a launcher. Invoke the launcher with a task to start it. 1083   /** Bind an executor, a stop token, and an allocator to produce a launcher. Invoke the launcher with a task to start it.
1084   1084  
1085   Construct the task as the direct argument of the two-call expression 1085   Construct the task as the direct argument of the two-call expression
1086   `run_async(ex)(task)`. 1086   `run_async(ex)(task)`.
1087   1087  
1088   @par Thread Safety 1088   @par Thread Safety
1089   The wrapper itself should only be used from one thread. 1089   The wrapper itself should only be used from one thread.
1090   1090  
1091   @param ex The executor to execute the task on. 1091   @param ex The executor to execute the task on.
1092   @param st The stop token for cooperative cancellation. 1092   @param st The stop token for cooperative cancellation.
1093   @param alloc The allocator for frame allocation (copied and stored). 1093   @param alloc The allocator for frame allocation (copied and stored).
1094   1094  
1095   @return A wrapper that accepts a `task<T>` for immediate execution. 1095   @return A wrapper that accepts a `task<T>` for immediate execution.
1096   1096  
1097   @see task 1097   @see task
1098   @see Executor 1098   @see Executor
1099   @see run_async_wrapper 1099   @see run_async_wrapper
1100   */ 1100   */
1101   template<Executor Ex, detail::Allocator Alloc> 1101   template<Executor Ex, detail::Allocator Alloc>
1102   [[nodiscard]] auto 1102   [[nodiscard]] auto
HITCBC 1103   2001 run_async(Ex ex, std::stop_token st, Alloc alloc) 1103   2001 run_async(Ex ex, std::stop_token st, Alloc alloc)
1104   { 1104   {
1105   return run_async_wrapper<Ex, detail::default_handler, Alloc>( 1105   return run_async_wrapper<Ex, detail::default_handler, Alloc>(
HITCBC 1106   2001 std::move(ex), 1106   2001 std::move(ex),
HITCBC 1107   2001 std::move(st), 1107   2001 std::move(st),
1108   detail::default_handler{}, 1108   detail::default_handler{},
HITCBC 1109   6003 std::move(alloc)); 1109   6003 std::move(alloc));
1110   } 1110   }
1111   1111  
1112   /** Bind an executor, a stop token, an allocator, and a result handler to produce a launcher. Invoke the launcher with a task to start it. 1112   /** Bind an executor, a stop token, an allocator, and a result handler to produce a launcher. Invoke the launcher with a task to start it.
1113   1113  
1114   Construct the task as the direct argument of the two-call expression 1114   Construct the task as the direct argument of the two-call expression
1115   `run_async(ex)(task)`. 1115   `run_async(ex)(task)`.
1116   1116  
1117   @par Thread Safety 1117   @par Thread Safety
1118   The wrapper itself should only be used from one thread. The handlers 1118   The wrapper itself should only be used from one thread. The handlers
1119   may be invoked from any thread where the executor schedules work. 1119   may be invoked from any thread where the executor schedules work.
1120   1120  
1121   @param ex The executor to execute the task on. 1121   @param ex The executor to execute the task on.
1122   @param st The stop token for cooperative cancellation. 1122   @param st The stop token for cooperative cancellation.
1123   @param alloc The allocator for frame allocation (copied and stored). 1123   @param alloc The allocator for frame allocation (copied and stored).
1124   @param h1 The handler to invoke with the result (and optionally exception). 1124   @param h1 The handler to invoke with the result (and optionally exception).
1125   1125  
1126   @return A wrapper that accepts a `task<T>` for immediate execution. 1126   @return A wrapper that accepts a `task<T>` for immediate execution.
1127   1127  
1128   @see task 1128   @see task
1129   @see Executor 1129   @see Executor
1130   @see run_async_wrapper 1130   @see run_async_wrapper
1131   */ 1131   */
1132   template<Executor Ex, detail::Allocator Alloc, class H1> 1132   template<Executor Ex, detail::Allocator Alloc, class H1>
1133   [[nodiscard]] auto 1133   [[nodiscard]] auto
1134   run_async(Ex ex, std::stop_token st, Alloc alloc, H1 h1) 1134   run_async(Ex ex, std::stop_token st, Alloc alloc, H1 h1)
1135   { 1135   {
1136   return run_async_wrapper<Ex, detail::handler_pair<H1, detail::default_handler>, Alloc>( 1136   return run_async_wrapper<Ex, detail::handler_pair<H1, detail::default_handler>, Alloc>(
1137   std::move(ex), 1137   std::move(ex),
1138   std::move(st), 1138   std::move(st),
1139   detail::handler_pair<H1, detail::default_handler>{std::move(h1)}, 1139   detail::handler_pair<H1, detail::default_handler>{std::move(h1)},
1140   std::move(alloc)); 1140   std::move(alloc));
1141   } 1141   }
1142   1142  
1143   /** Bind an executor, a stop token, an allocator, and separate result and error handlers to produce a launcher. Invoke the launcher with a task to start it. 1143   /** Bind an executor, a stop token, an allocator, and separate result and error handlers to produce a launcher. Invoke the launcher with a task to start it.
1144   1144  
1145   Construct the task as the direct argument of the two-call expression 1145   Construct the task as the direct argument of the two-call expression
1146   `run_async(ex)(task)`. 1146   `run_async(ex)(task)`.
1147   1147  
1148   @par Thread Safety 1148   @par Thread Safety
1149   The wrapper itself should only be used from one thread. The handlers 1149   The wrapper itself should only be used from one thread. The handlers
1150   may be invoked from any thread where the executor schedules work. 1150   may be invoked from any thread where the executor schedules work.
1151   1151  
1152   @param ex The executor to execute the task on. 1152   @param ex The executor to execute the task on.
1153   @param st The stop token for cooperative cancellation. 1153   @param st The stop token for cooperative cancellation.
1154   @param alloc The allocator for frame allocation (copied and stored). 1154   @param alloc The allocator for frame allocation (copied and stored).
1155   @param h1 The handler to invoke with the result on success. 1155   @param h1 The handler to invoke with the result on success.
1156   @param h2 The handler to invoke with the exception on failure. 1156   @param h2 The handler to invoke with the exception on failure.
1157   1157  
1158   @return A wrapper that accepts a `task<T>` for immediate execution. 1158   @return A wrapper that accepts a `task<T>` for immediate execution.
1159   1159  
1160   @see task 1160   @see task
1161   @see Executor 1161   @see Executor
1162   @see run_async_wrapper 1162   @see run_async_wrapper
1163   */ 1163   */
1164   template<Executor Ex, detail::Allocator Alloc, class H1, class H2> 1164   template<Executor Ex, detail::Allocator Alloc, class H1, class H2>
1165   [[nodiscard]] auto 1165   [[nodiscard]] auto
1166   run_async(Ex ex, std::stop_token st, Alloc alloc, H1 h1, H2 h2) 1166   run_async(Ex ex, std::stop_token st, Alloc alloc, H1 h1, H2 h2)
1167   { 1167   {
1168   return run_async_wrapper<Ex, detail::handler_pair<H1, H2>, Alloc>( 1168   return run_async_wrapper<Ex, detail::handler_pair<H1, H2>, Alloc>(
1169   std::move(ex), 1169   std::move(ex),
1170   std::move(st), 1170   std::move(st),
1171   detail::handler_pair<H1, H2>{std::move(h1), std::move(h2)}, 1171   detail::handler_pair<H1, H2>{std::move(h1), std::move(h2)},
1172   std::move(alloc)); 1172   std::move(alloc));
1173   } 1173   }
1174   1174  
1175   } // namespace capy 1175   } // namespace capy
1176   } // namespace boost 1176   } // namespace boost
1177   1177  
1178   #endif 1178   #endif