1  
//
1  
//
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3  
//
3  
//
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
//
6  
//
7  
// Official repository: https://github.com/cppalliance/capy
7  
// Official repository: https://github.com/cppalliance/capy
8  
//
8  
//
9  

9  

10  
#ifndef BOOST_CAPY_RUN_ASYNC_HPP
10  
#ifndef BOOST_CAPY_RUN_ASYNC_HPP
11  
#define BOOST_CAPY_RUN_ASYNC_HPP
11  
#define BOOST_CAPY_RUN_ASYNC_HPP
12  

12  

13  
#include <boost/capy/detail/config.hpp>
13  
#include <boost/capy/detail/config.hpp>
14  
#include <boost/capy/detail/run.hpp>
14  
#include <boost/capy/detail/run.hpp>
15  
#include <boost/capy/detail/run_callbacks.hpp>
15  
#include <boost/capy/detail/run_callbacks.hpp>
16  
#include <boost/capy/concept/executor.hpp>
16  
#include <boost/capy/concept/executor.hpp>
17 -
#include <boost/capy/concept/io_launchable_task.hpp>
17 +
#include <boost/capy/concept/io_runnable.hpp>
18  
#include <boost/capy/ex/execution_context.hpp>
18  
#include <boost/capy/ex/execution_context.hpp>
19  
#include <boost/capy/ex/frame_allocator.hpp>
19  
#include <boost/capy/ex/frame_allocator.hpp>
20  
#include <boost/capy/ex/io_env.hpp>
20  
#include <boost/capy/ex/io_env.hpp>
21  
#include <boost/capy/ex/recycling_memory_resource.hpp>
21  
#include <boost/capy/ex/recycling_memory_resource.hpp>
22  
#include <boost/capy/ex/work_guard.hpp>
22  
#include <boost/capy/ex/work_guard.hpp>
23  

23  

24  
#include <coroutine>
24  
#include <coroutine>
25  
#include <memory_resource>
25  
#include <memory_resource>
26  
#include <new>
26  
#include <new>
27  
#include <stop_token>
27  
#include <stop_token>
28  
#include <type_traits>
28  
#include <type_traits>
29  

29  

