1  
//
1  
//
2  
// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
2  
// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot 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_HPP
10  
#ifndef BOOST_CAPY_RUN_HPP
11  
#define BOOST_CAPY_RUN_HPP
11  
#define BOOST_CAPY_RUN_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/concept/executor.hpp>
15  
#include <boost/capy/concept/executor.hpp>
16  
#include <boost/capy/concept/io_launchable_task.hpp>
16  
#include <boost/capy/concept/io_launchable_task.hpp>
17  
#include <boost/capy/coro.hpp>
17  
#include <boost/capy/coro.hpp>
18  
#include <boost/capy/ex/executor_ref.hpp>
18  
#include <boost/capy/ex/executor_ref.hpp>
19  
#include <boost/capy/ex/frame_allocator.hpp>
19  
#include <boost/capy/ex/frame_allocator.hpp>
20  

20  

21  
#include <memory_resource>
21  
#include <memory_resource>
22  
#include <stop_token>
22  
#include <stop_token>
23  
#include <type_traits>
23  
#include <type_traits>
24  
#include <utility>
24  
#include <utility>
25  
#include <variant>
25  
#include <variant>
26  

26  

27  
/*
27  
/*
28  
    Allocator Lifetime Strategy
28  
    Allocator Lifetime Strategy
29  
    ===========================
29  
    ===========================
30  

30  

31  
    When using run() with a custom allocator:
31  
    When using run() with a custom allocator:
32  

32  

33  
        co_await run(ex, alloc)(my_task());
33  
        co_await run(ex, alloc)(my_task());
34  

34  

35  
    The evaluation order is:
35  
    The evaluation order is:
36  
        1. run(ex, alloc) creates a temporary wrapper
36  
        1. run(ex, alloc) creates a temporary wrapper
37  
        2. my_task() allocates its coroutine frame using TLS
37  
        2. my_task() allocates its coroutine frame using TLS
38  
        3. operator() returns an awaitable
38  
        3. operator() returns an awaitable
39  
        4. Wrapper temporary is DESTROYED
39  
        4. Wrapper temporary is DESTROYED
40  
        5. co_await suspends caller, resumes task
40  
        5. co_await suspends caller, resumes task
41  
        6. Task body executes (wrapper is already dead!)
41  
        6. Task body executes (wrapper is already dead!)
42  

42  

43  
    Problem: The wrapper's frame_memory_resource dies before the task
43  
    Problem: The wrapper's frame_memory_resource dies before the task
44  
    body runs. When initial_suspend::await_resume() restores TLS from
44  
    body runs. When initial_suspend::await_resume() restores TLS from
45  
    the saved pointer, it would point to dead memory.
45  
    the saved pointer, it would point to dead memory.
46  

46  

47  
    Solution: Store a COPY of the allocator in the awaitable (not just
47  
    Solution: Store a COPY of the allocator in the awaitable (not just
48  
    the wrapper). The co_await mechanism extends the awaitable's lifetime
48  
    the wrapper). The co_await mechanism extends the awaitable's lifetime
49  
    until the await completes. In await_suspend, we overwrite the promise's
49  
    until the await completes. In await_suspend, we overwrite the promise's
50  
    saved frame_allocator pointer to point to the awaitable's resource.
50  
    saved frame_allocator pointer to point to the awaitable's resource.
51  

51  

52  
    This works because standard allocator copies are equivalent - memory
52  
    This works because standard allocator copies are equivalent - memory
53  
    allocated with one copy can be deallocated with another copy. The
53  
    allocated with one copy can be deallocated with another copy. The
54  
    task's own frame uses the footer-stored pointer (safe), while nested
54  
    task's own frame uses the footer-stored pointer (safe), while nested
55  
    task creation uses TLS pointing to the awaitable's resource (also safe).
55  
    task creation uses TLS pointing to the awaitable's resource (also safe).
56  
*/
56  
*/
57  

57  

