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