LCOV - code coverage report
Current view: top level - capy/ex - run.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 100.0 % 154 154
Test Date: 2026-02-01 07:03:35 Functions: 100.0 % 106 106

            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
        

Generated by: LCOV version 2.3