58  
namespace boost::capy::detail {
58  
namespace boost::capy::detail {
59  

59  

60  
//----------------------------------------------------------
60  
//----------------------------------------------------------
61  
//
61  
//
62  
// run_awaitable_ex - with executor (executor switch)
62  
// run_awaitable_ex - with executor (executor switch)
63  
//
63  
//
64  
//----------------------------------------------------------
64  
//----------------------------------------------------------
65  

65  

66  
/** Awaitable that binds an IoLaunchableTask to a specific executor.
66  
/** Awaitable that binds an IoLaunchableTask to a specific executor.
67  

67  

68  
    Stores the executor and inner task by value. When co_awaited, the
68  
    Stores the executor and inner task by value. When co_awaited, the
69  
    co_await expression's lifetime extension keeps both alive for the
69  
    co_await expression's lifetime extension keeps both alive for the
70  
    duration of the operation.
70  
    duration of the operation.
71  

71  

72  
    @tparam Task The IoLaunchableTask type
72  
    @tparam Task The IoLaunchableTask type
73  
    @tparam Ex The executor type
73  
    @tparam Ex The executor type
74  
    @tparam InheritStopToken If true, inherit caller's stop token
74  
    @tparam InheritStopToken If true, inherit caller's stop token
75  
    @tparam Alloc The allocator type (void for no allocator)
75  
    @tparam Alloc The allocator type (void for no allocator)
76  
*/
76  
*/
77  
template<IoLaunchableTask Task, Executor Ex, bool InheritStopToken, class Alloc = void>
77  
template<IoLaunchableTask Task, Executor Ex, bool InheritStopToken, class Alloc = void>
78  
struct [[nodiscard]] run_awaitable_ex
78  
struct [[nodiscard]] run_awaitable_ex
79  
{
79  
{
80  
    Ex ex_;
80  
    Ex ex_;
81  
    frame_memory_resource<Alloc> resource_;
81  
    frame_memory_resource<Alloc> resource_;
82  
    Task inner_;
82  
    Task inner_;
83  
    std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_;
83  
    std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_;
84  

84  

85  
    // void allocator, inherit stop token
85  
    // void allocator, inherit stop token
86  
    run_awaitable_ex(Ex ex, Task inner)
86  
    run_awaitable_ex(Ex ex, Task inner)
87  
        requires (InheritStopToken && std::is_void_v<Alloc>)
87  
        requires (InheritStopToken && std::is_void_v<Alloc>)
88  
        : ex_(std::move(ex))
88  
        : ex_(std::move(ex))
89  
        , inner_(std::move(inner))
89  
        , inner_(std::move(inner))
90  
    {
90  
    {
91  
    }
91  
    }
92  

92  

93  
    // void allocator, explicit stop token
93  
    // void allocator, explicit stop token
94  
    run_awaitable_ex(Ex ex, Task inner, std::stop_token st)
94  
    run_awaitable_ex(Ex ex, Task inner, std::stop_token st)
95  
        requires (!InheritStopToken && std::is_void_v<Alloc>)
95  
        requires (!InheritStopToken && std::is_void_v<Alloc>)
96  
        : ex_(std::move(ex))
96  
        : ex_(std::move(ex))
97  
        , inner_(std::move(inner))
97  
        , inner_(std::move(inner))
98  
        , st_(std::move(st))
98  
        , st_(std::move(st))
99  
    {
99  
    {
100  
    }
100  
    }
101  

101  

102  
    // with allocator, inherit stop token (use template to avoid void parameter)
102  
    // with allocator, inherit stop token (use template to avoid void parameter)
103  
    template<class A>
103  
    template<class A>
104  
        requires (InheritStopToken && !std::is_void_v<Alloc> && std::same_as<A, Alloc>)
104  
        requires (InheritStopToken && !std::is_void_v<Alloc> && std::same_as<A, Alloc>)
105  
    run_awaitable_ex(Ex ex, A alloc, Task inner)
105  
    run_awaitable_ex(Ex ex, A alloc, Task inner)
106  
        : ex_(std::move(ex))
106  
        : ex_(std::move(ex))
107  
        , resource_(std::move(alloc))
107  
        , resource_(std::move(alloc))
108  
        , inner_(std::move(inner))
108  
        , inner_(std::move(inner))
109  
    {
109  
    {
110  
    }
110  
    }
111  

111  

112  
    // with allocator, explicit stop token (use template to avoid void parameter)
112  
    // with allocator, explicit stop token (use template to avoid void parameter)
113  
    template<class A>
113  
    template<class A>
114  
        requires (!InheritStopToken && !std::is_void_v<Alloc> && std::same_as<A, Alloc>)
114  
        requires (!InheritStopToken && !std::is_void_v<Alloc> && std::same_as<A, Alloc>)
115  
    run_awaitable_ex(Ex ex, A alloc, Task inner, std::stop_token st)
115  
    run_awaitable_ex(Ex ex, A alloc, Task inner, std::stop_token st)
116  
        : ex_(std::move(ex))
116  
        : ex_(std::move(ex))
117  
        , resource_(std::move(alloc))
117  
        , resource_(std::move(alloc))
118  
        , inner_(std::move(inner))
118  
        , inner_(std::move(inner))
119  
        , st_(std::move(st))
119  
        , st_(std::move(st))
120  
    {
120  
    {
121  
    }
121  
    }
122  

122  

123  
    bool await_ready() const noexcept
123  
    bool await_ready() const noexcept
124  
    {
124  
    {
125  
        return inner_.await_ready();
125  
        return inner_.await_ready();
126  
    }
126  
    }
127  

127  

128  
    decltype(auto) await_resume()
128  
    decltype(auto) await_resume()
129  
    {
129  
    {
130  
        return inner_.await_resume();
130  
        return inner_.await_resume();
131  
    }
131  
    }
132  

132  

133  
    template<typename Caller>
133  
    template<typename Caller>
134  
    coro await_suspend(coro cont, Caller const& caller_ex, std::stop_token token)
134  
    coro await_suspend(coro cont, Caller const& caller_ex, std::stop_token token)
135  
    {
135  
    {
136  
        auto h = inner_.handle();
136  
        auto h = inner_.handle();
137  
        auto& p = h.promise();
137  
        auto& p = h.promise();
138  
        p.set_executor(ex_);
138  
        p.set_executor(ex_);
139  
        p.set_continuation(cont, caller_ex);
139  
        p.set_continuation(cont, caller_ex);
140  

140  

141  
        if constexpr (InheritStopToken)
141  
        if constexpr (InheritStopToken)
142  
            p.set_stop_token(token);
142  
            p.set_stop_token(token);
143  
        else
143  
        else
144  
            p.set_stop_token(st_);
144  
            p.set_stop_token(st_);
145  

145  

146  
        // Refresh TLS pointer to this awaitable's resource. The wrapper's
146  
        // Refresh TLS pointer to this awaitable's resource. The wrapper's
147  
        // resource may be destroyed, but allocator copies are equivalent
147  
        // resource may be destroyed, but allocator copies are equivalent
148  
        // for deallocation. This awaitable lives until co_await completes.
148  
        // for deallocation. This awaitable lives until co_await completes.
149  
        if constexpr (!std::is_void_v<Alloc>)
149  
        if constexpr (!std::is_void_v<Alloc>)
150  
            p.set_frame_allocator(resource_.get());
150  
            p.set_frame_allocator(resource_.get());
151  

151  

152  
        return h;
152  
        return h;
153  
    }
153  
    }
154  

154  

155  
    // Non-copyable
155  
    // Non-copyable
156  
    run_awaitable_ex(run_awaitable_ex const&) = delete;
156  
    run_awaitable_ex(run_awaitable_ex const&) = delete;
157  
    run_awaitable_ex& operator=(run_awaitable_ex const&) = delete;
157  
    run_awaitable_ex& operator=(run_awaitable_ex const&) = delete;
158  

158  

159  
    // Movable (no noexcept - Task may throw)
159  
    // Movable (no noexcept - Task may throw)
160  
    run_awaitable_ex(run_awaitable_ex&&) = default;
160  
    run_awaitable_ex(run_awaitable_ex&&) = default;
161  
    run_awaitable_ex& operator=(run_awaitable_ex&&) = default;
161  
    run_awaitable_ex& operator=(run_awaitable_ex&&) = default;
162  
};
162  
};
163  

163  

164  
//----------------------------------------------------------
164  
//----------------------------------------------------------
165  
//
165  
//
166  
// run_awaitable - no executor (inherits caller's executor)
166  
// run_awaitable - no executor (inherits caller's executor)
167  
//
167  
//
168  
//----------------------------------------------------------
168  
//----------------------------------------------------------
169  

169  

170  
/** Awaitable that runs a task with optional stop_token override.
170  
/** Awaitable that runs a task with optional stop_token override.
171  

171  

172  
    Does NOT store an executor - the task inherits the caller's executor
172  
    Does NOT store an executor - the task inherits the caller's executor
173  
    directly. Since executor_ == caller_ex_, complete() does direct
173  
    directly. Since executor_ == caller_ex_, complete() does direct
174  
    symmetric transfer without dispatch overhead.
174  
    symmetric transfer without dispatch overhead.
175  

175  

176  
    @tparam Task The IoLaunchableTask type
176  
    @tparam Task The IoLaunchableTask type
177  
    @tparam InheritStopToken If true, inherit caller's stop token
177  
    @tparam InheritStopToken If true, inherit caller's stop token
178  
    @tparam Alloc The allocator type (void for no allocator)
178  
    @tparam Alloc The allocator type (void for no allocator)
179  
*/
179  
*/
180  
template<IoLaunchableTask Task, bool InheritStopToken, class Alloc = void>
180  
template<IoLaunchableTask Task, bool InheritStopToken, class Alloc = void>
181  
struct [[nodiscard]] run_awaitable
181  
struct [[nodiscard]] run_awaitable
182  
{
182  
{
183  
    frame_memory_resource<Alloc> resource_;
183  
    frame_memory_resource<Alloc> resource_;
184  
    Task inner_;
184  
    Task inner_;
185  
    std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_;
185  
    std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_;
186  

186  

187  
    // void allocator, inherit stop token
187  
    // void allocator, inherit stop token
188  
    explicit run_awaitable(Task inner)
188  
    explicit run_awaitable(Task inner)
189  
        requires (InheritStopToken && std::is_void_v<Alloc>)
189  
        requires (InheritStopToken && std::is_void_v<Alloc>)
190  
        : inner_(std::move(inner))
190  
        : inner_(std::move(inner))
191  
    {
191  
    {
192  
    }
192  
    }
193  

193  

194  
    // void allocator, explicit stop token
194  
    // void allocator, explicit stop token
195  
    run_awaitable(Task inner, std::stop_token st)
195  
    run_awaitable(Task inner, std::stop_token st)
196  
        requires (!InheritStopToken && std::is_void_v<Alloc>)
196  
        requires (!InheritStopToken && std::is_void_v<Alloc>)
197  
        : inner_(std::move(inner))
197  
        : inner_(std::move(inner))
198  
        , st_(std::move(st))
198  
        , st_(std::move(st))
199  
    {
199  
    {
200  
    }
200  
    }
201  

201  

202  
    // with allocator, inherit stop token (use template to avoid void parameter)
202  
    // with allocator, inherit stop token (use template to avoid void parameter)
203  
    template<class A>
203  
    template<class A>
204  
        requires (InheritStopToken && !std::is_void_v<Alloc> && std::same_as<A, Alloc>)
204  
        requires (InheritStopToken && !std::is_void_v<Alloc> && std::same_as<A, Alloc>)
205  
    run_awaitable(A alloc, Task inner)
205  
    run_awaitable(A alloc, Task inner)
206  
        : resource_(std::move(alloc))
206  
        : resource_(std::move(alloc))
207  
        , inner_(std::move(inner))
207  
        , inner_(std::move(inner))
208  
    {
208  
    {
209  
    }
209  
    }
210  

210  

211  
    // with allocator, explicit stop token (use template to avoid void parameter)
211  
    // with allocator, explicit stop token (use template to avoid void parameter)
212  
    template<class A>
212  
    template<class A>
213  
        requires (!InheritStopToken && !std::is_void_v<Alloc> && std::same_as<A, Alloc>)
213  
        requires (!InheritStopToken && !std::is_void_v<Alloc> && std::same_as<A, Alloc>)
214  
    run_awaitable(A alloc, Task inner, std::stop_token st)
214  
    run_awaitable(A alloc, Task inner, std::stop_token st)
215  
        : resource_(std::move(alloc))
215  
        : resource_(std::move(alloc))
216  
        , inner_(std::move(inner))
216  
        , inner_(std::move(inner))
217  
        , st_(std::move(st))
217  
        , st_(std::move(st))
218  
    {
218  
    {
219  
    }
219  
    }
220  

220  

221  
    bool await_ready() const noexcept
221  
    bool await_ready() const noexcept
222  
    {
222  
    {
223  
        return inner_.await_ready();
223  
        return inner_.await_ready();
224  
    }
224  
    }
225  

225  

226  
    decltype(auto) await_resume()
226  
    decltype(auto) await_resume()
227  
    {
227  
    {
228  
        return inner_.await_resume();
228  
        return inner_.await_resume();
229  
    }
229  
    }
230  

230  

231  
    template<typename Caller>
231  
    template<typename Caller>
232  
    coro await_suspend(coro cont, Caller const& caller_ex, std::stop_token token)
232  
    coro await_suspend(coro cont, Caller const& caller_ex, std::stop_token token)
233  
    {
233  
    {
234  
        auto h = inner_.handle();
234  
        auto h = inner_.handle();
235  
        auto& p = h.promise();
235  
        auto& p = h.promise();
236  
        p.set_executor(caller_ex);
236  
        p.set_executor(caller_ex);
237  
        p.set_continuation(cont, caller_ex);
237  
        p.set_continuation(cont, caller_ex);
238  

238  

239  
        if constexpr (InheritStopToken)
239  
        if constexpr (InheritStopToken)
240  
            p.set_stop_token(token);
240  
            p.set_stop_token(token);
241  
        else
241  
        else
242  
            p.set_stop_token(st_);
242  
            p.set_stop_token(st_);
243  

243  

244  
        // Refresh TLS pointer to this awaitable's resource. The wrapper's
244  
        // Refresh TLS pointer to this awaitable's resource. The wrapper's
245  
        // resource may be destroyed, but allocator copies are equivalent
245  
        // resource may be destroyed, but allocator copies are equivalent
246  
        // for deallocation. This awaitable lives until co_await completes.
246  
        // for deallocation. This awaitable lives until co_await completes.
247  
        if constexpr (!std::is_void_v<Alloc>)
247  
        if constexpr (!std::is_void_v<Alloc>)
248  
            p.set_frame_allocator(resource_.get());
248  
            p.set_frame_allocator(resource_.get());
249  

249  

250  
        return h;
250  
        return h;
251  
    }
251  
    }
252  

252  

253  
    // Non-copyable
253  
    // Non-copyable
254  
    run_awaitable(run_awaitable const&) = delete;
254  
    run_awaitable(run_awaitable const&) = delete;
255  
    run_awaitable& operator=(run_awaitable const&) = delete;
255  
    run_awaitable& operator=(run_awaitable const&) = delete;
256  

256  

257  
    // Movable (no noexcept - Task may throw)
257  
    // Movable (no noexcept - Task may throw)
258  
    run_awaitable(run_awaitable&&) = default;
258  
    run_awaitable(run_awaitable&&) = default;
259  
    run_awaitable& operator=(run_awaitable&&) = default;
259  
    run_awaitable& operator=(run_awaitable&&) = default;
260  
};
260  
};
261  

261  

262  
//----------------------------------------------------------
262  
//----------------------------------------------------------
263  
//
263  
//
264  
// run_wrapper_ex - with executor
264  
// run_wrapper_ex - with executor
265  
//
265  
//
266  
//----------------------------------------------------------
266  
//----------------------------------------------------------
267  

267  

268  
/** Wrapper returned by run(ex, ...) that accepts a task for execution.
268  
/** Wrapper returned by run(ex, ...) that accepts a task for execution.
269  

269  

270  
    @tparam Ex The executor type.
270  
    @tparam Ex The executor type.
271  
    @tparam InheritStopToken If true, inherit caller's stop token.
271  
    @tparam InheritStopToken If true, inherit caller's stop token.
272  
    @tparam Alloc The allocator type (void for no allocator).
272  
    @tparam Alloc The allocator type (void for no allocator).
273  
*/
273  
*/
274  
template<Executor Ex, bool InheritStopToken, class Alloc>
274  
template<Executor Ex, bool InheritStopToken, class Alloc>
275  
class [[nodiscard]] run_wrapper_ex
275  
class [[nodiscard]] run_wrapper_ex
276  
{
276  
{
277  
    Ex ex_;
277  
    Ex ex_;
278  
    std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_;
278  
    std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_;
279  
    frame_memory_resource<Alloc> resource_;
279  
    frame_memory_resource<Alloc> resource_;
280  
    Alloc alloc_;  // Copy to pass to awaitable
280  
    Alloc alloc_;  // Copy to pass to awaitable
281  

281  

282  
public:
282  
public:
283  
    run_wrapper_ex(Ex ex, Alloc alloc)
283  
    run_wrapper_ex(Ex ex, Alloc alloc)
284  
        requires InheritStopToken
284  
        requires InheritStopToken
285  
        : ex_(std::move(ex))
285  
        : ex_(std::move(ex))
286  
        , resource_(alloc)
286  
        , resource_(alloc)
287  
        , alloc_(std::move(alloc))
287  
        , alloc_(std::move(alloc))
288  
    {
288  
    {
289  
        current_frame_allocator() = &resource_;
289  
        current_frame_allocator() = &resource_;
290  
    }
290  
    }
291  

291  

292  
    run_wrapper_ex(Ex ex, std::stop_token st, Alloc alloc)
292  
    run_wrapper_ex(Ex ex, std::stop_token st, Alloc alloc)
293  
        requires (!InheritStopToken)
293  
        requires (!InheritStopToken)
294  
        : ex_(std::move(ex))
294  
        : ex_(std::move(ex))
295  
        , st_(std::move(st))
295  
        , st_(std::move(st))
296  
        , resource_(alloc)
296  
        , resource_(alloc)
297  
        , alloc_(std::move(alloc))
297  
        , alloc_(std::move(alloc))
298  
    {
298  
    {
299  
        current_frame_allocator() = &resource_;
299  
        current_frame_allocator() = &resource_;
300  
    }
300  
    }
301  

301  

302  
    // Non-copyable, non-movable (must be used immediately)
302  
    // Non-copyable, non-movable (must be used immediately)
303  
    run_wrapper_ex(run_wrapper_ex const&) = delete;
303  
    run_wrapper_ex(run_wrapper_ex const&) = delete;
304  
    run_wrapper_ex(run_wrapper_ex&&) = delete;
304  
    run_wrapper_ex(run_wrapper_ex&&) = delete;
305  
    run_wrapper_ex& operator=(run_wrapper_ex const&) = delete;
305  
    run_wrapper_ex& operator=(run_wrapper_ex const&) = delete;
306  
    run_wrapper_ex& operator=(run_wrapper_ex&&) = delete;
306  
    run_wrapper_ex& operator=(run_wrapper_ex&&) = delete;
307  

307  

308  
    template<IoLaunchableTask Task>
308  
    template<IoLaunchableTask Task>
309  
    [[nodiscard]] auto operator()(Task t) &&
309  
    [[nodiscard]] auto operator()(Task t) &&
310  
    {
310  
    {
311  
        if constexpr (InheritStopToken)
311  
        if constexpr (InheritStopToken)
312  
            return run_awaitable_ex<Task, Ex, true, Alloc>{
312  
            return run_awaitable_ex<Task, Ex, true, Alloc>{
313  
                std::move(ex_), std::move(alloc_), std::move(t)};
313  
                std::move(ex_), std::move(alloc_), std::move(t)};
314  
        else
314  
        else
315  
            return run_awaitable_ex<Task, Ex, false, Alloc>{
315  
            return run_awaitable_ex<Task, Ex, false, Alloc>{
316  
                std::move(ex_), std::move(alloc_), std::move(t), std::move(st_)};
316  
                std::move(ex_), std::move(alloc_), std::move(t), std::move(st_)};
317  
    }
317  
    }
318  
};
318  
};
319  

319  

320  
/// Specialization for memory_resource* - stores pointer directly.
320  
/// Specialization for memory_resource* - stores pointer directly.
321  
template<Executor Ex, bool InheritStopToken>
321  
template<Executor Ex, bool InheritStopToken>
322  
class [[nodiscard]] run_wrapper_ex<Ex, InheritStopToken, std::pmr::memory_resource*>
322  
class [[nodiscard]] run_wrapper_ex<Ex, InheritStopToken, std::pmr::memory_resource*>
323  
{
323  
{
324  
    Ex ex_;
324  
    Ex ex_;
325  
    std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_;
325  
    std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_;
326  
    std::pmr::memory_resource* mr_;
326  
    std::pmr::memory_resource* mr_;
327  

327  

328  
public:
328  
public:
329  
    run_wrapper_ex(Ex ex, std::pmr::memory_resource* mr)
329  
    run_wrapper_ex(Ex ex, std::pmr::memory_resource* mr)
330  
        requires InheritStopToken
330  
        requires InheritStopToken
331  
        : ex_(std::move(ex))
331  
        : ex_(std::move(ex))
332  
        , mr_(mr)
332  
        , mr_(mr)
333  
    {
333  
    {
334  
        current_frame_allocator() = mr_;
334  
        current_frame_allocator() = mr_;
335  
    }
335  
    }
336  

336  

337  
    run_wrapper_ex(Ex ex, std::stop_token st, std::pmr::memory_resource* mr)
337  
    run_wrapper_ex(Ex ex, std::stop_token st, std::pmr::memory_resource* mr)
338  
        requires (!InheritStopToken)
338  
        requires (!InheritStopToken)
339  
        : ex_(std::move(ex))
339  
        : ex_(std::move(ex))
340  
        , st_(std::move(st))
340  
        , st_(std::move(st))
341  
        , mr_(mr)
341  
        , mr_(mr)
342  
    {
342  
    {
343  
        current_frame_allocator() = mr_;
343  
        current_frame_allocator() = mr_;
344  
    }
344  
    }
345  

345  

346  
    // Non-copyable, non-movable (must be used immediately)
346  
    // Non-copyable, non-movable (must be used immediately)
347  
    run_wrapper_ex(run_wrapper_ex const&) = delete;
347  
    run_wrapper_ex(run_wrapper_ex const&) = delete;
348  
    run_wrapper_ex(run_wrapper_ex&&) = delete;
348  
    run_wrapper_ex(run_wrapper_ex&&) = delete;
349  
    run_wrapper_ex& operator=(run_wrapper_ex const&) = delete;
349  
    run_wrapper_ex& operator=(run_wrapper_ex const&) = delete;
350  
    run_wrapper_ex& operator=(run_wrapper_ex&&) = delete;
350  
    run_wrapper_ex& operator=(run_wrapper_ex&&) = delete;
351  

351  

352  
    template<IoLaunchableTask Task>
352  
    template<IoLaunchableTask Task>
353  
    [[nodiscard]] auto operator()(Task t) &&
353  
    [[nodiscard]] auto operator()(Task t) &&
354  
    {
354  
    {
355  
        if constexpr (InheritStopToken)
355  
        if constexpr (InheritStopToken)
356  
            return run_awaitable_ex<Task, Ex, true, std::pmr::memory_resource*>{
356  
            return run_awaitable_ex<Task, Ex, true, std::pmr::memory_resource*>{
357  
                std::move(ex_), mr_, std::move(t)};
357  
                std::move(ex_), mr_, std::move(t)};
358  
        else
358  
        else
359  
            return run_awaitable_ex<Task, Ex, false, std::pmr::memory_resource*>{
359  
            return run_awaitable_ex<Task, Ex, false, std::pmr::memory_resource*>{
360  
                std::move(ex_), mr_, std::move(t), std::move(st_)};
360  
                std::move(ex_), mr_, std::move(t), std::move(st_)};
361  
    }
361  
    }
362  
};
362  
};
363  

363  

364  
/// Specialization for no allocator (void).
364  
/// Specialization for no allocator (void).
365  
template<Executor Ex, bool InheritStopToken>
365  
template<Executor Ex, bool InheritStopToken>
366  
class [[nodiscard]] run_wrapper_ex<Ex, InheritStopToken, void>
366  
class [[nodiscard]] run_wrapper_ex<Ex, InheritStopToken, void>
367  
{
367  
{
368  
    Ex ex_;
368  
    Ex ex_;
369  
    std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_;
369  
    std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_;
370  

370  

371  
public:
371  
public:
372  
    explicit run_wrapper_ex(Ex ex)
372  
    explicit run_wrapper_ex(Ex ex)
373  
        requires InheritStopToken
373  
        requires InheritStopToken
374  
        : ex_(std::move(ex))
374  
        : ex_(std::move(ex))
375  
    {
375  
    {
376  
    }
376  
    }
377  

377  

378  
    run_wrapper_ex(Ex ex, std::stop_token st)
378  
    run_wrapper_ex(Ex ex, std::stop_token st)
379  
        requires (!InheritStopToken)
379  
        requires (!InheritStopToken)
380  
        : ex_(std::move(ex))
380  
        : ex_(std::move(ex))
381  
        , st_(std::move(st))
381  
        , st_(std::move(st))
382  
    {
382  
    {
383  
    }
383  
    }
384  

384  

385  
    // Non-copyable, non-movable (must be used immediately)
385  
    // Non-copyable, non-movable (must be used immediately)
386  
    run_wrapper_ex(run_wrapper_ex const&) = delete;
386  
    run_wrapper_ex(run_wrapper_ex const&) = delete;
387  
    run_wrapper_ex(run_wrapper_ex&&) = delete;
387  
    run_wrapper_ex(run_wrapper_ex&&) = delete;
388  
    run_wrapper_ex& operator=(run_wrapper_ex const&) = delete;
388  
    run_wrapper_ex& operator=(run_wrapper_ex const&) = delete;
389  
    run_wrapper_ex& operator=(run_wrapper_ex&&) = delete;
389  
    run_wrapper_ex& operator=(run_wrapper_ex&&) = delete;
390  

390  

391  
    template<IoLaunchableTask Task>
391  
    template<IoLaunchableTask Task>
392  
    [[nodiscard]] auto operator()(Task t) &&
392  
    [[nodiscard]] auto operator()(Task t) &&
393  
    {
393  
    {
394  
        if constexpr (InheritStopToken)
394  
        if constexpr (InheritStopToken)
395  
            return run_awaitable_ex<Task, Ex, true>{
395  
            return run_awaitable_ex<Task, Ex, true>{
396  
                std::move(ex_), std::move(t)};
396  
                std::move(ex_), std::move(t)};
397  
        else
397  
        else
398  
            return run_awaitable_ex<Task, Ex, false>{
398  
            return run_awaitable_ex<Task, Ex, false>{
399  
                std::move(ex_), std::move(t), std::move(st_)};
399  
                std::move(ex_), std::move(t), std::move(st_)};
400  
    }
400  
    }
401  
};
401  
};
402  

402  

403  
//----------------------------------------------------------
403  
//----------------------------------------------------------
404  
//
404  
//
405  
// run_wrapper - no executor (inherits caller's executor)
405  
// run_wrapper - no executor (inherits caller's executor)
406  
//
406  
//
407  
//----------------------------------------------------------
407  
//----------------------------------------------------------
408  

408  

409  
/** Wrapper returned by run(st) or run(alloc) that accepts a task.
409  
/** Wrapper returned by run(st) or run(alloc) that accepts a task.
410  

410  

411  
    @tparam InheritStopToken If true, inherit caller's stop token.
411  
    @tparam InheritStopToken If true, inherit caller's stop token.
412  
    @tparam Alloc The allocator type (void for no allocator).
412  
    @tparam Alloc The allocator type (void for no allocator).
413  
*/
413  
*/
414  
template<bool InheritStopToken, class Alloc>
414  
template<bool InheritStopToken, class Alloc>
415  
class [[nodiscard]] run_wrapper
415  
class [[nodiscard]] run_wrapper
416  
{
416  
{
417  
    std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_;
417  
    std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_;
418  
    frame_memory_resource<Alloc> resource_;
418  
    frame_memory_resource<Alloc> resource_;
419  
    Alloc alloc_;  // Copy to pass to awaitable
419  
    Alloc alloc_;  // Copy to pass to awaitable
420  

420  

421  
public:
421  
public:
422  
    explicit run_wrapper(Alloc alloc)
422  
    explicit run_wrapper(Alloc alloc)
423  
        requires InheritStopToken
423  
        requires InheritStopToken
424  
        : resource_(alloc)
424  
        : resource_(alloc)
425  
        , alloc_(std::move(alloc))
425  
        , alloc_(std::move(alloc))
426  
    {
426  
    {
427  
        current_frame_allocator() = &resource_;
427  
        current_frame_allocator() = &resource_;
428  
    }
428  
    }
429  

429  

430  
    run_wrapper(std::stop_token st, Alloc alloc)
430  
    run_wrapper(std::stop_token st, Alloc alloc)
431  
        requires (!InheritStopToken)
431  
        requires (!InheritStopToken)
432  
        : st_(std::move(st))
432  
        : st_(std::move(st))
433  
        , resource_(alloc)
433  
        , resource_(alloc)
434  
        , alloc_(std::move(alloc))
434  
        , alloc_(std::move(alloc))
435  
    {
435  
    {
436  
        current_frame_allocator() = &resource_;
436  
        current_frame_allocator() = &resource_;
437  
    }
437  
    }
438  

438  

439  
    // Non-copyable, non-movable (must be used immediately)
439  
    // Non-copyable, non-movable (must be used immediately)
440  
    run_wrapper(run_wrapper const&) = delete;
440  
    run_wrapper(run_wrapper const&) = delete;
441  
    run_wrapper(run_wrapper&&) = delete;
441  
    run_wrapper(run_wrapper&&) = delete;
442  
    run_wrapper& operator=(run_wrapper const&) = delete;
442  
    run_wrapper& operator=(run_wrapper const&) = delete;
443  
    run_wrapper& operator=(run_wrapper&&) = delete;
443  
    run_wrapper& operator=(run_wrapper&&) = delete;
444  

444  

445  
    template<IoLaunchableTask Task>
445  
    template<IoLaunchableTask Task>
446  
    [[nodiscard]] auto operator()(Task t) &&
446  
    [[nodiscard]] auto operator()(Task t) &&
447  
    {
447  
    {
448  
        if constexpr (InheritStopToken)
448  
        if constexpr (InheritStopToken)
449  
            return run_awaitable<Task, true, Alloc>{
449  
            return run_awaitable<Task, true, Alloc>{
450  
                std::move(alloc_), std::move(t)};
450  
                std::move(alloc_), std::move(t)};
451  
        else
451  
        else
452  
            return run_awaitable<Task, false, Alloc>{
452  
            return run_awaitable<Task, false, Alloc>{
453  
                std::move(alloc_), std::move(t), std::move(st_)};
453  
                std::move(alloc_), std::move(t), std::move(st_)};
454  
    }
454  
    }
455  
};
455  
};
456  

456  

457  
/// Specialization for memory_resource* - stores pointer directly.
457  
/// Specialization for memory_resource* - stores pointer directly.
458  
template<bool InheritStopToken>
458  
template<bool InheritStopToken>
459  
class [[nodiscard]] run_wrapper<InheritStopToken, std::pmr::memory_resource*>
459  
class [[nodiscard]] run_wrapper<InheritStopToken, std::pmr::memory_resource*>
460  
{
460  
{
461  
    std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_;
461  
    std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_;
462  
    std::pmr::memory_resource* mr_;
462  
    std::pmr::memory_resource* mr_;
463  

463  

464  
public:
464  
public:
465  
    explicit run_wrapper(std::pmr::memory_resource* mr)
465  
    explicit run_wrapper(std::pmr::memory_resource* mr)
466  
        requires InheritStopToken
466  
        requires InheritStopToken
467  
        : mr_(mr)
467  
        : mr_(mr)
468  
    {
468  
    {
469  
        current_frame_allocator() = mr_;
469  
        current_frame_allocator() = mr_;
470  
    }
470  
    }
471  

471  

472  
    run_wrapper(std::stop_token st, std::pmr::memory_resource* mr)
472  
    run_wrapper(std::stop_token st, std::pmr::memory_resource* mr)
473  
        requires (!InheritStopToken)
473  
        requires (!InheritStopToken)
474  
        : st_(std::move(st))
474  
        : st_(std::move(st))
475  
        , mr_(mr)
475  
        , mr_(mr)
476  
    {
476  
    {
477  
        current_frame_allocator() = mr_;
477  
        current_frame_allocator() = mr_;
478  
    }
478  
    }
479  

479  

480  
    // Non-copyable, non-movable (must be used immediately)
480  
    // Non-copyable, non-movable (must be used immediately)
481  
    run_wrapper(run_wrapper const&) = delete;
481  
    run_wrapper(run_wrapper const&) = delete;
482  
    run_wrapper(run_wrapper&&) = delete;
482  
    run_wrapper(run_wrapper&&) = delete;
483  
    run_wrapper& operator=(run_wrapper const&) = delete;
483  
    run_wrapper& operator=(run_wrapper const&) = delete;
484  
    run_wrapper& operator=(run_wrapper&&) = delete;
484  
    run_wrapper& operator=(run_wrapper&&) = delete;
485  

485  

486  
    template<IoLaunchableTask Task>
486  
    template<IoLaunchableTask Task>
487  
    [[nodiscard]] auto operator()(Task t) &&
487  
    [[nodiscard]] auto operator()(Task t) &&
488  
    {
488  
    {
489  
        if constexpr (InheritStopToken)
489  
        if constexpr (InheritStopToken)
490  
            return run_awaitable<Task, true, std::pmr::memory_resource*>{
490  
            return run_awaitable<Task, true, std::pmr::memory_resource*>{
491  
                mr_, std::move(t)};
491  
                mr_, std::move(t)};
492  
        else
492  
        else
493  
            return run_awaitable<Task, false, std::pmr::memory_resource*>{
493  
            return run_awaitable<Task, false, std::pmr::memory_resource*>{
494  
                mr_, std::move(t), std::move(st_)};
494  
                mr_, std::move(t), std::move(st_)};
495  
    }
495  
    }
496  
};
496  
};
497  

497  

498  
/// Specialization for stop_token only (no allocator).
498  
/// Specialization for stop_token only (no allocator).
499  
template<>
499  
template<>
500  
class [[nodiscard]] run_wrapper<false, void>
500  
class [[nodiscard]] run_wrapper<false, void>
501  
{
501  
{
502  
    std::stop_token st_;
502  
    std::stop_token st_;
503  

503  

504  
public:
504  
public:
505  
    explicit run_wrapper(std::stop_token st)
505  
    explicit run_wrapper(std::stop_token st)
506  
        : st_(std::move(st))
506  
        : st_(std::move(st))
507  
    {
507  
    {
508  
    }
508  
    }
509  

509  

510  
    // Non-copyable, non-movable (must be used immediately)
510  
    // Non-copyable, non-movable (must be used immediately)
511  
    run_wrapper(run_wrapper const&) = delete;
511  
    run_wrapper(run_wrapper const&) = delete;
512  
    run_wrapper(run_wrapper&&) = delete;
512  
    run_wrapper(run_wrapper&&) = delete;
513  
    run_wrapper& operator=(run_wrapper const&) = delete;
513  
    run_wrapper& operator=(run_wrapper const&) = delete;
514  
    run_wrapper& operator=(run_wrapper&&) = delete;
514  
    run_wrapper& operator=(run_wrapper&&) = delete;
515  

515  

516  
    template<IoLaunchableTask Task>
516  
    template<IoLaunchableTask Task>
517  
    [[nodiscard]] auto operator()(Task t) &&
517  
    [[nodiscard]] auto operator()(Task t) &&
518  
    {
518  
    {
519  
        return run_awaitable<Task, false, void>{std::move(t), std::move(st_)};
519  
        return run_awaitable<Task, false, void>{std::move(t), std::move(st_)};
520  
    }
520  
    }
521  
};
521  
};
522  

522  

523  
} // namespace boost::capy::detail
523  
} // namespace boost::capy::detail
524  

