LCOV - code coverage report
Current view: top level - boost/capy - task.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 93.0 % 71 66
Test Date: 2026-01-17 12:16:19 Functions: 93.3 % 240 224

            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/corosio
       8              : //
       9              : 
      10              : #ifndef BOOST_CAPY_TASK_HPP
      11              : #define BOOST_CAPY_TASK_HPP
      12              : 
      13              : #include <boost/capy/detail/config.hpp>
      14              : #include <boost/capy/ex/any_dispatcher.hpp>
      15              : #include <boost/capy/concept/affine_awaitable.hpp>
      16              : #include <boost/capy/concept/stoppable_awaitable.hpp>
      17              : #include <boost/capy/ex/frame_allocator.hpp>
      18              : #include <boost/capy/ex/get_stop_token.hpp>
      19              : #include <boost/capy/ex/make_affine.hpp>
      20              : #include <boost/capy/ex/stop_token_support.hpp>
      21              : 
      22              : #include <exception>
      23              : #include <optional>
      24              : #include <type_traits>
      25              : #include <utility>
      26              : #include <variant>
      27              : 
      28              : namespace boost {
      29              : namespace capy {
      30              : 
      31              : namespace detail {
      32              : 
      33              : // Helper base for result storage and return_void/return_value
      34              : template<typename T>
      35              : struct task_return_base
      36              : {
      37              :     std::optional<T> result_;
      38              : 
      39          149 :     void return_value(T value)
      40              :     {
      41          149 :         result_ = std::move(value);
      42          149 :     }
      43              : };
      44              : 
      45              : template<>
      46              : struct task_return_base<void>
      47              : {
      48           37 :     void return_void()
      49              :     {
      50           37 :     }
      51              : };
      52              : 
      53              : } // namespace detail
      54              : 
      55              : /** A coroutine task type implementing the affine awaitable protocol.
      56              : 
      57              :     This task type represents an asynchronous operation that can be awaited.
      58              :     It implements the affine awaitable protocol where `await_suspend` receives
      59              :     the caller's executor, enabling proper completion dispatch across executor
      60              :     boundaries.
      61              : 
      62              :     @tparam T The return type of the task. Defaults to void.
      63              : 
      64              :     Key features:
      65              :     @li Lazy execution - the coroutine does not start until awaited
      66              :     @li Symmetric transfer - uses coroutine handle returns for efficient
      67              :         resumption
      68              :     @li Executor inheritance - inherits caller's executor unless explicitly
      69              :         bound
      70              : 
      71              :     The task uses `[[clang::coro_await_elidable]]` (when available) to enable
      72              :     heap allocation elision optimization (HALO) for nested coroutine calls.
      73              : 
      74              :     @see any_dispatcher
      75              : */
      76              : template<typename T = void>
      77              : struct [[nodiscard]] BOOST_CAPY_CORO_AWAIT_ELIDABLE
      78              :     task
      79              : {
      80              :     struct promise_type
      81              :         : frame_allocating_base
      82              : #if BOOST_CAPY_HAS_STOP_TOKEN
      83              :         , stop_token_support<promise_type>
      84              : #endif
      85              :         , detail::task_return_base<T>
      86              :     {
      87              :         any_dispatcher ex_;
      88              :         any_dispatcher caller_ex_;
      89              :         any_coro continuation_;
      90              :         std::exception_ptr ep_;
      91              :         detail::frame_allocator_base* alloc_ = nullptr;
      92              :         bool needs_dispatch_ = false;
      93              : 
      94          224 :         task get_return_object()
      95              :         {
      96          224 :             return task{std::coroutine_handle<promise_type>::from_promise(*this)};
      97              :         }
      98              : 
      99          224 :         auto initial_suspend() noexcept
     100              :         {
     101              :             struct awaiter
     102              :             {
     103              :                 promise_type* p_;
     104              : 
     105          224 :                 bool await_ready() const noexcept
     106              :                 {
     107          224 :                     return false;
     108              :                 }
     109              : 
     110          224 :                 void await_suspend(any_coro) const noexcept
     111              :                 {
     112              :                     // Capture TLS allocator while it's still valid
     113          224 :                     p_->alloc_ = get_frame_allocator();
     114          224 :                 }
     115              : 
     116          223 :                 void await_resume() const noexcept
     117              :                 {
     118              :                     // Restore TLS when body starts executing
     119          223 :                     if(p_->alloc_)
     120            0 :                         set_frame_allocator(*p_->alloc_);
     121          223 :                 }
     122              :             };
     123          224 :             return awaiter{this};
     124              :         }
     125              : 
     126          223 :         auto final_suspend() noexcept
     127              :         {
     128              :             struct awaiter
     129              :             {
     130              :                 promise_type* p_;
     131              : 
     132          223 :                 bool await_ready() const noexcept
     133              :                 {
     134          223 :                     return false;
     135              :                 }
     136              : 
     137          223 :                 any_coro await_suspend(any_coro) const noexcept
     138              :                 {
     139          223 :                     if(p_->continuation_)
     140              :                     {
     141              :                         // Same dispatcher: true symmetric transfer
     142          205 :                         if(!p_->needs_dispatch_)
     143          205 :                             return p_->continuation_;
     144            0 :                         return p_->caller_ex_(p_->continuation_);
     145              :                     }
     146           18 :                     return std::noop_coroutine();
     147              :                 }
     148              : 
     149            0 :                 void await_resume() const noexcept
     150              :                 {
     151            0 :                 }
     152              :             };
     153          223 :             return awaiter{this};
     154              :         }
     155              : 
     156              :         // return_void() or return_value() inherited from task_return_base
     157              : 
     158           37 :         void unhandled_exception()
     159              :         {
     160           37 :             ep_ = std::current_exception();
     161           37 :         }
     162              : 
     163              :         template<class Awaitable>
     164              :         struct transform_awaiter
     165              :         {
     166              :             std::decay_t<Awaitable> a_;
     167              :             promise_type* p_;
     168              : 
     169           99 :             bool await_ready()
     170              :             {
     171           99 :                 return a_.await_ready();
     172              :             }
     173              : 
     174           99 :             auto await_resume()
     175              :             {
     176              :                 // Restore TLS before body resumes
     177           99 :                 if(p_->alloc_)
     178            0 :                     set_frame_allocator(*p_->alloc_);
     179           99 :                 return a_.await_resume();
     180              :             }
     181              : 
     182              :             template<class Promise>
     183           99 :             auto await_suspend(std::coroutine_handle<Promise> h)
     184              :             {
     185              : #if BOOST_CAPY_HAS_STOP_TOKEN
     186              :                 using A = std::decay_t<Awaitable>;
     187              :                 if constexpr (stoppable_awaitable<A, any_dispatcher>)
     188           75 :                     return a_.await_suspend(h, p_->ex_, p_->stop_token());
     189              :                 else
     190              : #endif
     191           24 :                     return a_.await_suspend(h, p_->ex_);
     192              :             }
     193              :         };
     194              : 
     195              :         template<class Awaitable>
     196           99 :         auto transform_awaitable(Awaitable&& a)
     197              :         {
     198              :             using A = std::decay_t<Awaitable>;
     199              :             if constexpr (affine_awaitable<A, any_dispatcher>)
     200              :             {
     201              :                 // Zero-overhead path for affine awaitables
     202              :                 return transform_awaiter<Awaitable>{
     203          174 :                     std::forward<Awaitable>(a), this};
     204              :             }
     205              :             else
     206              :             {
     207              :                 // Trampoline fallback for legacy awaitables
     208              :                 return make_affine(std::forward<Awaitable>(a), ex_);
     209              :             }
     210           75 :         }
     211              : 
     212              : #if !BOOST_CAPY_HAS_STOP_TOKEN
     213              :         // Without stop token support, provide await_transform directly
     214              :         template<class Awaitable>
     215              :         auto await_transform(Awaitable&& a)
     216              :         {
     217              :             return transform_awaitable(std::forward<Awaitable>(a));
     218              :         }
     219              : #endif
     220              :     };
     221              : 
     222              :     std::coroutine_handle<promise_type> h_;
     223              : 
     224          590 :     ~task()
     225              :     {
     226          590 :         if(h_)
     227          113 :             h_.destroy();
     228          590 :     }
     229              : 
     230          113 :     bool await_ready() const noexcept
     231              :     {
     232          113 :         return false;
     233              :     }
     234              : 
     235          112 :     auto await_resume()
     236              :     {
     237          112 :         if(h_.promise().ep_)
     238           16 :             std::rethrow_exception(h_.promise().ep_);
     239              :         if constexpr (! std::is_void_v<T>)
     240           79 :             return std::move(*h_.promise().result_);
     241              :         else
     242           17 :             return;
     243              :     }
     244              : 
     245              :     // Affine awaitable: receive caller's dispatcher for completion dispatch
     246              :     template<dispatcher D>
     247              :     any_coro await_suspend(any_coro continuation, D const& caller_ex)
     248              :     {
     249              :         h_.promise().caller_ex_ = caller_ex;
     250              :         h_.promise().continuation_ = continuation;
     251              :         h_.promise().ex_ = caller_ex;
     252              :         h_.promise().needs_dispatch_ = false;
     253              :         return h_;
     254              :     }
     255              : 
     256              : #if BOOST_CAPY_HAS_STOP_TOKEN
     257              :     // Stoppable awaitable: receive caller's dispatcher and stop_token
     258              :     template<dispatcher D>
     259          112 :     any_coro await_suspend(any_coro continuation, D const& caller_ex, std::stop_token token)
     260              :     {
     261          112 :         h_.promise().caller_ex_ = caller_ex;
     262          112 :         h_.promise().continuation_ = continuation;
     263          112 :         h_.promise().ex_ = caller_ex;
     264          112 :         h_.promise().set_stop_token(token);
     265          112 :         h_.promise().needs_dispatch_ = false;
     266          112 :         return h_;
     267              :     }
     268              : #endif
     269              : 
     270              :     /** Release ownership of the coroutine handle.
     271              : 
     272              :         After calling this, the task no longer owns the handle and will
     273              :         not destroy it. The caller is responsible for the handle's lifetime.
     274              : 
     275              :         @return The coroutine handle, or nullptr if already released.
     276              :     */
     277          114 :     auto release() noexcept ->
     278              :         std::coroutine_handle<promise_type>
     279              :     {
     280          114 :         return std::exchange(h_, nullptr);
     281              :     }
     282              : 
     283              :     // Non-copyable
     284              :     task(task const&) = delete;
     285              :     task& operator=(task const&) = delete;
     286              : 
     287              :     // Movable
     288          366 :     task(task&& other) noexcept
     289          366 :         : h_(std::exchange(other.h_, nullptr))
     290              :     {
     291          366 :     }
     292              : 
     293              :     task& operator=(task&& other) noexcept
     294              :     {
     295              :         if(this != &other)
     296              :         {
     297              :             if(h_)
     298              :                 h_.destroy();
     299              :             h_ = std::exchange(other.h_, nullptr);
     300              :         }
     301              :         return *this;
     302              :     }
     303              : 
     304              : private:
     305          224 :     explicit task(std::coroutine_handle<promise_type> h)
     306          224 :         : h_(h)
     307              :     {
     308          224 :     }
     309              : };
     310              : 
     311              : } // namespace capy
     312              : } // namespace boost
     313              : 
     314              : #endif
        

Generated by: LCOV version 2.3