LCOV - code coverage report
Current view: top level - boost/capy - task.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 95.8 % 71 68
Test Date: 2026-01-15 23:24:39 Functions: 95.0 % 262 249

            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          135 :     void return_value(T value)
      39              :     {
      40          135 :         result_ = std::move(value);
      41          135 :     }
      42              : };
      43              : 
      44              : template<>
      45              : struct task_return_base<void>
      46              : {
      47           33 :     void return_void()
      48              :     {
      49           33 :     }
      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          205 :         task get_return_object()
      94              :         {
      95          205 :             return task{std::coroutine_handle<promise_type>::from_promise(*this)};
      96              :         }
      97              : 
      98          205 :         auto initial_suspend() noexcept
      99              :         {
     100              :             struct awaiter
     101              :             {
     102              :                 promise_type* p_;
     103              : 
     104          205 :                 bool await_ready() const noexcept
     105              :                 {
     106          205 :                     return false;
     107              :                 }
     108              : 
     109          205 :                 void await_suspend(any_coro) const noexcept
     110              :                 {
     111              :                     // Capture TLS allocator while it's still valid
     112          205 :                     p_->alloc_ = get_frame_allocator();
     113          205 :                 }
     114              : 
     115          204 :                 void await_resume() const noexcept
     116              :                 {
     117              :                     // Restore TLS when body starts executing
     118          204 :                     if(p_->alloc_)
     119          175 :                         set_frame_allocator(*p_->alloc_);
     120          204 :                 }
     121              :             };
     122          205 :             return awaiter{this};
     123              :         }
     124              : 
     125          204 :         auto final_suspend() noexcept
     126              :         {
     127              :             struct awaiter
     128              :             {
     129              :                 promise_type* p_;
     130              : 
     131          204 :                 bool await_ready() const noexcept
     132              :                 {
     133          204 :                     return false;
     134              :                 }
     135              : 
     136          204 :                 any_coro await_suspend(any_coro) const noexcept
     137              :                 {
     138          204 :                     if(p_->continuation_)
     139              :                     {
     140              :                         // Same dispatcher: true symmetric transfer
     141          187 :                         if(!p_->needs_dispatch_)
     142          187 :                             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          204 :             return awaiter{this};
     153              :         }
     154              : 
     155              :         // return_void() or return_value() inherited from task_return_base
     156              : 
     157           36 :         void unhandled_exception()
     158              :         {
     159           36 :             ep_ = std::current_exception();
     160           36 :         }
     161              : 
     162              :         template<class Awaitable>
     163              :         struct transform_awaiter
     164              :         {
     165              :             std::decay_t<Awaitable> a_;
     166              :             promise_type* p_;
     167              : 
     168          123 :             bool await_ready()
     169              :             {
     170          123 :                 return a_.await_ready();
     171              :             }
     172              : 
     173          123 :             auto await_resume()
     174              :             {
     175              :                 // Restore TLS before body resumes
     176          123 :                 if(p_->alloc_)
     177          111 :                     set_frame_allocator(*p_->alloc_);
     178          123 :                 return a_.await_resume();
     179              :             }
     180              : 
     181              :             template<class Promise>
     182          123 :             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           89 :                     return a_.await_suspend(h, p_->ex_, p_->stop_token_);
     188              :                 else
     189              : #endif
     190           34 :                     return a_.await_suspend(h, p_->ex_);
     191              :             }
     192              :         };
     193              : 
     194              :         template<class Awaitable>
     195          123 :         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          222 :                     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           99 :         }
     210              :     };
     211              : 
     212              :     std::coroutine_handle<promise_type> h_;
     213              : 
     214          638 :     ~task()
     215              :     {
     216          638 :         if(h_)
     217          127 :             h_.destroy();
     218          638 :     }
     219              : 
     220          127 :     bool await_ready() const noexcept
     221              :     {
     222          127 :         return false;
     223              :     }
     224              : 
     225          126 :     auto await_resume()
     226              :     {
     227          126 :         if(h_.promise().ep_)
     228           21 :             std::rethrow_exception(h_.promise().ep_);
     229              :         if constexpr (! std::is_void_v<T>)
     230           91 :             return std::move(*h_.promise().result_);
     231              :         else
     232           14 :             return;
     233              :     }
     234              : 
     235              :     // Affine awaitable: receive caller's dispatcher for completion dispatch
     236              :     template<dispatcher D>
     237              :     any_coro await_suspend(any_coro continuation, D const& caller_ex)
     238              :     {
     239              :         h_.promise().caller_ex_ = caller_ex;
     240              :         h_.promise().continuation_ = continuation;
     241              :         h_.promise().ex_ = caller_ex;
     242              :         h_.promise().needs_dispatch_ = false;
     243              :         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          126 :     any_coro await_suspend(any_coro continuation, D const& caller_ex, std::stop_token token)
     250              :     {
     251          126 :         h_.promise().caller_ex_ = caller_ex;
     252          126 :         h_.promise().continuation_ = continuation;
     253          126 :         h_.promise().ex_ = caller_ex;
     254          126 :         h_.promise().stop_token_ = token;
     255          126 :         h_.promise().needs_dispatch_ = false;
     256          126 :         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           81 :     auto release() noexcept ->
     268              :         std::coroutine_handle<promise_type>
     269              :     {
     270           81 :         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          433 :     task(task&& other) noexcept
     279          433 :         : h_(std::exchange(other.h_, nullptr))
     280              :     {
     281          433 :     }
     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          205 :     explicit task(std::coroutine_handle<promise_type> h)
     296          205 :         : h_(h)
     297              :     {
     298          205 :     }
     299              : };
     300              : 
     301              : } // namespace capy
     302              : } // namespace boost
     303              : 
     304              : #endif
        

Generated by: LCOV version 2.3