30  
namespace boost {
30  
namespace boost {
31  
namespace capy {
31  
namespace capy {
32  
namespace detail {
32  
namespace detail {
33  

33  

34  
/// Function pointer type for type-erased frame deallocation.
34  
/// Function pointer type for type-erased frame deallocation.
35  
using dealloc_fn = void(*)(void*, std::size_t);
35  
using dealloc_fn = void(*)(void*, std::size_t);
36  

36  

37  
/// Type-erased deallocator implementation for trampoline frames.
37  
/// Type-erased deallocator implementation for trampoline frames.
38  
template<class Alloc>
38  
template<class Alloc>
39  
void dealloc_impl(void* raw, std::size_t total)
39  
void dealloc_impl(void* raw, std::size_t total)
40  
{
40  
{
41  
    static_assert(std::is_same_v<typename Alloc::value_type, std::byte>);
41  
    static_assert(std::is_same_v<typename Alloc::value_type, std::byte>);
42  
    auto* a = std::launder(reinterpret_cast<Alloc*>(
42  
    auto* a = std::launder(reinterpret_cast<Alloc*>(
43  
        static_cast<char*>(raw) + total - sizeof(Alloc)));
43  
        static_cast<char*>(raw) + total - sizeof(Alloc)));
44  
    Alloc ba(std::move(*a));
44  
    Alloc ba(std::move(*a));
45  
    a->~Alloc();
45  
    a->~Alloc();
46  
    ba.deallocate(static_cast<std::byte*>(raw), total);
46  
    ba.deallocate(static_cast<std::byte*>(raw), total);
47  
}
47  
}
48  

48  

49  
/// Awaiter to access the promise from within the coroutine.
49  
/// Awaiter to access the promise from within the coroutine.
50  
template<class Promise>
50  
template<class Promise>
51  
struct get_promise_awaiter
51  
struct get_promise_awaiter
52  
{
52  
{
53  
    Promise* p_ = nullptr;
53  
    Promise* p_ = nullptr;
54  

54  

55  
    bool await_ready() const noexcept { return false; }
55  
    bool await_ready() const noexcept { return false; }
56  

56  

57  
    bool await_suspend(std::coroutine_handle<Promise> h) noexcept
57  
    bool await_suspend(std::coroutine_handle<Promise> h) noexcept
58  
    {
58  
    {
59  
        p_ = &h.promise();
59  
        p_ = &h.promise();
60  
        return false;
60  
        return false;
61  
    }
61  
    }
62  

62  

63  
    Promise& await_resume() const noexcept
63  
    Promise& await_resume() const noexcept
64  
    {
64  
    {
65  
        return *p_;
65  
        return *p_;
66  
    }
66  
    }
67  
};
67  
};
68  

68  

69  
/** Internal run_async_trampoline coroutine for run_async.
69  
/** Internal run_async_trampoline coroutine for run_async.
70  

70  

71  
    The run_async_trampoline is allocated BEFORE the task (via C++17 postfix evaluation
71  
    The run_async_trampoline is allocated BEFORE the task (via C++17 postfix evaluation
72  
    order) and serves as the task's continuation. When the task final_suspends,
72  
    order) and serves as the task's continuation. When the task final_suspends,
73  
    control returns to the run_async_trampoline which then invokes the appropriate handler.
73  
    control returns to the run_async_trampoline which then invokes the appropriate handler.
74  

74  

75  
    For value-type allocators, the run_async_trampoline stores a frame_memory_resource
75  
    For value-type allocators, the run_async_trampoline stores a frame_memory_resource
76  
    that wraps the allocator. For memory_resource*, it stores the pointer directly.
76  
    that wraps the allocator. For memory_resource*, it stores the pointer directly.
77  

77  

78  
    @tparam Ex The executor type.
78  
    @tparam Ex The executor type.
79  
    @tparam Handlers The handler type (default_handler or handler_pair).
79  
    @tparam Handlers The handler type (default_handler or handler_pair).
80  
    @tparam Alloc The allocator type (value type or memory_resource*).
80  
    @tparam Alloc The allocator type (value type or memory_resource*).
81  
*/
81  
*/
82  
template<class Ex, class Handlers, class Alloc>
82  
template<class Ex, class Handlers, class Alloc>
83  
struct run_async_trampoline
83  
struct run_async_trampoline
84  
{
84  
{
85  
    using invoke_fn = void(*)(void*, Handlers&);
85  
    using invoke_fn = void(*)(void*, Handlers&);
86  

86  

87  
    struct promise_type
87  
    struct promise_type
88  
    {
88  
    {
89  
        work_guard<Ex> wg_;
89  
        work_guard<Ex> wg_;
90  
        Handlers handlers_;
90  
        Handlers handlers_;
91  
        frame_memory_resource<Alloc> resource_;
91  
        frame_memory_resource<Alloc> resource_;
92  
        io_env env_;
92  
        io_env env_;
93  
        invoke_fn invoke_ = nullptr;
93  
        invoke_fn invoke_ = nullptr;
94  
        void* task_promise_ = nullptr;
94  
        void* task_promise_ = nullptr;
95  
        std::coroutine_handle<> task_h_;
95  
        std::coroutine_handle<> task_h_;
96  

96  

97  
        promise_type(Ex& ex, Handlers& h, Alloc& a) noexcept
97  
        promise_type(Ex& ex, Handlers& h, Alloc& a) noexcept
98  
            : wg_(std::move(ex))
98  
            : wg_(std::move(ex))
99  
            , handlers_(std::move(h))
99  
            , handlers_(std::move(h))
100  
            , resource_(std::move(a))
100  
            , resource_(std::move(a))
101  
        {
101  
        {
102  
        }
102  
        }
103  

103  

104  
        static void* operator new(
104  
        static void* operator new(
105  
            std::size_t size, Ex const&, Handlers const&, Alloc a)
105  
            std::size_t size, Ex const&, Handlers const&, Alloc a)
106  
        {
106  
        {
107  
            using byte_alloc = typename std::allocator_traits<Alloc>
107  
            using byte_alloc = typename std::allocator_traits<Alloc>
108  
                ::template rebind_alloc<std::byte>;
108  
                ::template rebind_alloc<std::byte>;
109  

109  

110  
            constexpr auto footer_align =
110  
            constexpr auto footer_align =
111  
                (std::max)(alignof(dealloc_fn), alignof(Alloc));
111  
                (std::max)(alignof(dealloc_fn), alignof(Alloc));
112  
            auto padded = (size + footer_align - 1) & ~(footer_align - 1);
112  
            auto padded = (size + footer_align - 1) & ~(footer_align - 1);
113  
            auto total = padded + sizeof(dealloc_fn) + sizeof(Alloc);
113  
            auto total = padded + sizeof(dealloc_fn) + sizeof(Alloc);
114  

114  

115  
            byte_alloc ba(std::move(a));
115  
            byte_alloc ba(std::move(a));
116  
            void* raw = ba.allocate(total);
116  
            void* raw = ba.allocate(total);
117  

117  

118  
            auto* fn_loc = reinterpret_cast<dealloc_fn*>(
118  
            auto* fn_loc = reinterpret_cast<dealloc_fn*>(
119  
                static_cast<char*>(raw) + padded);
119  
                static_cast<char*>(raw) + padded);
120  
            *fn_loc = &dealloc_impl<byte_alloc>;
120  
            *fn_loc = &dealloc_impl<byte_alloc>;
121  

121  

122  
            new (fn_loc + 1) byte_alloc(std::move(ba));
122  
            new (fn_loc + 1) byte_alloc(std::move(ba));
123  

123  

124  
            return raw;
124  
            return raw;
125  
        }
125  
        }
126  

126  

127  
        static void operator delete(void* ptr, std::size_t size)
127  
        static void operator delete(void* ptr, std::size_t size)
128  
        {
128  
        {
129  
            constexpr auto footer_align =
129  
            constexpr auto footer_align =
130  
                (std::max)(alignof(dealloc_fn), alignof(Alloc));
130  
                (std::max)(alignof(dealloc_fn), alignof(Alloc));
131  
            auto padded = (size + footer_align - 1) & ~(footer_align - 1);
131  
            auto padded = (size + footer_align - 1) & ~(footer_align - 1);
132  
            auto total = padded + sizeof(dealloc_fn) + sizeof(Alloc);
132  
            auto total = padded + sizeof(dealloc_fn) + sizeof(Alloc);
133  

133  

134  
            auto* fn = reinterpret_cast<dealloc_fn*>(
134  
            auto* fn = reinterpret_cast<dealloc_fn*>(
135  
                static_cast<char*>(ptr) + padded);
135  
                static_cast<char*>(ptr) + padded);
136  
            (*fn)(ptr, total);
136  
            (*fn)(ptr, total);
137  
        }
137  
        }
138  

138  

139  
        std::pmr::memory_resource* get_resource() noexcept
139  
        std::pmr::memory_resource* get_resource() noexcept
140  
        {
140  
        {
141  
            return &resource_;
141  
            return &resource_;
142  
        }
142  
        }
143  

143  

144  
        run_async_trampoline get_return_object() noexcept
144  
        run_async_trampoline get_return_object() noexcept
145  
        {
145  
        {
146  
            return run_async_trampoline{
146  
            return run_async_trampoline{
147  
                std::coroutine_handle<promise_type>::from_promise(*this)};
147  
                std::coroutine_handle<promise_type>::from_promise(*this)};
148  
        }
148  
        }
149  

149  

150  
        std::suspend_always initial_suspend() noexcept
150  
        std::suspend_always initial_suspend() noexcept
151  
        {
151  
        {
152  
            return {};
152  
            return {};
153  
        }
153  
        }
154  

154  

155  
        std::suspend_never final_suspend() noexcept
155  
        std::suspend_never final_suspend() noexcept
156  
        {
156  
        {
157  
            return {};
157  
            return {};
158  
        }
158  
        }
159  

159  

160  
        void return_void() noexcept
160  
        void return_void() noexcept
161  
        {
161  
        {
162  
        }
162  
        }
163  

163  

164  
        void unhandled_exception() noexcept
164  
        void unhandled_exception() noexcept
165  
        {
165  
        {
166  
        }
166  
        }
167  
    };
167  
    };
168  

168  

169  
    std::coroutine_handle<promise_type> h_;
169  
    std::coroutine_handle<promise_type> h_;
170  

170  

171 -
    template<IoLaunchableTask Task>
171 +
    template<IoRunnable Task>
172  
    static void invoke_impl(void* p, Handlers& h)
172  
    static void invoke_impl(void* p, Handlers& h)
173  
    {
173  
    {
174  
        using R = decltype(std::declval<Task&>().await_resume());
174  
        using R = decltype(std::declval<Task&>().await_resume());
175  
        auto& promise = *static_cast<typename Task::promise_type*>(p);
175  
        auto& promise = *static_cast<typename Task::promise_type*>(p);
176  
        if(promise.exception())
176  
        if(promise.exception())
177  
            h(promise.exception());
177  
            h(promise.exception());
178  
        else if constexpr(std::is_void_v<R>)
178  
        else if constexpr(std::is_void_v<R>)
179  
            h();
179  
            h();
180  
        else
180  
        else
181  
            h(std::move(promise.result()));
181  
            h(std::move(promise.result()));
182  
    }
182  
    }
183  
};
183  
};
184  

184  

185  
/** Specialization for memory_resource* - stores pointer directly.
185  
/** Specialization for memory_resource* - stores pointer directly.
186  

186  

187  
    This avoids double indirection when the user passes a memory_resource*.
187  
    This avoids double indirection when the user passes a memory_resource*.
188  
*/
188  
*/
189  
template<class Ex, class Handlers>
189  
template<class Ex, class Handlers>
190  
struct run_async_trampoline<Ex, Handlers, std::pmr::memory_resource*>
190  
struct run_async_trampoline<Ex, Handlers, std::pmr::memory_resource*>
191  
{
191  
{
192  
    using invoke_fn = void(*)(void*, Handlers&);
192  
    using invoke_fn = void(*)(void*, Handlers&);
193  

193  

194  
    struct promise_type
194  
    struct promise_type
195  
    {
195  
    {
196  
        work_guard<Ex> wg_;
196  
        work_guard<Ex> wg_;
197  
        Handlers handlers_;
197  
        Handlers handlers_;
198  
        std::pmr::memory_resource* mr_;
198  
        std::pmr::memory_resource* mr_;
199  
        io_env env_;
199  
        io_env env_;
200  
        invoke_fn invoke_ = nullptr;
200  
        invoke_fn invoke_ = nullptr;
201  
        void* task_promise_ = nullptr;
201  
        void* task_promise_ = nullptr;
202  
        std::coroutine_handle<> task_h_;
202  
        std::coroutine_handle<> task_h_;
203  

203  

204  
        promise_type(
204  
        promise_type(
205  
            Ex& ex, Handlers& h, std::pmr::memory_resource* mr) noexcept
205  
            Ex& ex, Handlers& h, std::pmr::memory_resource* mr) noexcept
206  
            : wg_(std::move(ex))
206  
            : wg_(std::move(ex))
207  
            , handlers_(std::move(h))
207  
            , handlers_(std::move(h))
208  
            , mr_(mr)
208  
            , mr_(mr)
209  
        {
209  
        {
210  
        }
210  
        }
211  

211  

212  
        static void* operator new(
212  
        static void* operator new(
213  
            std::size_t size, Ex const&, Handlers const&,
213  
            std::size_t size, Ex const&, Handlers const&,
214  
            std::pmr::memory_resource* mr)
214  
            std::pmr::memory_resource* mr)
215  
        {
215  
        {
216  
            auto total = size + sizeof(mr);
216  
            auto total = size + sizeof(mr);
217  
            void* raw = mr->allocate(total, alignof(std::max_align_t));
217  
            void* raw = mr->allocate(total, alignof(std::max_align_t));
218  
            *reinterpret_cast<std::pmr::memory_resource**>(
218  
            *reinterpret_cast<std::pmr::memory_resource**>(
219  
                static_cast<char*>(raw) + size) = mr;
219  
                static_cast<char*>(raw) + size) = mr;
220  
            return raw;
220  
            return raw;
221  
        }
221  
        }
222  

222  

223  
        static void operator delete(void* ptr, std::size_t size)
223  
        static void operator delete(void* ptr, std::size_t size)
224  
        {
224  
        {
225  
            auto* mr = *reinterpret_cast<std::pmr::memory_resource**>(
225  
            auto* mr = *reinterpret_cast<std::pmr::memory_resource**>(
226  
                static_cast<char*>(ptr) + size);
226  
                static_cast<char*>(ptr) + size);
227  
            mr->deallocate(ptr, size + sizeof(mr), alignof(std::max_align_t));
227  
            mr->deallocate(ptr, size + sizeof(mr), alignof(std::max_align_t));
228  
        }
228  
        }
229  

229  

230  
        std::pmr::memory_resource* get_resource() noexcept
230  
        std::pmr::memory_resource* get_resource() noexcept
231  
        {
231  
        {
232  
            return mr_;
232  
            return mr_;
233  
        }
233  
        }
234  

234  

235  
        run_async_trampoline get_return_object() noexcept
235  
        run_async_trampoline get_return_object() noexcept
236  
        {
236  
        {
237  
            return run_async_trampoline{
237  
            return run_async_trampoline{
238  
                std::coroutine_handle<promise_type>::from_promise(*this)};
238  
                std::coroutine_handle<promise_type>::from_promise(*this)};
239  
        }
239  
        }
240  

240  

241  
        std::suspend_always initial_suspend() noexcept
241  
        std::suspend_always initial_suspend() noexcept
242  
        {
242  
        {
243  
            return {};
243  
            return {};
244  
        }
244  
        }
245  

245  

246  
        std::suspend_never final_suspend() noexcept
246  
        std::suspend_never final_suspend() noexcept
247  
        {
247  
        {
248  
            return {};
248  
            return {};
249  
        }
249  
        }
250  

250  

251  
        void return_void() noexcept
251  
        void return_void() noexcept
252  
        {
252  
        {
253  
        }
253  
        }
254  

254  

255  
        void unhandled_exception() noexcept
255  
        void unhandled_exception() noexcept
256  
        {
256  
        {
257  
        }
257  
        }
258  
    };
258  
    };
259  

259  

260  
    std::coroutine_handle<promise_type> h_;
260  
    std::coroutine_handle<promise_type> h_;
261  

261  

262 -
    template<IoLaunchableTask Task>
262 +
    template<IoRunnable Task>
263  
    static void invoke_impl(void* p, Handlers& h)
263  
    static void invoke_impl(void* p, Handlers& h)
264  
    {
264  
    {
265  
        using R = decltype(std::declval<Task&>().await_resume());
265  
        using R = decltype(std::declval<Task&>().await_resume());
266  
        auto& promise = *static_cast<typename Task::promise_type*>(p);
266  
        auto& promise = *static_cast<typename Task::promise_type*>(p);
267  
        if(promise.exception())
267  
        if(promise.exception())
268  
            h(promise.exception());
268  
            h(promise.exception());
269  
        else if constexpr(std::is_void_v<R>)
269  
        else if constexpr(std::is_void_v<R>)
270  
            h();
270  
            h();
271  
        else
271  
        else
272  
            h(std::move(promise.result()));
272  
            h(std::move(promise.result()));
273  
    }
273  
    }
274  
};
274  
};
275  

275  

276  
/// Coroutine body for run_async_trampoline - invokes handlers then destroys task.
276  
/// Coroutine body for run_async_trampoline - invokes handlers then destroys task.
277  
template<class Ex, class Handlers, class Alloc>
277  
template<class Ex, class Handlers, class Alloc>
278  
run_async_trampoline<Ex, Handlers, Alloc>
278  
run_async_trampoline<Ex, Handlers, Alloc>
279  
make_trampoline(Ex, Handlers, Alloc)
279  
make_trampoline(Ex, Handlers, Alloc)
280  
{
280  
{
281  
    // promise_type ctor steals the parameters
281  
    // promise_type ctor steals the parameters
282  
    auto& p = co_await get_promise_awaiter<
282  
    auto& p = co_await get_promise_awaiter<
283  
        typename run_async_trampoline<Ex, Handlers, Alloc>::promise_type>{};
283  
        typename run_async_trampoline<Ex, Handlers, Alloc>::promise_type>{};
284  
    
284  
    
285  
    p.invoke_(p.task_promise_, p.handlers_);
285  
    p.invoke_(p.task_promise_, p.handlers_);
286  
    p.task_h_.destroy();
286  
    p.task_h_.destroy();
287  
}
287  
}
288  

288  

289  
} // namespace detail
289  
} // namespace detail
290  

290  

291  
//----------------------------------------------------------
291  
//----------------------------------------------------------
292  
//
292  
//
293  
// run_async_wrapper
293  
// run_async_wrapper
294  
//
294  
//
295  
//----------------------------------------------------------
295  
//----------------------------------------------------------
296  

296  

297  
/** Wrapper returned by run_async that accepts a task for execution.
297  
/** Wrapper returned by run_async that accepts a task for execution.
298  

298  

299  
    This wrapper holds the run_async_trampoline coroutine, executor, stop token,
299  
    This wrapper holds the run_async_trampoline coroutine, executor, stop token,
300  
    and handlers. The run_async_trampoline is allocated when the wrapper is constructed
300  
    and handlers. The run_async_trampoline is allocated when the wrapper is constructed
301  
    (before the task due to C++17 postfix evaluation order).
301  
    (before the task due to C++17 postfix evaluation order).
302  

302  

303  
    The rvalue ref-qualifier on `operator()` ensures the wrapper can only
303  
    The rvalue ref-qualifier on `operator()` ensures the wrapper can only
304  
    be used as a temporary, preventing misuse that would violate LIFO ordering.
304  
    be used as a temporary, preventing misuse that would violate LIFO ordering.
305  

305  

306  
    @tparam Ex The executor type satisfying the `Executor` concept.
306  
    @tparam Ex The executor type satisfying the `Executor` concept.
307  
    @tparam Handlers The handler type (default_handler or handler_pair).
307  
    @tparam Handlers The handler type (default_handler or handler_pair).
308  
    @tparam Alloc The allocator type (value type or memory_resource*).
308  
    @tparam Alloc The allocator type (value type or memory_resource*).
309  

309  

310  
    @par Thread Safety
310  
    @par Thread Safety
311  
    The wrapper itself should only be used from one thread. The handlers
311  
    The wrapper itself should only be used from one thread. The handlers
312  
    may be invoked from any thread where the executor schedules work.
312  
    may be invoked from any thread where the executor schedules work.
313  

313  

314  
    @par Example
314  
    @par Example
315  
    @code
315  
    @code
316  
    // Correct usage - wrapper is temporary
316  
    // Correct usage - wrapper is temporary
317  
    run_async(ex)(my_task());
317  
    run_async(ex)(my_task());
318  

318  

319  
    // Compile error - cannot call operator() on lvalue
319  
    // Compile error - cannot call operator() on lvalue
320  
    auto w = run_async(ex);
320  
    auto w = run_async(ex);
321  
    w(my_task());  // Error: operator() requires rvalue
321  
    w(my_task());  // Error: operator() requires rvalue
322  
    @endcode
322  
    @endcode
323  

323  

324  
    @see run_async
324  
    @see run_async
325  
*/
325  
*/
326  
template<Executor Ex, class Handlers, class Alloc>
326  
template<Executor Ex, class Handlers, class Alloc>
327  
class [[nodiscard]] run_async_wrapper
327  
class [[nodiscard]] run_async_wrapper
328  
{
328  
{
329  
    detail::run_async_trampoline<Ex, Handlers, Alloc> tr_;
329  
    detail::run_async_trampoline<Ex, Handlers, Alloc> tr_;
330  
    std::stop_token st_;
330  
    std::stop_token st_;
331  

331  

332  
public:
332  
public:
333  
    /// Construct wrapper with executor, stop token, handlers, and allocator.
333  
    /// Construct wrapper with executor, stop token, handlers, and allocator.
334  
    run_async_wrapper(
334  
    run_async_wrapper(
335  
        Ex ex,
335  
        Ex ex,
336  
        std::stop_token st,
336  
        std::stop_token st,
337  
        Handlers h,
337  
        Handlers h,
338  
        Alloc a) noexcept
338  
        Alloc a) noexcept
339  
        : tr_(detail::make_trampoline<Ex, Handlers, Alloc>(
339  
        : tr_(detail::make_trampoline<Ex, Handlers, Alloc>(
340  
            std::move(ex), std::move(h), std::move(a)))
340  
            std::move(ex), std::move(h), std::move(a)))
341  
        , st_(std::move(st))
341  
        , st_(std::move(st))
342  
    {
342  
    {
343  
        if constexpr (!std::is_same_v<Alloc, std::pmr::memory_resource*>)
343  
        if constexpr (!std::is_same_v<Alloc, std::pmr::memory_resource*>)
344  
        {
344  
        {
345  
            static_assert(
345  
            static_assert(
346  
                std::is_nothrow_move_constructible_v<Alloc>,
346  
                std::is_nothrow_move_constructible_v<Alloc>,
347  
                "Allocator must be nothrow move constructible");
347  
                "Allocator must be nothrow move constructible");
348  
        }
348  
        }
349  
        // Set TLS before task argument is evaluated
349  
        // Set TLS before task argument is evaluated
350  
        current_frame_allocator() = tr_.h_.promise().get_resource();
350  
        current_frame_allocator() = tr_.h_.promise().get_resource();
351  
    }
351  
    }
352  

352  

353  
    // Non-copyable, non-movable (must be used immediately)
353  
    // Non-copyable, non-movable (must be used immediately)
354  
    run_async_wrapper(run_async_wrapper const&) = delete;
354  
    run_async_wrapper(run_async_wrapper const&) = delete;
355  
    run_async_wrapper(run_async_wrapper&&) = delete;
355  
    run_async_wrapper(run_async_wrapper&&) = delete;
356  
    run_async_wrapper& operator=(run_async_wrapper const&) = delete;
356  
    run_async_wrapper& operator=(run_async_wrapper const&) = delete;
357  
    run_async_wrapper& operator=(run_async_wrapper&&) = delete;
357  
    run_async_wrapper& operator=(run_async_wrapper&&) = delete;
358  

358  

359  
    /** Launch the task for execution.
359  
    /** Launch the task for execution.
360  

360  

361  
        This operator accepts a task and launches it on the executor.
361  
        This operator accepts a task and launches it on the executor.
362  
        The rvalue ref-qualifier ensures the wrapper is consumed, enforcing
362  
        The rvalue ref-qualifier ensures the wrapper is consumed, enforcing
363  
        correct LIFO destruction order.
363  
        correct LIFO destruction order.
364  

364  

365  
        The `io_env` constructed for the task is owned by the trampoline
365  
        The `io_env` constructed for the task is owned by the trampoline
366  
        coroutine and is guaranteed to outlive the task and all awaitables
366  
        coroutine and is guaranteed to outlive the task and all awaitables
367  
        in its chain. Awaitables may store `io_env const*` without concern
367  
        in its chain. Awaitables may store `io_env const*` without concern
368  
        for dangling references.
368  
        for dangling references.
369  

369  

370 -
        @tparam Task The IoLaunchableTask type.
370 +
        @tparam Task The IoRunnable type.
371  

371  

372  
        @param t The task to execute. Ownership is transferred to the
372  
        @param t The task to execute. Ownership is transferred to the
373  
                 run_async_trampoline which will destroy it after completion.
373  
                 run_async_trampoline which will destroy it after completion.
374  
    */
374  
    */
375 -
    template<IoLaunchableTask Task>
375 +
    template<IoRunnable Task>
376  
    void operator()(Task t) &&
376  
    void operator()(Task t) &&
377  
    {
377  
    {
378  
        auto task_h = t.handle();
378  
        auto task_h = t.handle();
379  
        auto& task_promise = task_h.promise();
379  
        auto& task_promise = task_h.promise();
380  
        t.release();
380  
        t.release();
381  

381  

382  
        auto& p = tr_.h_.promise();
382  
        auto& p = tr_.h_.promise();
383  

383  

384  
        // Inject Task-specific invoke function
384  
        // Inject Task-specific invoke function
385  
        p.invoke_ = detail::run_async_trampoline<Ex, Handlers, Alloc>::template invoke_impl<Task>;
385  
        p.invoke_ = detail::run_async_trampoline<Ex, Handlers, Alloc>::template invoke_impl<Task>;
386  
        p.task_promise_ = &task_promise;
386  
        p.task_promise_ = &task_promise;
387  
        p.task_h_ = task_h;
387  
        p.task_h_ = task_h;
388  

388  

389  
        // Setup task's continuation to return to run_async_trampoline
389  
        // Setup task's continuation to return to run_async_trampoline
390 -
        task_promise.set_continuation(tr_.h_, p.wg_.executor());
390 +
        task_promise.set_continuation(tr_.h_);
391  
        p.env_ = {p.wg_.executor(), st_, p.get_resource()};
391  
        p.env_ = {p.wg_.executor(), st_, p.get_resource()};
392 -
        task_promise.set_environment(p.env_);
392 +
        task_promise.set_environment(&p.env_);
393  

393  

394  
        // Start task through executor
394  
        // Start task through executor
395  
        p.wg_.executor().dispatch(task_h).resume();
395  
        p.wg_.executor().dispatch(task_h).resume();
396  
    }
396  
    }
397  
};
397  
};
398  

398  

399  
//----------------------------------------------------------
399  
//----------------------------------------------------------
400  
//
400  
//
401  
// run_async Overloads
401  
// run_async Overloads
402  
//
402  
//
403  
//----------------------------------------------------------
403  
//----------------------------------------------------------
404  

404  

405  
// Executor only (uses default recycling allocator)
405  
// Executor only (uses default recycling allocator)
406  

406  

407  
/** Asynchronously launch a lazy task on the given executor.
407  
/** Asynchronously launch a lazy task on the given executor.
408  

408  

409  
    Use this to start execution of a `task<T>` that was created lazily.
409  
    Use this to start execution of a `task<T>` that was created lazily.
410  
    The returned wrapper must be immediately invoked with the task;
410  
    The returned wrapper must be immediately invoked with the task;
411  
    storing the wrapper and calling it later violates LIFO ordering.
411  
    storing the wrapper and calling it later violates LIFO ordering.
412  

412  

413  
    Uses the default recycling frame allocator for coroutine frames.
413  
    Uses the default recycling frame allocator for coroutine frames.
414  
    With no handlers, the result is discarded and exceptions are rethrown.
414  
    With no handlers, the result is discarded and exceptions are rethrown.
415  

415  

416  
    @par Thread Safety
416  
    @par Thread Safety
417  
    The wrapper and handlers may be called from any thread where the
417  
    The wrapper and handlers may be called from any thread where the
418  
    executor schedules work.
418  
    executor schedules work.
419  

419  

420  
    @par Example
420  
    @par Example
421  
    @code
421  
    @code
422  
    run_async(ioc.get_executor())(my_task());
422  
    run_async(ioc.get_executor())(my_task());
423  
    @endcode
423  
    @endcode
424  

424  

425  
    @param ex The executor to execute the task on.
425  
    @param ex The executor to execute the task on.
426  

426  

427  
    @return A wrapper that accepts a `task<T>` for immediate execution.
427  
    @return A wrapper that accepts a `task<T>` for immediate execution.
428  

428  

429  
    @see task
429  
    @see task
430  
    @see executor
430  
    @see executor
431  
*/
431  
*/
432  
template<Executor Ex>
432  
template<Executor Ex>
433  
[[nodiscard]] auto
433  
[[nodiscard]] auto
434  
run_async(Ex ex)
434  
run_async(Ex ex)
435  
{
435  
{
436  
    auto* mr = ex.context().get_frame_allocator();
436  
    auto* mr = ex.context().get_frame_allocator();
437  
    return run_async_wrapper<Ex, detail::default_handler, std::pmr::memory_resource*>(
437  
    return run_async_wrapper<Ex, detail::default_handler, std::pmr::memory_resource*>(
438  
        std::move(ex),
438  
        std::move(ex),
439  
        std::stop_token{},
439  
        std::stop_token{},
440  
        detail::default_handler{},
440  
        detail::default_handler{},
441  
        mr);
441  
        mr);
442  
}
442  
}
443  

443  

444  
/** Asynchronously launch a lazy task with a result handler.
444  
/** Asynchronously launch a lazy task with a result handler.
445  

445  

446  
    The handler `h1` is called with the task's result on success. If `h1`
446  
    The handler `h1` is called with the task's result on success. If `h1`
447  
    is also invocable with `std::exception_ptr`, it handles exceptions too.
447  
    is also invocable with `std::exception_ptr`, it handles exceptions too.
448  
    Otherwise, exceptions are rethrown.
448  
    Otherwise, exceptions are rethrown.
449  

449  

450  
    @par Thread Safety
450  
    @par Thread Safety
451  
    The handler may be called from any thread where the executor
451  
    The handler may be called from any thread where the executor
452  
    schedules work.
452  
    schedules work.
453  

453  

454  
    @par Example
454  
    @par Example
455  
    @code
455  
    @code
456  
    // Handler for result only (exceptions rethrown)
456  
    // Handler for result only (exceptions rethrown)
457  
    run_async(ex, [](int result) {
457  
    run_async(ex, [](int result) {
458  
        std::cout << "Got: " << result << "\n";
458  
        std::cout << "Got: " << result << "\n";
459  
    })(compute_value());
459  
    })(compute_value());
460  

460  

461  
    // Overloaded handler for both result and exception
461  
    // Overloaded handler for both result and exception
462  
    run_async(ex, overloaded{
462  
    run_async(ex, overloaded{
463  
        [](int result) { std::cout << "Got: " << result << "\n"; },
463  
        [](int result) { std::cout << "Got: " << result << "\n"; },
464  
        [](std::exception_ptr) { std::cout << "Failed\n"; }
464  
        [](std::exception_ptr) { std::cout << "Failed\n"; }
465  
    })(compute_value());
465  
    })(compute_value());
466  
    @endcode
466  
    @endcode
467  

467  

468  
    @param ex The executor to execute the task on.
468  
    @param ex The executor to execute the task on.
469  
    @param h1 The handler to invoke with the result (and optionally exception).
469  
    @param h1 The handler to invoke with the result (and optionally exception).
470  

470  

471  
    @return A wrapper that accepts a `task<T>` for immediate execution.
471  
    @return A wrapper that accepts a `task<T>` for immediate execution.
472  

472  

473  
    @see task
473  
    @see task
474  
    @see executor
474  
    @see executor
475  
*/
475  
*/
476  
template<Executor Ex, class H1>
476  
template<Executor Ex, class H1>
477  
[[nodiscard]] auto
477  
[[nodiscard]] auto
478  
run_async(Ex ex, H1 h1)
478  
run_async(Ex ex, H1 h1)
479  
{
479  
{
480  
    auto* mr = ex.context().get_frame_allocator();
480  
    auto* mr = ex.context().get_frame_allocator();
481  
    return run_async_wrapper<Ex, detail::handler_pair<H1, detail::default_handler>, std::pmr::memory_resource*>(
481  
    return run_async_wrapper<Ex, detail::handler_pair<H1, detail::default_handler>, std::pmr::memory_resource*>(
482  
        std::move(ex),
482  
        std::move(ex),
483  
        std::stop_token{},
483  
        std::stop_token{},
484  
        detail::handler_pair<H1, detail::default_handler>{std::move(h1)},
484  
        detail::handler_pair<H1, detail::default_handler>{std::move(h1)},
485  
        mr);
485  
        mr);
486  
}
486  
}
487  

487  

488  
/** Asynchronously launch a lazy task with separate result and error handlers.
488  
/** Asynchronously launch a lazy task with separate result and error handlers.
489  

489  

490  
    The handler `h1` is called with the task's result on success.
490  
    The handler `h1` is called with the task's result on success.
491  
    The handler `h2` is called with the exception_ptr on failure.
491  
    The handler `h2` is called with the exception_ptr on failure.
492  

492  

493  
    @par Thread Safety
493  
    @par Thread Safety
494  
    The handlers may be called from any thread where the executor
494  
    The handlers may be called from any thread where the executor
495  
    schedules work.
495  
    schedules work.
496  

496  

497  
    @par Example
497  
    @par Example
498  
    @code
498  
    @code
499  
    run_async(ex,
499  
    run_async(ex,
500  
        [](int result) { std::cout << "Got: " << result << "\n"; },
500  
        [](int result) { std::cout << "Got: " << result << "\n"; },
501  
        [](std::exception_ptr ep) {
501  
        [](std::exception_ptr ep) {
502  
            try { std::rethrow_exception(ep); }
502  
            try { std::rethrow_exception(ep); }
503  
            catch (std::exception const& e) {
503  
            catch (std::exception const& e) {
504  
                std::cout << "Error: " << e.what() << "\n";
504  
                std::cout << "Error: " << e.what() << "\n";
505  
            }
505  
            }
506  
        }
506  
        }
507  
    )(compute_value());
507  
    )(compute_value());
508  
    @endcode
508  
    @endcode
509  

509  

510  
    @param ex The executor to execute the task on.
510  
    @param ex The executor to execute the task on.
511  
    @param h1 The handler to invoke with the result on success.
511  
    @param h1 The handler to invoke with the result on success.
512  
    @param h2 The handler to invoke with the exception on failure.
512  
    @param h2 The handler to invoke with the exception on failure.
513  

513  

514  
    @return A wrapper that accepts a `task<T>` for immediate execution.
514  
    @return A wrapper that accepts a `task<T>` for immediate execution.
515  

515  

516  
    @see task
516  
    @see task
517  
    @see executor
517  
    @see executor
518  
*/
518  
*/
519  
template<Executor Ex, class H1, class H2>
519  
template<Executor Ex, class H1, class H2>
520  
[[nodiscard]] auto
520  
[[nodiscard]] auto
521  
run_async(Ex ex, H1 h1, H2 h2)
521  
run_async(Ex ex, H1 h1, H2 h2)
522  
{
522  
{
523  
    auto* mr = ex.context().get_frame_allocator();
523  
    auto* mr = ex.context().get_frame_allocator();
524  
    return run_async_wrapper<Ex, detail::handler_pair<H1, H2>, std::pmr::memory_resource*>(
524  
    return run_async_wrapper<Ex, detail::handler_pair<H1, H2>, std::pmr::memory_resource*>(
525  
        std::move(ex),
525  
        std::move(ex),
526  
        std::stop_token{},
526  
        std::stop_token{},
527  
        detail::handler_pair<H1, H2>{std::move(h1), std::move(h2)},
527  
        detail::handler_pair<H1, H2>{std::move(h1), std::move(h2)},
528  
        mr);
528  
        mr);
529  
}
529  
}
530  

530  

531  
// Ex + stop_token
531  
// Ex + stop_token
532  

532  

533  
/** Asynchronously launch a lazy task with stop token support.
533  
/** Asynchronously launch a lazy task with stop token support.
534  

534  

535  
    The stop token is propagated to the task, enabling cooperative
535  
    The stop token is propagated to the task, enabling cooperative
536  
    cancellation. With no handlers, the result is discarded and
536  
    cancellation. With no handlers, the result is discarded and
537  
    exceptions are rethrown.
537  
    exceptions are rethrown.
538  

538  

539  
    @par Thread Safety
539  
    @par Thread Safety
540  
    The wrapper may be called from any thread where the executor
540  
    The wrapper may be called from any thread where the executor
541  
    schedules work.
541  
    schedules work.
542  

542  

543  
    @par Example
543  
    @par Example
544  
    @code
544  
    @code
545  
    std::stop_source source;
545  
    std::stop_source source;
546  
    run_async(ex, source.get_token())(cancellable_task());
546  
    run_async(ex, source.get_token())(cancellable_task());
547  
    // Later: source.request_stop();
547  
    // Later: source.request_stop();
548  
    @endcode
548  
    @endcode
549  

549  

550  
    @param ex The executor to execute the task on.
550  
    @param ex The executor to execute the task on.
551  
    @param st The stop token for cooperative cancellation.
551  
    @param st The stop token for cooperative cancellation.
552  

552  

553  
    @return A wrapper that accepts a `task<T>` for immediate execution.
553  
    @return A wrapper that accepts a `task<T>` for immediate execution.
554  

554  

555  
    @see task
555  
    @see task
556  
    @see executor
556  
    @see executor
557  
*/
557  
*/
558  
template<Executor Ex>
558  
template<Executor Ex>
559  
[[nodiscard]] auto
559  
[[nodiscard]] auto
560  
run_async(Ex ex, std::stop_token st)
560  
run_async(Ex ex, std::stop_token st)
561  
{
561  
{
562  
    auto* mr = ex.context().get_frame_allocator();
562  
    auto* mr = ex.context().get_frame_allocator();
563  
    return run_async_wrapper<Ex, detail::default_handler, std::pmr::memory_resource*>(
563  
    return run_async_wrapper<Ex, detail::default_handler, std::pmr::memory_resource*>(
564  
        std::move(ex),
564  
        std::move(ex),
565  
        std::move(st),
565  
        std::move(st),
566  
        detail::default_handler{},
566  
        detail::default_handler{},
567  
        mr);
567  
        mr);
568  
}
568  
}
569  

569  

570  
/** Asynchronously launch a lazy task with stop token and result handler.
570  
/** Asynchronously launch a lazy task with stop token and result handler.
571  

571  

572  
    The stop token is propagated to the task for cooperative cancellation.
572  
    The stop token is propagated to the task for cooperative cancellation.
573  
    The handler `h1` is called with the result on success, and optionally
573  
    The handler `h1` is called with the result on success, and optionally
574  
    with exception_ptr if it accepts that type.
574  
    with exception_ptr if it accepts that type.
575  

575  

576  
    @param ex The executor to execute the task on.
576  
    @param ex The executor to execute the task on.
577  
    @param st The stop token for cooperative cancellation.
577  
    @param st The stop token for cooperative cancellation.
578  
    @param h1 The handler to invoke with the result (and optionally exception).
578  
    @param h1 The handler to invoke with the result (and optionally exception).
579  

579  

580  
    @return A wrapper that accepts a `task<T>` for immediate execution.
580  
    @return A wrapper that accepts a `task<T>` for immediate execution.
581  

581  

582  
    @see task
582  
    @see task
583  
    @see executor
583  
    @see executor
584  
*/
584  
*/
585  
template<Executor Ex, class H1>
585  
template<Executor Ex, class H1>
586  
[[nodiscard]] auto
586  
[[nodiscard]] auto
587  
run_async(Ex ex, std::stop_token st, H1 h1)
587  
run_async(Ex ex, std::stop_token st, H1 h1)
588  
{
588  
{
589  
    auto* mr = ex.context().get_frame_allocator();
589  
    auto* mr = ex.context().get_frame_allocator();
590  
    return run_async_wrapper<Ex, detail::handler_pair<H1, detail::default_handler>, std::pmr::memory_resource*>(
590  
    return run_async_wrapper<Ex, detail::handler_pair<H1, detail::default_handler>, std::pmr::memory_resource*>(
591  
        std::move(ex),
591  
        std::move(ex),
592  
        std::move(st),
592  
        std::move(st),
593  
        detail::handler_pair<H1, detail::default_handler>{std::move(h1)},
593  
        detail::handler_pair<H1, detail::default_handler>{std::move(h1)},
594  
        mr);
594  
        mr);
595  
}
595  
}
596  

596  

597  
/** Asynchronously launch a lazy task with stop token and separate handlers.
597  
/** Asynchronously launch a lazy task with stop token and separate handlers.
598  

598  

599  
    The stop token is propagated to the task for cooperative cancellation.
599  
    The stop token is propagated to the task for cooperative cancellation.
600  
    The handler `h1` is called on success, `h2` on failure.
600  
    The handler `h1` is called on success, `h2` on failure.
601  

601  

602  
    @param ex The executor to execute the task on.
602  
    @param ex The executor to execute the task on.
603  
    @param st The stop token for cooperative cancellation.
603  
    @param st The stop token for cooperative cancellation.
604  
    @param h1 The handler to invoke with the result on success.
604  
    @param h1 The handler to invoke with the result on success.
605  
    @param h2 The handler to invoke with the exception on failure.
605  
    @param h2 The handler to invoke with the exception on failure.
606  

606  

607  
    @return A wrapper that accepts a `task<T>` for immediate execution.
607  
    @return A wrapper that accepts a `task<T>` for immediate execution.
608  

608  

609  
    @see task
609  
    @see task
610  
    @see executor
610  
    @see executor
611  
*/
611  
*/
612  
template<Executor Ex, class H1, class H2>
612  
template<Executor Ex, class H1, class H2>
613  
[[nodiscard]] auto
613  
[[nodiscard]] auto
614  
run_async(Ex ex, std::stop_token st, H1 h1, H2 h2)
614  
run_async(Ex ex, std::stop_token st, H1 h1, H2 h2)
615  
{
615  
{
616  
    auto* mr = ex.context().get_frame_allocator();
616  
    auto* mr = ex.context().get_frame_allocator();
617  
    return run_async_wrapper<Ex, detail::handler_pair<H1, H2>, std::pmr::memory_resource*>(
617  
    return run_async_wrapper<Ex, detail::handler_pair<H1, H2>, std::pmr::memory_resource*>(
618  
        std::move(ex),
618  
        std::move(ex),
619  
        std::move(st),
619  
        std::move(st),
620  
        detail::handler_pair<H1, H2>{std::move(h1), std::move(h2)},
620  
        detail::handler_pair<H1, H2>{std::move(h1), std::move(h2)},
621  
        mr);
621  
        mr);
622  
}
622  
}
623  

623  

624  
// Ex + memory_resource*
624  
// Ex + memory_resource*
625  

625  

626  
/** Asynchronously launch a lazy task with custom memory resource.
626  
/** Asynchronously launch a lazy task with custom memory resource.
627  

627  

628  
    The memory resource is used for coroutine frame allocation. The caller
628  
    The memory resource is used for coroutine frame allocation. The caller
629  
    is responsible for ensuring the memory resource outlives all tasks.
629  
    is responsible for ensuring the memory resource outlives all tasks.
630  

630  

631  
    @param ex The executor to execute the task on.
631  
    @param ex The executor to execute the task on.
632  
    @param mr The memory resource for frame allocation.
632  
    @param mr The memory resource for frame allocation.
633  

633  

634  
    @return A wrapper that accepts a `task<T>` for immediate execution.
634  
    @return A wrapper that accepts a `task<T>` for immediate execution.
635  

635  

636  
    @see task
636  
    @see task
637  
    @see executor
637  
    @see executor
638  
*/
638  
*/
639  
template<Executor Ex>
639  
template<Executor Ex>
640  
[[nodiscard]] auto
640  
[[nodiscard]] auto
641  
run_async(Ex ex, std::pmr::memory_resource* mr)
641  
run_async(Ex ex, std::pmr::memory_resource* mr)
642  
{
642  
{
643  
    return run_async_wrapper<Ex, detail::default_handler, std::pmr::memory_resource*>(
643  
    return run_async_wrapper<Ex, detail::default_handler, std::pmr::memory_resource*>(
644  
        std::move(ex),
644  
        std::move(ex),
645  
        std::stop_token{},
645  
        std::stop_token{},
646  
        detail::default_handler{},
646  
        detail::default_handler{},
647  
        mr);
647  
        mr);
648  
}
648  
}
649  

649  

650  
/** Asynchronously launch a lazy task with memory resource and handler.
650  
/** Asynchronously launch a lazy task with memory resource and handler.
651  

651  

652  
    @param ex The executor to execute the task on.
652  
    @param ex The executor to execute the task on.
653  
    @param mr The memory resource for frame allocation.
653  
    @param mr The memory resource for frame allocation.
654  
    @param h1 The handler to invoke with the result (and optionally exception).
654  
    @param h1 The handler to invoke with the result (and optionally exception).
655  

655  

656  
    @return A wrapper that accepts a `task<T>` for immediate execution.
656  
    @return A wrapper that accepts a `task<T>` for immediate execution.
657  

657  

658  
    @see task
658  
    @see task
659  
    @see executor
659  
    @see executor
660  
*/
660  
*/
661  
template<Executor Ex, class H1>
661  
template<Executor Ex, class H1>
662  
[[nodiscard]] auto
662  
[[nodiscard]] auto
663  
run_async(Ex ex, std::pmr::memory_resource* mr, H1 h1)
663  
run_async(Ex ex, std::pmr::memory_resource* mr, H1 h1)
664  
{
664  
{
665  
    return run_async_wrapper<Ex, detail::handler_pair<H1, detail::default_handler>, std::pmr::memory_resource*>(
665  
    return run_async_wrapper<Ex, detail::handler_pair<H1, detail::default_handler>, std::pmr::memory_resource*>(
666  
        std::move(ex),
666  
        std::move(ex),
667  
        std::stop_token{},
667  
        std::stop_token{},
668  
        detail::handler_pair<H1, detail::default_handler>{std::move(h1)},
668  
        detail::handler_pair<H1, detail::default_handler>{std::move(h1)},
669  
        mr);
669  
        mr);
670  
}
670  
}
671  

671  

672  
/** Asynchronously launch a lazy task with memory resource and handlers.
672  
/** Asynchronously launch a lazy task with memory resource and handlers.
673  

673  

674  
    @param ex The executor to execute the task on.
674  
    @param ex The executor to execute the task on.
675  
    @param mr The memory resource for frame allocation.
675  
    @param mr The memory resource for frame allocation.
676  
    @param h1 The handler to invoke with the result on success.
676  
    @param h1 The handler to invoke with the result on success.
677  
    @param h2 The handler to invoke with the exception on failure.
677  
    @param h2 The handler to invoke with the exception on failure.
678  

678  

679  
    @return A wrapper that accepts a `task<T>` for immediate execution.
679  
    @return A wrapper that accepts a `task<T>` for immediate execution.
680  

680  

681  
    @see task
681  
    @see task
682  
    @see executor
682  
    @see executor
683  
*/
683  
*/
684  
template<Executor Ex, class H1, class H2>
684  
template<Executor Ex, class H1, class H2>
685  
[[nodiscard]] auto
685  
[[nodiscard]] auto
686  
run_async(Ex ex, std::pmr::memory_resource* mr, H1 h1, H2 h2)
686  
run_async(Ex ex, std::pmr::memory_resource* mr, H1 h1, H2 h2)
687  
{
687  
{
688  
    return run_async_wrapper<Ex, detail::handler_pair<H1, H2>, std::pmr::memory_resource*>(
688  
    return run_async_wrapper<Ex, detail::handler_pair<H1, H2>, std::pmr::memory_resource*>(
689  
        std::move(ex),
689  
        std::move(ex),
690  
        std::stop_token{},
690  
        std::stop_token{},
691  
        detail::handler_pair<H1, H2>{std::move(h1), std::move(h2)},
691  
        detail::handler_pair<H1, H2>{std::move(h1), std::move(h2)},
692  
        mr);
692  
        mr);
693  
}
693  
}
694  

694  

695  
// Ex + stop_token + memory_resource*
695  
// Ex + stop_token + memory_resource*
696  

696  

697  
/** Asynchronously launch a lazy task with stop token and memory resource.
697  
/** Asynchronously launch a lazy task with stop token and memory resource.
698  

698  

699  
    @param ex The executor to execute the task on.
699  
    @param ex The executor to execute the task on.
700  
    @param st The stop token for cooperative cancellation.
700  
    @param st The stop token for cooperative cancellation.
701  
    @param mr The memory resource for frame allocation.
701  
    @param mr The memory resource for frame allocation.
702  

702  

703  
    @return A wrapper that accepts a `task<T>` for immediate execution.
703  
    @return A wrapper that accepts a `task<T>` for immediate execution.
704  

704  

705  
    @see task
705  
    @see task
706  
    @see executor
706  
    @see executor
707  
*/
707  
*/
708  
template<Executor Ex>
708  
template<Executor Ex>
709  
[[nodiscard]] auto
709  
[[nodiscard]] auto
710  
run_async(Ex ex, std::stop_token st, std::pmr::memory_resource* mr)
710  
run_async(Ex ex, std::stop_token st, std::pmr::memory_resource* mr)
711  
{
711  
{
712  
    return run_async_wrapper<Ex, detail::default_handler, std::pmr::memory_resource*>(
712  
    return run_async_wrapper<Ex, detail::default_handler, std::pmr::memory_resource*>(
713  
        std::move(ex),
713  
        std::move(ex),
714  
        std::move(st),
714  
        std::move(st),
715  
        detail::default_handler{},
715  
        detail::default_handler{},
716  
        mr);
716  
        mr);
717  
}
717  
}
718  

718  

719  
/** Asynchronously launch a lazy task with stop token, memory resource, and handler.
719  
/** Asynchronously launch a lazy task with stop token, memory resource, and handler.
720  

720  

721  
    @param ex The executor to execute the task on.
721  
    @param ex The executor to execute the task on.
722  
    @param st The stop token for cooperative cancellation.
722  
    @param st The stop token for cooperative cancellation.
723  
    @param mr The memory resource for frame allocation.
723  
    @param mr The memory resource for frame allocation.
724  
    @param h1 The handler to invoke with the result (and optionally exception).
724  
    @param h1 The handler to invoke with the result (and optionally exception).
725  

725  

726  
    @return A wrapper that accepts a `task<T>` for immediate execution.
726  
    @return A wrapper that accepts a `task<T>` for immediate execution.
727  

727  

728  
    @see task
728  
    @see task
729  
    @see executor
729  
    @see executor
730  
*/
730  
*/
731  
template<Executor Ex, class H1>
731  
template<Executor Ex, class H1>
732  
[[nodiscard]] auto
732  
[[nodiscard]] auto
733  
run_async(Ex ex, std::stop_token st, std::pmr::memory_resource* mr, H1 h1)
733  
run_async(Ex ex, std::stop_token st, std::pmr::memory_resource* mr, H1 h1)
734  
{
734  
{
735  
    return run_async_wrapper<Ex, detail::handler_pair<H1, detail::default_handler>, std::pmr::memory_resource*>(
735  
    return run_async_wrapper<Ex, detail::handler_pair<H1, detail::default_handler>, std::pmr::memory_resource*>(
736  
        std::move(ex),
736  
        std::move(ex),
737  
        std::move(st),
737  
        std::move(st),
738  
        detail::handler_pair<H1, detail::default_handler>{std::move(h1)},
738  
        detail::handler_pair<H1, detail::default_handler>{std::move(h1)},
739  
        mr);
739  
        mr);
740  
}
740  
}
741  

741  

742  
/** Asynchronously launch a lazy task with stop token, memory resource, and handlers.
742  
/** Asynchronously launch a lazy task with stop token, memory resource, and handlers.
743  

743  

744  
    @param ex The executor to execute the task on.
744  
    @param ex The executor to execute the task on.
745  
    @param st The stop token for cooperative cancellation.
745  
    @param st The stop token for cooperative cancellation.
746  
    @param mr The memory resource for frame allocation.
746  
    @param mr The memory resource for frame allocation.
747  
    @param h1 The handler to invoke with the result on success.
747  
    @param h1 The handler to invoke with the result on success.
748  
    @param h2 The handler to invoke with the exception on failure.
748  
    @param h2 The handler to invoke with the exception on failure.
749  

749  

750  
    @return A wrapper that accepts a `task<T>` for immediate execution.
750  
    @return A wrapper that accepts a `task<T>` for immediate execution.
751  

751  

752  
    @see task
752  
    @see task
753  
    @see executor
753  
    @see executor
754  
*/
754  
*/
755  
template<Executor Ex, class H1, class H2>
755  
template<Executor Ex, class H1, class H2>
756  
[[nodiscard]] auto
756  
[[nodiscard]] auto
757  
run_async(Ex ex, std::stop_token st, std::pmr::memory_resource* mr, H1 h1, H2 h2)
757  
run_async(Ex ex, std::stop_token st, std::pmr::memory_resource* mr, H1 h1, H2 h2)
758  
{
758  
{
759  
    return run_async_wrapper<Ex, detail::handler_pair<H1, H2>, std::pmr::memory_resource*>(
759  
    return run_async_wrapper<Ex, detail::handler_pair<H1, H2>, std::pmr::memory_resource*>(
760  
        std::move(ex),
760  
        std::move(ex),
761  
        std::move(st),
761  
        std::move(st),
762  
        detail::handler_pair<H1, H2>{std::move(h1), std::move(h2)},
762  
        detail::handler_pair<H1, H2>{std::move(h1), std::move(h2)},
763  
        mr);
763  
        mr);
764  
}
764  
}
765  

765  

766  
// Ex + standard Allocator (value type)
766  
// Ex + standard Allocator (value type)
767  

767  

768  
/** Asynchronously launch a lazy task with custom allocator.
768  
/** Asynchronously launch a lazy task with custom allocator.
769  

769  

770  
    The allocator is wrapped in a frame_memory_resource and stored in the
770  
    The allocator is wrapped in a frame_memory_resource and stored in the
771  
    run_async_trampoline, ensuring it outlives all coroutine frames.
771  
    run_async_trampoline, ensuring it outlives all coroutine frames.
772  

772  

773  
    @param ex The executor to execute the task on.
773  
    @param ex The executor to execute the task on.
774  
    @param alloc The allocator for frame allocation (copied and stored).
774  
    @param alloc The allocator for frame allocation (copied and stored).
775  

775  

776  
    @return A wrapper that accepts a `task<T>` for immediate execution.
776  
    @return A wrapper that accepts a `task<T>` for immediate execution.
777  

777  

778  
    @see task
778  
    @see task
779  
    @see executor
779  
    @see executor
780  
*/
780  
*/
781  
template<Executor Ex, detail::Allocator Alloc>
781  
template<Executor Ex, detail::Allocator Alloc>
782  
[[nodiscard]] auto
782  
[[nodiscard]] auto
783  
run_async(Ex ex, Alloc alloc)
783  
run_async(Ex ex, Alloc alloc)
784  
{
784  
{
785  
    return run_async_wrapper<Ex, detail::default_handler, Alloc>(
785  
    return run_async_wrapper<Ex, detail::default_handler, Alloc>(
786  
        std::move(ex),
786  
        std::move(ex),
787  
        std::stop_token{},
787  
        std::stop_token{},
788  
        detail::default_handler{},
788  
        detail::default_handler{},
789  
        std::move(alloc));
789  
        std::move(alloc));
790  
}
790  
}
791  

791  

792  
/** Asynchronously launch a lazy task with allocator and handler.
792  
/** Asynchronously launch a lazy task with allocator and handler.
793  

793  

794  
    @param ex The executor to execute the task on.
794  
    @param ex The executor to execute the task on.
795  
    @param alloc The allocator for frame allocation (copied and stored).
795  
    @param alloc The allocator for frame allocation (copied and stored).
796  
    @param h1 The handler to invoke with the result (and optionally exception).
796  
    @param h1 The handler to invoke with the result (and optionally exception).
797  

797  

798  
    @return A wrapper that accepts a `task<T>` for immediate execution.
798  
    @return A wrapper that accepts a `task<T>` for immediate execution.
799  

799  

800  
    @see task
800  
    @see task
801  
    @see executor
801  
    @see executor
802  
*/
802  
*/
803  
template<Executor Ex, detail::Allocator Alloc, class H1>
803  
template<Executor Ex, detail::Allocator Alloc, class H1>
804  
[[nodiscard]] auto
804  
[[nodiscard]] auto
805  
run_async(Ex ex, Alloc alloc, H1 h1)
805  
run_async(Ex ex, Alloc alloc, H1 h1)
806  
{
806  
{
807  
    return run_async_wrapper<Ex, detail::handler_pair<H1, detail::default_handler>, Alloc>(
807  
    return run_async_wrapper<Ex, detail::handler_pair<H1, detail::default_handler>, Alloc>(
808  
        std::move(ex),
808  
        std::move(ex),
809  
        std::stop_token{},
809  
        std::stop_token{},
810  
        detail::handler_pair<H1, detail::default_handler>{std::move(h1)},
810  
        detail::handler_pair<H1, detail::default_handler>{std::move(h1)},
811  
        std::move(alloc));
811  
        std::move(alloc));
812  
}
812  
}
813  

813  

814  
/** Asynchronously launch a lazy task with allocator and handlers.
814  
/** Asynchronously launch a lazy task with allocator and handlers.
815  

815  

816  
    @param ex The executor to execute the task on.
816  
    @param ex The executor to execute the task on.
817  
    @param alloc The allocator for frame allocation (copied and stored).
817  
    @param alloc The allocator for frame allocation (copied and stored).
818  
    @param h1 The handler to invoke with the result on success.
818  
    @param h1 The handler to invoke with the result on success.
819  
    @param h2 The handler to invoke with the exception on failure.
819  
    @param h2 The handler to invoke with the exception on failure.
820  

820  

821  
    @return A wrapper that accepts a `task<T>` for immediate execution.
821  
    @return A wrapper that accepts a `task<T>` for immediate execution.
822  

822  

823  
    @see task
823  
    @see task
824  
    @see executor
824  
    @see executor
825  
*/
825  
*/
826  
template<Executor Ex, detail::Allocator Alloc, class H1, class H2>
826  
template<Executor Ex, detail::Allocator Alloc, class H1, class H2>
827  
[[nodiscard]] auto
827  
[[nodiscard]] auto
828  
run_async(Ex ex, Alloc alloc, H1 h1, H2 h2)
828  
run_async(Ex ex, Alloc alloc, H1 h1, H2 h2)
829  
{
829  
{
830  
    return run_async_wrapper<Ex, detail::handler_pair<H1, H2>, Alloc>(
830  
    return run_async_wrapper<Ex, detail::handler_pair<H1, H2>, Alloc>(
831  
        std::move(ex),
831  
        std::move(ex),
832  
        std::stop_token{},
832  
        std::stop_token{},
833  
        detail::handler_pair<H1, H2>{std::move(h1), std::move(h2)},
833  
        detail::handler_pair<H1, H2>{std::move(h1), std::move(h2)},
834  
        std::move(alloc));
834  
        std::move(alloc));
835  
}
835  
}
836  

836  

837  
// Ex + stop_token + standard Allocator
837  
// Ex + stop_token + standard Allocator
838  

838  

839  
/** Asynchronously launch a lazy task with stop token and allocator.
839  
/** Asynchronously launch a lazy task with stop token and allocator.
840  

840  

841  
    @param ex The executor to execute the task on.
841  
    @param ex The executor to execute the task on.
842  
    @param st The stop token for cooperative cancellation.
842  
    @param st The stop token for cooperative cancellation.
843  
    @param alloc The allocator for frame allocation (copied and stored).
843  
    @param alloc The allocator for frame allocation (copied and stored).
844  

844  

845  
    @return A wrapper that accepts a `task<T>` for immediate execution.
845  
    @return A wrapper that accepts a `task<T>` for immediate execution.
846  

846  

847  
    @see task
847  
    @see task
848  
    @see executor
848  
    @see executor
849  
*/
849  
*/
850  
template<Executor Ex, detail::Allocator Alloc>
850  
template<Executor Ex, detail::Allocator Alloc>
851  
[[nodiscard]] auto
851  
[[nodiscard]] auto
852  
run_async(Ex ex, std::stop_token st, Alloc alloc)
852  
run_async(Ex ex, std::stop_token st, Alloc alloc)
853  
{
853  
{
854  
    return run_async_wrapper<Ex, detail::default_handler, Alloc>(
854  
    return run_async_wrapper<Ex, detail::default_handler, Alloc>(
855  
        std::move(ex),
855  
        std::move(ex),
856  
        std::move(st),
856  
        std::move(st),
857  
        detail::default_handler{},
857  
        detail::default_handler{},
858  
        std::move(alloc));
858  
        std::move(alloc));
859  
}
859  
}
860  

860  

861  
/** Asynchronously launch a lazy task with stop token, allocator, and handler.
861  
/** Asynchronously launch a lazy task with stop token, allocator, and handler.
862  

862  

863  
    @param ex The executor to execute the task on.
863  
    @param ex The executor to execute the task on.
864  
    @param st The stop token for cooperative cancellation.
864  
    @param st The stop token for cooperative cancellation.
865  
    @param alloc The allocator for frame allocation (copied and stored).
865  
    @param alloc The allocator for frame allocation (copied and stored).
866  
    @param h1 The handler to invoke with the result (and optionally exception).
866  
    @param h1 The handler to invoke with the result (and optionally exception).
867  

867  

868  
    @return A wrapper that accepts a `task<T>` for immediate execution.
868  
    @return A wrapper that accepts a `task<T>` for immediate execution.
869  

869  

870  
    @see task
870  
    @see task
871  
    @see executor
871  
    @see executor
872  
*/
872  
*/
873  
template<Executor Ex, detail::Allocator Alloc, class H1>
873  
template<Executor Ex, detail::Allocator Alloc, class H1>
874  
[[nodiscard]] auto
874  
[[nodiscard]] auto
875  
run_async(Ex ex, std::stop_token st, Alloc alloc, H1 h1)
875  
run_async(Ex ex, std::stop_token st, Alloc alloc, H1 h1)
876  
{
876  
{
877  
    return run_async_wrapper<Ex, detail::handler_pair<H1, detail::default_handler>, Alloc>(
877  
    return run_async_wrapper<Ex, detail::handler_pair<H1, detail::default_handler>, Alloc>(
878  
        std::move(ex),
878  
        std::move(ex),
879  
        std::move(st),
879  
        std::move(st),
880  
        detail::handler_pair<H1, detail::default_handler>{std::move(h1)},
880  
        detail::handler_pair<H1, detail::default_handler>{std::move(h1)},
881  
        std::move(alloc));
881  
        std::move(alloc));
882  
}
882  
}
883  

883  

884  
/** Asynchronously launch a lazy task with stop token, allocator, and handlers.
884  
/** Asynchronously launch a lazy task with stop token, allocator, and handlers.
885  

885  

886  
    @param ex The executor to execute the task on.
886  
    @param ex The executor to execute the task on.
887  
    @param st The stop token for cooperative cancellation.
887  
    @param st The stop token for cooperative cancellation.
888  
    @param alloc The allocator for frame allocation (copied and stored).
888  
    @param alloc The allocator for frame allocation (copied and stored).
889  
    @param h1 The handler to invoke with the result on success.
889  
    @param h1 The handler to invoke with the result on success.
890  
    @param h2 The handler to invoke with the exception on failure.
890  
    @param h2 The handler to invoke with the exception on failure.
891  

891  

892  
    @return A wrapper that accepts a `task<T>` for immediate execution.
892  
    @return A wrapper that accepts a `task<T>` for immediate execution.
893  

893  

894  
    @see task
894  
    @see task
895  
    @see executor
895  
    @see executor
896  
*/
896  
*/
897  
template<Executor Ex, detail::Allocator Alloc, class H1, class H2>
897  
template<Executor Ex, detail::Allocator Alloc, class H1, class H2>
898  
[[nodiscard]] auto
898  
[[nodiscard]] auto
899  
run_async(Ex ex, std::stop_token st, Alloc alloc, H1 h1, H2 h2)
899  
run_async(Ex ex, std::stop_token st, Alloc alloc, H1 h1, H2 h2)
900  
{
900  
{
901  
    return run_async_wrapper<Ex, detail::handler_pair<H1, H2>, Alloc>(
901  
    return run_async_wrapper<Ex, detail::handler_pair<H1, H2>, Alloc>(
902  
        std::move(ex),
902  
        std::move(ex),
903  
        std::move(st),
903  
        std::move(st),
904  
        detail::handler_pair<H1, H2>{std::move(h1), std::move(h2)},
904  
        detail::handler_pair<H1, H2>{std::move(h1), std::move(h2)},
905  
        std::move(alloc));
905  
        std::move(alloc));
906  
}
906  
}
907  

907  

908  
} // namespace capy
908  
} // namespace capy
909  
} // namespace boost
909  
} // namespace boost
910  

910  

911  
#endif
911  
#endif