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