LCOV - code coverage report
Current view: top level - boost/capy - task.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 96.1 % 77 74
Test Date: 2026-01-15 18:27:21 Functions: 94.9 % 255 242

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

Generated by: LCOV version 2.3