LCOV - code coverage report
Current view: top level - boost/capy - task.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 92.9 % 70 65
Test Date: 2026-01-18 20:48:05 Functions: 93.1 % 232 216

            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/concept/executor.hpp>
      15              : #include <boost/capy/concept/io_awaitable.hpp>
      16              : #include <boost/capy/ex/any_executor_ref.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          134 :     void return_value(T value)
      40              :     {
      41          134 :         result_ = std::move(value);
      42          134 :     }
      43              : };
      44              : 
      45              : template<>
      46              : struct task_return_base<void>
      47              : {
      48           28 :     void return_void()
      49              :     {
      50           28 :     }
      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_executor_ref
      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              :         , stop_token_support<promise_type>
      83              :         , detail::task_return_base<T>
      84              :     {
      85              :         any_executor_ref ex_;
      86              :         any_executor_ref caller_ex_;
      87              :         any_coro continuation_;
      88              :         std::exception_ptr ep_;
      89              :         detail::frame_allocator_base* alloc_ = nullptr;
      90              :         bool needs_dispatch_ = false;
      91              : 
      92          200 :         task get_return_object()
      93              :         {
      94          200 :             return task{std::coroutine_handle<promise_type>::from_promise(*this)};
      95              :         }
      96              : 
      97          200 :         auto initial_suspend() noexcept
      98              :         {
      99              :             struct awaiter
     100              :             {
     101              :                 promise_type* p_;
     102              : 
     103          200 :                 bool await_ready() const noexcept
     104              :                 {
     105          200 :                     return false;
     106              :                 }
     107              : 
     108          200 :                 void await_suspend(any_coro) const noexcept
     109              :                 {
     110              :                     // Capture TLS allocator while it's still valid
     111          200 :                     p_->alloc_ = get_frame_allocator();
     112          200 :                 }
     113              : 
     114          199 :                 void await_resume() const noexcept
     115              :                 {
     116              :                     // Restore TLS when body starts executing
     117          199 :                     if(p_->alloc_)
     118            0 :                         set_frame_allocator(*p_->alloc_);
     119          199 :                 }
     120              :             };
     121          200 :             return awaiter{this};
     122              :         }
     123              : 
     124          199 :         auto final_suspend() noexcept
     125              :         {
     126              :             struct awaiter
     127              :             {
     128              :                 promise_type* p_;
     129              : 
     130          199 :                 bool await_ready() const noexcept
     131              :                 {
     132          199 :                     return false;
     133              :                 }
     134              : 
     135          199 :                 any_coro await_suspend(any_coro) const noexcept
     136              :                 {
     137          199 :                     if(p_->continuation_)
     138              :                     {
     139              :                         // Same executor: true symmetric transfer
     140          182 :                         if(!p_->needs_dispatch_)
     141          182 :                             return p_->continuation_;
     142            0 :                         return p_->caller_ex_.dispatch(p_->continuation_);
     143              :                     }
     144           17 :                     return std::noop_coroutine();
     145              :                 }
     146              : 
     147            0 :                 void await_resume() const noexcept
     148              :                 {
     149            0 :                 }
     150              :             };
     151          199 :             return awaiter{this};
     152              :         }
     153              : 
     154              :         // return_void() or return_value() inherited from task_return_base
     155              : 
     156           37 :         void unhandled_exception()
     157              :         {
     158           37 :             ep_ = std::current_exception();
     159           37 :         }
     160              : 
     161              :         template<class Awaitable>
     162              :         struct transform_awaiter
     163              :         {
     164              :             std::decay_t<Awaitable> a_;
     165              :             promise_type* p_;
     166              : 
     167           64 :             bool await_ready()
     168              :             {
     169           64 :                 return a_.await_ready();
     170              :             }
     171              : 
     172           64 :             auto await_resume()
     173              :             {
     174              :                 // Restore TLS before body resumes
     175           64 :                 if(p_->alloc_)
     176            0 :                     set_frame_allocator(*p_->alloc_);
     177           64 :                 return a_.await_resume();
     178              :             }
     179              : 
     180              :             template<class Promise>
     181           64 :             auto await_suspend(std::coroutine_handle<Promise> h)
     182              :             {
     183           64 :                 return a_.await_suspend(h, p_->ex_, p_->stop_token());
     184              :             }
     185              :         };
     186              : 
     187              :         template<class Awaitable>
     188           64 :         auto transform_awaitable(Awaitable&& a)
     189              :         {
     190              :             using A = std::decay_t<Awaitable>;
     191              :             if constexpr (IoAwaitable<A, any_executor_ref>)
     192              :             {
     193              :                 // Zero-overhead path for I/O awaitables
     194              :                 return transform_awaiter<Awaitable>{
     195          104 :                     std::forward<Awaitable>(a), this};
     196              :             }
     197              :             else
     198              :             {
     199              :                 // Trampoline fallback for legacy awaitables
     200              :                 return make_affine(std::forward<Awaitable>(a), ex_);
     201              :             }
     202           40 :         }
     203              :     };
     204              : 
     205              :     std::coroutine_handle<promise_type> h_;
     206              : 
     207          555 :     ~task()
     208              :     {
     209          555 :         if(h_)
     210          102 :             h_.destroy();
     211          555 :     }
     212              : 
     213          102 :     bool await_ready() const noexcept
     214              :     {
     215          102 :         return false;
     216              :     }
     217              : 
     218          101 :     auto await_resume()
     219              :     {
     220          101 :         if(h_.promise().ep_)
     221           16 :             std::rethrow_exception(h_.promise().ep_);
     222              :         if constexpr (! std::is_void_v<T>)
     223           72 :             return std::move(*h_.promise().result_);
     224              :         else
     225           13 :             return;
     226              :     }
     227              : 
     228              :     // IoAwaitable: receive caller's executor and stop_token for completion dispatch
     229              :     template<typename Ex>
     230          101 :     any_coro await_suspend(any_coro continuation, Ex const& caller_ex, std::stop_token token)
     231              :     {
     232          101 :         h_.promise().caller_ex_ = caller_ex;
     233          101 :         h_.promise().continuation_ = continuation;
     234          101 :         h_.promise().ex_ = caller_ex;
     235          101 :         h_.promise().set_stop_token(token);
     236          101 :         h_.promise().needs_dispatch_ = false;
     237          101 :         return h_;
     238              :     }
     239              : 
     240              :     /** Release ownership of the coroutine handle.
     241              : 
     242              :         After calling this, the task no longer owns the handle and will
     243              :         not destroy it. The caller is responsible for the handle's lifetime.
     244              : 
     245              :         @return The coroutine handle, or nullptr if already released.
     246              :     */
     247          101 :     auto release() noexcept ->
     248              :         std::coroutine_handle<promise_type>
     249              :     {
     250          101 :         return std::exchange(h_, nullptr);
     251              :     }
     252              : 
     253              :     // Non-copyable
     254              :     task(task const&) = delete;
     255              :     task& operator=(task const&) = delete;
     256              : 
     257              :     // Movable
     258          355 :     task(task&& other) noexcept
     259          355 :         : h_(std::exchange(other.h_, nullptr))
     260              :     {
     261          355 :     }
     262              : 
     263              :     task& operator=(task&& other) noexcept
     264              :     {
     265              :         if(this != &other)
     266              :         {
     267              :             if(h_)
     268              :                 h_.destroy();
     269              :             h_ = std::exchange(other.h_, nullptr);
     270              :         }
     271              :         return *this;
     272              :     }
     273              : 
     274              : private:
     275          200 :     explicit task(std::coroutine_handle<promise_type> h)
     276          200 :         : h_(h)
     277              :     {
     278          200 :     }
     279              : };
     280              : 
     281              : } // namespace capy
     282              : } // namespace boost
     283              : 
     284              : #endif
        

Generated by: LCOV version 2.3