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