524  

525  
namespace boost::capy {
525  
namespace boost::capy {
526  

526  

527  
//----------------------------------------------------------
527  
//----------------------------------------------------------
528  
//
528  
//
529  
// run() overloads - with executor
529  
// run() overloads - with executor
530  
//
530  
//
531  
//----------------------------------------------------------
531  
//----------------------------------------------------------
532  

532  

533  
/** Bind a task to execute on a specific executor.
533  
/** Bind a task to execute on a specific executor.
534  

534  

535  
    Returns a wrapper that accepts a task and produces an awaitable.
535  
    Returns a wrapper that accepts a task and produces an awaitable.
536  
    When co_awaited, the task runs on the specified executor.
536  
    When co_awaited, the task runs on the specified executor.
537  

537  

538  
    @par Example
538  
    @par Example
539  
    @code
539  
    @code
540  
    co_await run(other_executor)(my_task());
540  
    co_await run(other_executor)(my_task());
541  
    @endcode
541  
    @endcode
542  

542  

543  
    @param ex The executor on which the task should run.
543  
    @param ex The executor on which the task should run.
544  

544  

545  
    @return A wrapper that accepts a task for execution.
545  
    @return A wrapper that accepts a task for execution.
546  

546  

547  
    @see task
547  
    @see task
548  
    @see executor
548  
    @see executor
549  
*/
549  
*/
550  
template<Executor Ex>
550  
template<Executor Ex>
551  
[[nodiscard]] auto
551  
[[nodiscard]] auto
552  
run(Ex ex)
552  
run(Ex ex)
553  
{
553  
{
554  
    return detail::run_wrapper_ex<Ex, true, void>{std::move(ex)};
554  
    return detail::run_wrapper_ex<Ex, true, void>{std::move(ex)};
555  
}
555  
}
556  

556  

557  
/** Bind a task to an executor with a stop token.
557  
/** Bind a task to an executor with a stop token.
558  

558  

559  
    @param ex The executor on which the task should run.
559  
    @param ex The executor on which the task should run.
560  
    @param st The stop token for cooperative cancellation.
560  
    @param st The stop token for cooperative cancellation.
561  

561  

562  
    @return A wrapper that accepts a task for execution.
562  
    @return A wrapper that accepts a task for execution.
563  
*/
563  
*/
564  
template<Executor Ex>
564  
template<Executor Ex>
565  
[[nodiscard]] auto
565  
[[nodiscard]] auto
566  
run(Ex ex, std::stop_token st)
566  
run(Ex ex, std::stop_token st)
567  
{
567  
{
568  
    return detail::run_wrapper_ex<Ex, false, void>{
568  
    return detail::run_wrapper_ex<Ex, false, void>{
569  
        std::move(ex), std::move(st)};
569  
        std::move(ex), std::move(st)};
570  
}
570  
}
571  

571  

572  
/** Bind a task to an executor with a memory resource.
572  
/** Bind a task to an executor with a memory resource.
573  

573  

574  
    @param ex The executor on which the task should run.
574  
    @param ex The executor on which the task should run.
575  
    @param mr The memory resource for frame allocation.
575  
    @param mr The memory resource for frame allocation.
576  

576  

577  
    @return A wrapper that accepts a task for execution.
577  
    @return A wrapper that accepts a task for execution.
578  
*/
578  
*/
579  
template<Executor Ex>
579  
template<Executor Ex>
580  
[[nodiscard]] auto
580  
[[nodiscard]] auto
581  
run(Ex ex, std::pmr::memory_resource* mr)
581  
run(Ex ex, std::pmr::memory_resource* mr)
582  
{
582  
{
583  
    return detail::run_wrapper_ex<Ex, true, std::pmr::memory_resource*>{
583  
    return detail::run_wrapper_ex<Ex, true, std::pmr::memory_resource*>{
584  
        std::move(ex), mr};
584  
        std::move(ex), mr};
585  
}
585  
}
586  

586  

587  
/** Bind a task to an executor with a standard allocator.
587  
/** Bind a task to an executor with a standard allocator.
588  

588  

589  
    @param ex The executor on which the task should run.
589  
    @param ex The executor on which the task should run.
590  
    @param alloc The allocator for frame allocation.
590  
    @param alloc The allocator for frame allocation.
591  

591  

592  
    @return A wrapper that accepts a task for execution.
592  
    @return A wrapper that accepts a task for execution.
593  
*/
593  
*/
594  
template<Executor Ex, detail::Allocator Alloc>
594  
template<Executor Ex, detail::Allocator Alloc>
595  
[[nodiscard]] auto
595  
[[nodiscard]] auto
596  
run(Ex ex, Alloc alloc)
596  
run(Ex ex, Alloc alloc)
597  
{
597  
{
598  
    return detail::run_wrapper_ex<Ex, true, Alloc>{
598  
    return detail::run_wrapper_ex<Ex, true, Alloc>{
599  
        std::move(ex), std::move(alloc)};
599  
        std::move(ex), std::move(alloc)};
600  
}
600  
}
601  

601  

602  
/** Bind a task to an executor with stop token and memory resource.
602  
/** Bind a task to an executor with stop token and memory resource.
603  

603  

604  
    @param ex The executor on which the task should run.
604  
    @param ex The executor on which the task should run.
605  
    @param st The stop token for cooperative cancellation.
605  
    @param st The stop token for cooperative cancellation.
606  
    @param mr The memory resource for frame allocation.
606  
    @param mr The memory resource for frame allocation.
607  

607  

608  
    @return A wrapper that accepts a task for execution.
608  
    @return A wrapper that accepts a task for execution.
609  
*/
609  
*/
610  
template<Executor Ex>
610  
template<Executor Ex>
611  
[[nodiscard]] auto
611  
[[nodiscard]] auto
612  
run(Ex ex, std::stop_token st, std::pmr::memory_resource* mr)
612  
run(Ex ex, std::stop_token st, std::pmr::memory_resource* mr)
613  
{
613  
{
614  
    return detail::run_wrapper_ex<Ex, false, std::pmr::memory_resource*>{
614  
    return detail::run_wrapper_ex<Ex, false, std::pmr::memory_resource*>{
615  
        std::move(ex), std::move(st), mr};
615  
        std::move(ex), std::move(st), mr};
616  
}
616  
}
617  

617  

618  
/** Bind a task to an executor with stop token and standard allocator.
618  
/** Bind a task to an executor with stop token and standard allocator.
619  

619  

620  
    @param ex The executor on which the task should run.
620  
    @param ex The executor on which the task should run.
621  
    @param st The stop token for cooperative cancellation.
621  
    @param st The stop token for cooperative cancellation.
622  
    @param alloc The allocator for frame allocation.
622  
    @param alloc The allocator for frame allocation.
623  

623  

624  
    @return A wrapper that accepts a task for execution.
624  
    @return A wrapper that accepts a task for execution.
625  
*/
625  
*/
626  
template<Executor Ex, detail::Allocator Alloc>
626  
template<Executor Ex, detail::Allocator Alloc>
627  
[[nodiscard]] auto
627  
[[nodiscard]] auto
628  
run(Ex ex, std::stop_token st, Alloc alloc)
628  
run(Ex ex, std::stop_token st, Alloc alloc)
629  
{
629  
{
630  
    return detail::run_wrapper_ex<Ex, false, Alloc>{
630  
    return detail::run_wrapper_ex<Ex, false, Alloc>{
631  
        std::move(ex), std::move(st), std::move(alloc)};
631  
        std::move(ex), std::move(st), std::move(alloc)};
632  
}
632  
}
633  

633  

634  
//----------------------------------------------------------
634  
//----------------------------------------------------------
635  
//
635  
//
636  
// run() overloads - no executor (inherits caller's)
636  
// run() overloads - no executor (inherits caller's)
637  
//
637  
//
638  
//----------------------------------------------------------
638  
//----------------------------------------------------------
639  

639  

640  
/** Run a task with a custom stop token.
640  
/** Run a task with a custom stop token.
641  

641  

642  
    The task inherits the caller's executor. Only the stop token
642  
    The task inherits the caller's executor. Only the stop token
643  
    is overridden.
643  
    is overridden.
644  

644  

645  
    @par Example
645  
    @par Example
646  
    @code
646  
    @code
647  
    std::stop_source source;
647  
    std::stop_source source;
648  
    co_await run(source.get_token())(cancellable_task());
648  
    co_await run(source.get_token())(cancellable_task());
649  
    @endcode
649  
    @endcode
650  

650  

651  
    @param st The stop token for cooperative cancellation.
651  
    @param st The stop token for cooperative cancellation.
652  

652  

653  
    @return A wrapper that accepts a task for execution.
653  
    @return A wrapper that accepts a task for execution.
654  
*/
654  
*/
655  
[[nodiscard]] inline auto
655  
[[nodiscard]] inline auto
656  
run(std::stop_token st)
656  
run(std::stop_token st)
657  
{
657  
{
658  
    return detail::run_wrapper<false, void>{std::move(st)};
658  
    return detail::run_wrapper<false, void>{std::move(st)};
659  
}
659  
}
660  

660  

661  
/** Run a task with a custom memory resource.
661  
/** Run a task with a custom memory resource.
662  

662  

663  
    The task inherits the caller's executor. The memory resource
663  
    The task inherits the caller's executor. The memory resource
664  
    is used for nested frame allocations.
664  
    is used for nested frame allocations.
665  

665  

666  
    @param mr The memory resource for frame allocation.
666  
    @param mr The memory resource for frame allocation.
667  

667  

668  
    @return A wrapper that accepts a task for execution.
668  
    @return A wrapper that accepts a task for execution.
669  
*/
669  
*/
670  
[[nodiscard]] inline auto
670  
[[nodiscard]] inline auto
671  
run(std::pmr::memory_resource* mr)
671  
run(std::pmr::memory_resource* mr)
672  
{
672  
{
673  
    return detail::run_wrapper<true, std::pmr::memory_resource*>{mr};
673  
    return detail::run_wrapper<true, std::pmr::memory_resource*>{mr};
674  
}
674  
}
675  

675  

676  
/** Run a task with a custom standard allocator.
676  
/** Run a task with a custom standard allocator.
677  

677  

678  
    The task inherits the caller's executor. The allocator is used
678  
    The task inherits the caller's executor. The allocator is used
679  
    for nested frame allocations.
679  
    for nested frame allocations.
680  

680  

681  
    @param alloc The allocator for frame allocation.
681  
    @param alloc The allocator for frame allocation.
682  

682  

683  
    @return A wrapper that accepts a task for execution.
683  
    @return A wrapper that accepts a task for execution.
684  
*/
684  
*/
685  
template<detail::Allocator Alloc>
685  
template<detail::Allocator Alloc>
686  
[[nodiscard]] auto
686  
[[nodiscard]] auto
687  
run(Alloc alloc)
687  
run(Alloc alloc)
688  
{
688  
{
689  
    return detail::run_wrapper<true, Alloc>{std::move(alloc)};
689  
    return detail::run_wrapper<true, Alloc>{std::move(alloc)};
690  
}
690  
}
691  

691  

692  
/** Run a task with stop token and memory resource.
692  
/** Run a task with stop token and memory resource.
693  

693  

694  
    The task inherits the caller's executor.
694  
    The task inherits the caller's executor.
695  

695  

696  
    @param st The stop token for cooperative cancellation.
696  
    @param st The stop token for cooperative cancellation.
697  
    @param mr The memory resource for frame allocation.
697  
    @param mr The memory resource for frame allocation.
698  

698  

699  
    @return A wrapper that accepts a task for execution.
699  
    @return A wrapper that accepts a task for execution.
700  
*/
700  
*/
701  
[[nodiscard]] inline auto
701  
[[nodiscard]] inline auto
702  
run(std::stop_token st, std::pmr::memory_resource* mr)
702  
run(std::stop_token st, std::pmr::memory_resource* mr)
703  
{
703  
{
704  
    return detail::run_wrapper<false, std::pmr::memory_resource*>{
704  
    return detail::run_wrapper<false, std::pmr::memory_resource*>{
705  
        std::move(st), mr};
705  
        std::move(st), mr};
706  
}
706  
}
707  

707  

708  
/** Run a task with stop token and standard allocator.
708  
/** Run a task with stop token and standard allocator.
709  

709  

710  
    The task inherits the caller's executor.
710  
    The task inherits the caller's executor.
711  

711  

712  
    @param st The stop token for cooperative cancellation.
712  
    @param st The stop token for cooperative cancellation.
713  
    @param alloc The allocator for frame allocation.
713  
    @param alloc The allocator for frame allocation.
714  

714  

715  
    @return A wrapper that accepts a task for execution.
715  
    @return A wrapper that accepts a task for execution.
716  
*/
716  
*/
717  
template<detail::Allocator Alloc>
717  
template<detail::Allocator Alloc>
718  
[[nodiscard]] auto
718  
[[nodiscard]] auto
719  
run(std::stop_token st, Alloc alloc)
719  
run(std::stop_token st, Alloc alloc)
720  
{
720  
{
721  
    return detail::run_wrapper<false, Alloc>{
721  
    return detail::run_wrapper<false, Alloc>{
722  
        std::move(st), std::move(alloc)};
722  
        std::move(st), std::move(alloc)};
723  
}
723  
}
724  

724  

725  
} // namespace boost::capy
725  
} // namespace boost::capy
726  

726  

727  
#endif
727  
#endif