Line data Source code
1 : //
2 : // Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
3 : //
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)
6 : //
7 : // Official repository: https://github.com/cppalliance/capy
8 : //
9 :
10 : #ifndef BOOST_CAPY_RUN_HPP
11 : #define BOOST_CAPY_RUN_HPP
12 :
13 : #include <boost/capy/detail/config.hpp>
14 : #include <boost/capy/detail/run.hpp>
15 : #include <boost/capy/concept/executor.hpp>
16 : #include <boost/capy/concept/io_launchable_task.hpp>
17 : #include <boost/capy/coro.hpp>
18 : #include <boost/capy/ex/executor_ref.hpp>
19 : #include <boost/capy/ex/frame_allocator.hpp>
20 :
21 : #include <memory_resource>
22 : #include <stop_token>
23 : #include <type_traits>
24 : #include <utility>
25 : #include <variant>
26 :
27 : /*
28 : Allocator Lifetime Strategy
29 : ===========================
30 :
31 : When using run() with a custom allocator:
32 :
33 : co_await run(ex, alloc)(my_task());
34 :
35 : The evaluation order is:
36 : 1. run(ex, alloc) creates a temporary wrapper
37 : 2. my_task() allocates its coroutine frame using TLS
38 : 3. operator() returns an awaitable
39 : 4. Wrapper temporary is DESTROYED
40 : 5. co_await suspends caller, resumes task
41 : 6. Task body executes (wrapper is already dead!)
42 :
43 : Problem: The wrapper's frame_memory_resource dies before the task
44 : body runs. When initial_suspend::await_resume() restores TLS from
45 : the saved pointer, it would point to dead memory.
46 :
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
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.
51 :
52 : This works because standard allocator copies are equivalent - memory
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
55 : task creation uses TLS pointing to the awaitable's resource (also safe).
56 : */
57 :
58 : namespace boost::capy::detail {
59 :
60 : //----------------------------------------------------------
61 : //
62 : // run_awaitable_ex - with executor (executor switch)
63 : //
64 : //----------------------------------------------------------
65 :
66 : /** Awaitable that binds an IoLaunchableTask to a specific executor.
67 :
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
70 : duration of the operation.
71 :
72 : @tparam Task The IoLaunchableTask type
73 : @tparam Ex The executor type
74 : @tparam InheritStopToken If true, inherit caller's stop token
75 : @tparam Alloc The allocator type (void for no allocator)
76 : */
77 : template<IoLaunchableTask Task, Executor Ex, bool InheritStopToken, class Alloc = void>
78 : struct [[nodiscard]] run_awaitable_ex
79 : {
80 : Ex ex_;
81 : frame_memory_resource<Alloc> resource_;
82 : Task inner_;
83 : std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_;
84 :
85 : // void allocator, inherit stop token
86 2 : run_awaitable_ex(Ex ex, Task inner)
87 : requires (InheritStopToken && std::is_void_v<Alloc>)
88 2 : : ex_(std::move(ex))
89 2 : , inner_(std::move(inner))
90 : {
91 2 : }
92 :
93 : // void allocator, explicit stop token
94 2 : run_awaitable_ex(Ex ex, Task inner, std::stop_token st)
95 : requires (!InheritStopToken && std::is_void_v<Alloc>)
96 2 : : ex_(std::move(ex))
97 2 : , inner_(std::move(inner))
98 2 : , st_(std::move(st))
99 : {
100 2 : }
101 :
102 : // with allocator, inherit stop token (use template to avoid void parameter)
103 : template<class A>
104 : requires (InheritStopToken && !std::is_void_v<Alloc> && std::same_as<A, Alloc>)
105 2 : run_awaitable_ex(Ex ex, A alloc, Task inner)
106 2 : : ex_(std::move(ex))
107 2 : , resource_(std::move(alloc))
108 2 : , inner_(std::move(inner))
109 : {
110 2 : }
111 :
112 : // with allocator, explicit stop token (use template to avoid void parameter)
113 : template<class A>
114 : requires (!InheritStopToken && !std::is_void_v<Alloc> && std::same_as<A, Alloc>)
115 2 : run_awaitable_ex(Ex ex, A alloc, Task inner, std::stop_token st)
116 2 : : ex_(std::move(ex))
117 2 : , resource_(std::move(alloc))
118 2 : , inner_(std::move(inner))
119 2 : , st_(std::move(st))
120 : {
121 2 : }
122 :
123 8 : bool await_ready() const noexcept
124 : {
125 8 : return inner_.await_ready();
126 : }
127 :
128 8 : decltype(auto) await_resume()
129 : {
130 8 : return inner_.await_resume();
131 : }
132 :
133 : template<typename Caller>
134 8 : coro await_suspend(coro cont, Caller const& caller_ex, std::stop_token token)
135 : {
136 8 : auto h = inner_.handle();
137 8 : auto& p = h.promise();
138 8 : p.set_executor(ex_);
139 8 : p.set_continuation(cont, caller_ex);
140 :
141 : if constexpr (InheritStopToken)
142 4 : p.set_stop_token(token);
143 : else
144 4 : p.set_stop_token(st_);
145 :
146 : // Refresh TLS pointer to this awaitable's resource. The wrapper's
147 : // resource may be destroyed, but allocator copies are equivalent
148 : // for deallocation. This awaitable lives until co_await completes.
149 : if constexpr (!std::is_void_v<Alloc>)
150 4 : p.set_frame_allocator(resource_.get());
151 :
152 8 : return h;
153 : }
154 :
155 : // Non-copyable
156 : run_awaitable_ex(run_awaitable_ex const&) = delete;
157 : run_awaitable_ex& operator=(run_awaitable_ex const&) = delete;
158 :
159 : // Movable (no noexcept - Task may throw)
160 8 : run_awaitable_ex(run_awaitable_ex&&) = default;
161 : run_awaitable_ex& operator=(run_awaitable_ex&&) = default;
162 : };
163 :
164 : //----------------------------------------------------------
165 : //
166 : // run_awaitable - no executor (inherits caller's executor)
167 : //
168 : //----------------------------------------------------------
169 :
170 : /** Awaitable that runs a task with optional stop_token override.
171 :
172 : Does NOT store an executor - the task inherits the caller's executor
173 : directly. Since executor_ == caller_ex_, complete() does direct
174 : symmetric transfer without dispatch overhead.
175 :
176 : @tparam Task The IoLaunchableTask type
177 : @tparam InheritStopToken If true, inherit caller's stop token
178 : @tparam Alloc The allocator type (void for no allocator)
179 : */
180 : template<IoLaunchableTask Task, bool InheritStopToken, class Alloc = void>
181 : struct [[nodiscard]] run_awaitable
182 : {
183 : frame_memory_resource<Alloc> resource_;
184 : Task inner_;
185 : std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_;
186 :
187 : // void allocator, inherit stop token
188 : explicit run_awaitable(Task inner)
189 : requires (InheritStopToken && std::is_void_v<Alloc>)
190 : : inner_(std::move(inner))
191 : {
192 : }
193 :
194 : // void allocator, explicit stop token
195 1 : run_awaitable(Task inner, std::stop_token st)
196 : requires (!InheritStopToken && std::is_void_v<Alloc>)
197 1 : : inner_(std::move(inner))
198 1 : , st_(std::move(st))
199 : {
200 1 : }
201 :
202 : // with allocator, inherit stop token (use template to avoid void parameter)
203 : template<class A>
204 : requires (InheritStopToken && !std::is_void_v<Alloc> && std::same_as<A, Alloc>)
205 3 : run_awaitable(A alloc, Task inner)
206 3 : : resource_(std::move(alloc))
207 3 : , inner_(std::move(inner))
208 : {
209 3 : }
210 :
211 : // with allocator, explicit stop token (use template to avoid void parameter)
212 : template<class A>
213 : requires (!InheritStopToken && !std::is_void_v<Alloc> && std::same_as<A, Alloc>)
214 2 : run_awaitable(A alloc, Task inner, std::stop_token st)
215 2 : : resource_(std::move(alloc))
216 2 : , inner_(std::move(inner))
217 2 : , st_(std::move(st))
218 : {
219 2 : }
220 :
221 6 : bool await_ready() const noexcept
222 : {
223 6 : return inner_.await_ready();
224 : }
225 :
226 6 : decltype(auto) await_resume()
227 : {
228 6 : return inner_.await_resume();
229 : }
230 :
231 : template<typename Caller>
232 6 : coro await_suspend(coro cont, Caller const& caller_ex, std::stop_token token)
233 : {
234 6 : auto h = inner_.handle();
235 6 : auto& p = h.promise();
236 6 : p.set_executor(caller_ex);
237 6 : p.set_continuation(cont, caller_ex);
238 :
239 : if constexpr (InheritStopToken)
240 3 : p.set_stop_token(token);
241 : else
242 3 : p.set_stop_token(st_);
243 :
244 : // Refresh TLS pointer to this awaitable's resource. The wrapper's
245 : // resource may be destroyed, but allocator copies are equivalent
246 : // for deallocation. This awaitable lives until co_await completes.
247 : if constexpr (!std::is_void_v<Alloc>)
248 5 : p.set_frame_allocator(resource_.get());
249 :
250 6 : return h;
251 : }
252 :
253 : // Non-copyable
254 : run_awaitable(run_awaitable const&) = delete;
255 : run_awaitable& operator=(run_awaitable const&) = delete;
256 :
257 : // Movable (no noexcept - Task may throw)
258 6 : run_awaitable(run_awaitable&&) = default;
259 : run_awaitable& operator=(run_awaitable&&) = default;
260 : };
261 :
262 : //----------------------------------------------------------
263 : //
264 : // run_wrapper_ex - with executor
265 : //
266 : //----------------------------------------------------------
267 :
268 : /** Wrapper returned by run(ex, ...) that accepts a task for execution.
269 :
270 : @tparam Ex The executor type.
271 : @tparam InheritStopToken If true, inherit caller's stop token.
272 : @tparam Alloc The allocator type (void for no allocator).
273 : */
274 : template<Executor Ex, bool InheritStopToken, class Alloc>
275 : class [[nodiscard]] run_wrapper_ex
276 : {
277 : Ex ex_;
278 : std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_;
279 : frame_memory_resource<Alloc> resource_;
280 : Alloc alloc_; // Copy to pass to awaitable
281 :
282 : public:
283 1 : run_wrapper_ex(Ex ex, Alloc alloc)
284 : requires InheritStopToken
285 1 : : ex_(std::move(ex))
286 1 : , resource_(alloc)
287 1 : , alloc_(std::move(alloc))
288 : {
289 1 : current_frame_allocator() = &resource_;
290 1 : }
291 :
292 1 : run_wrapper_ex(Ex ex, std::stop_token st, Alloc alloc)
293 : requires (!InheritStopToken)
294 1 : : ex_(std::move(ex))
295 1 : , st_(std::move(st))
296 1 : , resource_(alloc)
297 1 : , alloc_(std::move(alloc))
298 : {
299 1 : current_frame_allocator() = &resource_;
300 1 : }
301 :
302 : // Non-copyable, non-movable (must be used immediately)
303 : run_wrapper_ex(run_wrapper_ex const&) = delete;
304 : run_wrapper_ex(run_wrapper_ex&&) = delete;
305 : run_wrapper_ex& operator=(run_wrapper_ex const&) = delete;
306 : run_wrapper_ex& operator=(run_wrapper_ex&&) = delete;
307 :
308 : template<IoLaunchableTask Task>
309 2 : [[nodiscard]] auto operator()(Task t) &&
310 : {
311 : if constexpr (InheritStopToken)
312 : return run_awaitable_ex<Task, Ex, true, Alloc>{
313 1 : std::move(ex_), std::move(alloc_), std::move(t)};
314 : else
315 : return run_awaitable_ex<Task, Ex, false, Alloc>{
316 1 : std::move(ex_), std::move(alloc_), std::move(t), std::move(st_)};
317 : }
318 : };
319 :
320 : /// Specialization for memory_resource* - stores pointer directly.
321 : template<Executor Ex, bool InheritStopToken>
322 : class [[nodiscard]] run_wrapper_ex<Ex, InheritStopToken, std::pmr::memory_resource*>
323 : {
324 : Ex ex_;
325 : std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_;
326 : std::pmr::memory_resource* mr_;
327 :
328 : public:
329 1 : run_wrapper_ex(Ex ex, std::pmr::memory_resource* mr)
330 : requires InheritStopToken
331 1 : : ex_(std::move(ex))
332 1 : , mr_(mr)
333 : {
334 1 : current_frame_allocator() = mr_;
335 1 : }
336 :
337 1 : run_wrapper_ex(Ex ex, std::stop_token st, std::pmr::memory_resource* mr)
338 : requires (!InheritStopToken)
339 1 : : ex_(std::move(ex))
340 1 : , st_(std::move(st))
341 1 : , mr_(mr)
342 : {
343 1 : current_frame_allocator() = mr_;
344 1 : }
345 :
346 : // Non-copyable, non-movable (must be used immediately)
347 : run_wrapper_ex(run_wrapper_ex const&) = delete;
348 : run_wrapper_ex(run_wrapper_ex&&) = delete;
349 : run_wrapper_ex& operator=(run_wrapper_ex const&) = delete;
350 : run_wrapper_ex& operator=(run_wrapper_ex&&) = delete;
351 :
352 : template<IoLaunchableTask Task>
353 2 : [[nodiscard]] auto operator()(Task t) &&
354 : {
355 : if constexpr (InheritStopToken)
356 : return run_awaitable_ex<Task, Ex, true, std::pmr::memory_resource*>{
357 1 : std::move(ex_), mr_, std::move(t)};
358 : else
359 : return run_awaitable_ex<Task, Ex, false, std::pmr::memory_resource*>{
360 1 : std::move(ex_), mr_, std::move(t), std::move(st_)};
361 : }
362 : };
363 :
364 : /// Specialization for no allocator (void).
365 : template<Executor Ex, bool InheritStopToken>
366 : class [[nodiscard]] run_wrapper_ex<Ex, InheritStopToken, void>
367 : {
368 : Ex ex_;
369 : std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_;
370 :
371 : public:
372 2 : explicit run_wrapper_ex(Ex ex)
373 : requires InheritStopToken
374 2 : : ex_(std::move(ex))
375 : {
376 2 : }
377 :
378 2 : run_wrapper_ex(Ex ex, std::stop_token st)
379 : requires (!InheritStopToken)
380 2 : : ex_(std::move(ex))
381 2 : , st_(std::move(st))
382 : {
383 2 : }
384 :
385 : // Non-copyable, non-movable (must be used immediately)
386 : run_wrapper_ex(run_wrapper_ex const&) = delete;
387 : run_wrapper_ex(run_wrapper_ex&&) = delete;
388 : run_wrapper_ex& operator=(run_wrapper_ex const&) = delete;
389 : run_wrapper_ex& operator=(run_wrapper_ex&&) = delete;
390 :
391 : template<IoLaunchableTask Task>
392 4 : [[nodiscard]] auto operator()(Task t) &&
393 : {
394 : if constexpr (InheritStopToken)
395 : return run_awaitable_ex<Task, Ex, true>{
396 2 : std::move(ex_), std::move(t)};
397 : else
398 : return run_awaitable_ex<Task, Ex, false>{
399 2 : std::move(ex_), std::move(t), std::move(st_)};
400 : }
401 : };
402 :
403 : //----------------------------------------------------------
404 : //
405 : // run_wrapper - no executor (inherits caller's executor)
406 : //
407 : //----------------------------------------------------------
408 :
409 : /** Wrapper returned by run(st) or run(alloc) that accepts a task.
410 :
411 : @tparam InheritStopToken If true, inherit caller's stop token.
412 : @tparam Alloc The allocator type (void for no allocator).
413 : */
414 : template<bool InheritStopToken, class Alloc>
415 : class [[nodiscard]] run_wrapper
416 : {
417 : std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_;
418 : frame_memory_resource<Alloc> resource_;
419 : Alloc alloc_; // Copy to pass to awaitable
420 :
421 : public:
422 1 : explicit run_wrapper(Alloc alloc)
423 : requires InheritStopToken
424 1 : : resource_(alloc)
425 1 : , alloc_(std::move(alloc))
426 : {
427 1 : current_frame_allocator() = &resource_;
428 1 : }
429 :
430 1 : run_wrapper(std::stop_token st, Alloc alloc)
431 : requires (!InheritStopToken)
432 1 : : st_(std::move(st))
433 1 : , resource_(alloc)
434 1 : , alloc_(std::move(alloc))
435 : {
436 1 : current_frame_allocator() = &resource_;
437 1 : }
438 :
439 : // Non-copyable, non-movable (must be used immediately)
440 : run_wrapper(run_wrapper const&) = delete;
441 : run_wrapper(run_wrapper&&) = delete;
442 : run_wrapper& operator=(run_wrapper const&) = delete;
443 : run_wrapper& operator=(run_wrapper&&) = delete;
444 :
445 : template<IoLaunchableTask Task>
446 2 : [[nodiscard]] auto operator()(Task t) &&
447 : {
448 : if constexpr (InheritStopToken)
449 : return run_awaitable<Task, true, Alloc>{
450 1 : std::move(alloc_), std::move(t)};
451 : else
452 : return run_awaitable<Task, false, Alloc>{
453 1 : std::move(alloc_), std::move(t), std::move(st_)};
454 : }
455 : };
456 :
457 : /// Specialization for memory_resource* - stores pointer directly.
458 : template<bool InheritStopToken>
459 : class [[nodiscard]] run_wrapper<InheritStopToken, std::pmr::memory_resource*>
460 : {
461 : std::conditional_t<InheritStopToken, std::monostate, std::stop_token> st_;
462 : std::pmr::memory_resource* mr_;
463 :
464 : public:
465 2 : explicit run_wrapper(std::pmr::memory_resource* mr)
466 : requires InheritStopToken
467 2 : : mr_(mr)
468 : {
469 2 : current_frame_allocator() = mr_;
470 2 : }
471 :
472 1 : run_wrapper(std::stop_token st, std::pmr::memory_resource* mr)
473 : requires (!InheritStopToken)
474 1 : : st_(std::move(st))
475 1 : , mr_(mr)
476 : {
477 1 : current_frame_allocator() = mr_;
478 1 : }
479 :
480 : // Non-copyable, non-movable (must be used immediately)
481 : run_wrapper(run_wrapper const&) = delete;
482 : run_wrapper(run_wrapper&&) = delete;
483 : run_wrapper& operator=(run_wrapper const&) = delete;
484 : run_wrapper& operator=(run_wrapper&&) = delete;
485 :
486 : template<IoLaunchableTask Task>
487 3 : [[nodiscard]] auto operator()(Task t) &&
488 : {
489 : if constexpr (InheritStopToken)
490 : return run_awaitable<Task, true, std::pmr::memory_resource*>{
491 2 : mr_, std::move(t)};
492 : else
493 : return run_awaitable<Task, false, std::pmr::memory_resource*>{
494 1 : mr_, std::move(t), std::move(st_)};
495 : }
496 : };
497 :
498 : /// Specialization for stop_token only (no allocator).
499 : template<>
500 : class [[nodiscard]] run_wrapper<false, void>
501 : {
502 : std::stop_token st_;
503 :
504 : public:
505 1 : explicit run_wrapper(std::stop_token st)
506 1 : : st_(std::move(st))
507 : {
508 1 : }
509 :
510 : // Non-copyable, non-movable (must be used immediately)
511 : run_wrapper(run_wrapper const&) = delete;
512 : run_wrapper(run_wrapper&&) = delete;
513 : run_wrapper& operator=(run_wrapper const&) = delete;
514 : run_wrapper& operator=(run_wrapper&&) = delete;
515 :
516 : template<IoLaunchableTask Task>
517 1 : [[nodiscard]] auto operator()(Task t) &&
518 : {
519 1 : return run_awaitable<Task, false, void>{std::move(t), std::move(st_)};
520 : }
521 : };
522 :
523 : } // namespace boost::capy::detail
524 :
525 : namespace boost::capy {
526 :
527 : //----------------------------------------------------------
528 : //
529 : // run() overloads - with executor
530 : //
531 : //----------------------------------------------------------
532 :
533 : /** Bind a task to execute on a specific executor.
534 :
535 : Returns a wrapper that accepts a task and produces an awaitable.
536 : When co_awaited, the task runs on the specified executor.
537 :
538 : @par Example
539 : @code
540 : co_await run(other_executor)(my_task());
541 : @endcode
542 :
543 : @param ex The executor on which the task should run.
544 :
545 : @return A wrapper that accepts a task for execution.
546 :
547 : @see task
548 : @see executor
549 : */
550 : template<Executor Ex>
551 : [[nodiscard]] auto
552 2 : run(Ex ex)
553 : {
554 2 : return detail::run_wrapper_ex<Ex, true, void>{std::move(ex)};
555 : }
556 :
557 : /** Bind a task to an executor with a stop token.
558 :
559 : @param ex The executor on which the task should run.
560 : @param st The stop token for cooperative cancellation.
561 :
562 : @return A wrapper that accepts a task for execution.
563 : */
564 : template<Executor Ex>
565 : [[nodiscard]] auto
566 2 : run(Ex ex, std::stop_token st)
567 : {
568 : return detail::run_wrapper_ex<Ex, false, void>{
569 2 : std::move(ex), std::move(st)};
570 : }
571 :
572 : /** Bind a task to an executor with a memory resource.
573 :
574 : @param ex The executor on which the task should run.
575 : @param mr The memory resource for frame allocation.
576 :
577 : @return A wrapper that accepts a task for execution.
578 : */
579 : template<Executor Ex>
580 : [[nodiscard]] auto
581 1 : run(Ex ex, std::pmr::memory_resource* mr)
582 : {
583 : return detail::run_wrapper_ex<Ex, true, std::pmr::memory_resource*>{
584 1 : std::move(ex), mr};
585 : }
586 :
587 : /** Bind a task to an executor with a standard allocator.
588 :
589 : @param ex The executor on which the task should run.
590 : @param alloc The allocator for frame allocation.
591 :
592 : @return A wrapper that accepts a task for execution.
593 : */
594 : template<Executor Ex, detail::Allocator Alloc>
595 : [[nodiscard]] auto
596 1 : run(Ex ex, Alloc alloc)
597 : {
598 : return detail::run_wrapper_ex<Ex, true, Alloc>{
599 1 : std::move(ex), std::move(alloc)};
600 : }
601 :
602 : /** Bind a task to an executor with stop token and memory resource.
603 :
604 : @param ex The executor on which the task should run.
605 : @param st The stop token for cooperative cancellation.
606 : @param mr The memory resource for frame allocation.
607 :
608 : @return A wrapper that accepts a task for execution.
609 : */
610 : template<Executor Ex>
611 : [[nodiscard]] auto
612 1 : run(Ex ex, std::stop_token st, std::pmr::memory_resource* mr)
613 : {
614 : return detail::run_wrapper_ex<Ex, false, std::pmr::memory_resource*>{
615 1 : std::move(ex), std::move(st), mr};
616 : }
617 :
618 : /** Bind a task to an executor with stop token and standard allocator.
619 :
620 : @param ex The executor on which the task should run.
621 : @param st The stop token for cooperative cancellation.
622 : @param alloc The allocator for frame allocation.
623 :
624 : @return A wrapper that accepts a task for execution.
625 : */
626 : template<Executor Ex, detail::Allocator Alloc>
627 : [[nodiscard]] auto
628 1 : run(Ex ex, std::stop_token st, Alloc alloc)
629 : {
630 : return detail::run_wrapper_ex<Ex, false, Alloc>{
631 1 : std::move(ex), std::move(st), std::move(alloc)};
632 : }
633 :
634 : //----------------------------------------------------------
635 : //
636 : // run() overloads - no executor (inherits caller's)
637 : //
638 : //----------------------------------------------------------
639 :
640 : /** Run a task with a custom stop token.
641 :
642 : The task inherits the caller's executor. Only the stop token
643 : is overridden.
644 :
645 : @par Example
646 : @code
647 : std::stop_source source;
648 : co_await run(source.get_token())(cancellable_task());
649 : @endcode
650 :
651 : @param st The stop token for cooperative cancellation.
652 :
653 : @return A wrapper that accepts a task for execution.
654 : */
655 : [[nodiscard]] inline auto
656 1 : run(std::stop_token st)
657 : {
658 1 : return detail::run_wrapper<false, void>{std::move(st)};
659 : }
660 :
661 : /** Run a task with a custom memory resource.
662 :
663 : The task inherits the caller's executor. The memory resource
664 : is used for nested frame allocations.
665 :
666 : @param mr The memory resource for frame allocation.
667 :
668 : @return A wrapper that accepts a task for execution.
669 : */
670 : [[nodiscard]] inline auto
671 2 : run(std::pmr::memory_resource* mr)
672 : {
673 2 : return detail::run_wrapper<true, std::pmr::memory_resource*>{mr};
674 : }
675 :
676 : /** Run a task with a custom standard allocator.
677 :
678 : The task inherits the caller's executor. The allocator is used
679 : for nested frame allocations.
680 :
681 : @param alloc The allocator for frame allocation.
682 :
683 : @return A wrapper that accepts a task for execution.
684 : */
685 : template<detail::Allocator Alloc>
686 : [[nodiscard]] auto
687 1 : run(Alloc alloc)
688 : {
689 1 : return detail::run_wrapper<true, Alloc>{std::move(alloc)};
690 : }
691 :
692 : /** Run a task with stop token and memory resource.
693 :
694 : The task inherits the caller's executor.
695 :
696 : @param st The stop token for cooperative cancellation.
697 : @param mr The memory resource for frame allocation.
698 :
699 : @return A wrapper that accepts a task for execution.
700 : */
701 : [[nodiscard]] inline auto
702 1 : run(std::stop_token st, std::pmr::memory_resource* mr)
703 : {
704 : return detail::run_wrapper<false, std::pmr::memory_resource*>{
705 1 : std::move(st), mr};
706 : }
707 :
708 : /** Run a task with stop token and standard allocator.
709 :
710 : The task inherits the caller's executor.
711 :
712 : @param st The stop token for cooperative cancellation.
713 : @param alloc The allocator for frame allocation.
714 :
715 : @return A wrapper that accepts a task for execution.
716 : */
717 : template<detail::Allocator Alloc>
718 : [[nodiscard]] auto
719 1 : run(std::stop_token st, Alloc alloc)
720 : {
721 : return detail::run_wrapper<false, Alloc>{
722 1 : std::move(st), std::move(alloc)};
723 : }
724 :
725 : } // namespace boost::capy
726 :
727 : #endif
|