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 18:26:31 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/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          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_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              : #if BOOST_CAPY_HAS_STOP_TOKEN
      83              :         , stop_token_support<promise_type>
      84              : #endif
      85              :         , detail::task_return_base<T>
      86              :     {
      87              :         any_executor_ref ex_;
      88              :         any_executor_ref 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 executor: true symmetric transfer
     142          205 :                         if(!p_->needs_dispatch_)
     143          205 :                             return p_->continuation_;
     144            0 :                         return p_->caller_ex_.dispatch(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           99 :                 return a_.await_suspend(h, p_->ex_, p_->stop_token());
     187              : #else
     188              :                 return a_.await_suspend(h, p_->ex_, std::stop_token{});
     189              : #endif
     190              :             }
     191              :         };
     192              : 
     193              :         template<class Awaitable>
     194           99 :         auto transform_awaitable(Awaitable&& a)
     195              :         {
     196              :             using A = std::decay_t<Awaitable>;
     197              :             if constexpr (IoAwaitable<A, any_executor_ref>)
     198              :             {
     199              :                 // Zero-overhead path for I/O awaitables
     200              :                 return transform_awaiter<Awaitable>{
     201          174 :                     std::forward<Awaitable>(a), this};
     202              :             }
     203              :             else
     204              :             {
     205              :                 // Trampoline fallback for legacy awaitables
     206              :                 return make_affine(std::forward<Awaitable>(a), ex_);
     207              :             }
     208           75 :         }
     209              : 
     210              : #if !BOOST_CAPY_HAS_STOP_TOKEN
     211              :         // Without stop token support, provide await_transform directly
     212              :         template<class Awaitable>
     213              :         auto await_transform(Awaitable&& a)
     214              :         {
     215              :             return transform_awaitable(std::forward<Awaitable>(a));
     216              :         }
     217              : #endif
     218              :     };
     219              : 
     220              :     std::coroutine_handle<promise_type> h_;
     221              : 
     222          590 :     ~task()
     223              :     {
     224          590 :         if(h_)
     225          113 :             h_.destroy();
     226          590 :     }
     227              : 
     228          113 :     bool await_ready() const noexcept
     229              :     {
     230          113 :         return false;
     231              :     }
     232              : 
     233          112 :     auto await_resume()
     234              :     {
     235          112 :         if(h_.promise().ep_)
     236           16 :             std::rethrow_exception(h_.promise().ep_);
     237              :         if constexpr (! std::is_void_v<T>)
     238           79 :             return std::move(*h_.promise().result_);
     239              :         else
     240           17 :             return;
     241              :     }
     242              : 
     243              :     // IoAwaitable: receive caller's executor and stop_token for completion dispatch
     244              :     template<typename Ex>
     245          112 :     any_coro await_suspend(any_coro continuation, Ex const& caller_ex, std::stop_token token)
     246              :     {
     247          112 :         h_.promise().caller_ex_ = caller_ex;
     248          112 :         h_.promise().continuation_ = continuation;
     249          112 :         h_.promise().ex_ = caller_ex;
     250              : #if BOOST_CAPY_HAS_STOP_TOKEN
     251          112 :         h_.promise().set_stop_token(token);
     252              : #else
     253              :         (void)token;
     254              : #endif
     255          112 :         h_.promise().needs_dispatch_ = false;
     256          112 :         return h_;
     257              :     }
     258              : 
     259              :     /** Release ownership of the coroutine handle.
     260              : 
     261              :         After calling this, the task no longer owns the handle and will
     262              :         not destroy it. The caller is responsible for the handle's lifetime.
     263              : 
     264              :         @return The coroutine handle, or nullptr if already released.
     265              :     */
     266          114 :     auto release() noexcept ->
     267              :         std::coroutine_handle<promise_type>
     268              :     {
     269          114 :         return std::exchange(h_, nullptr);
     270              :     }
     271              : 
     272              :     // Non-copyable
     273              :     task(task const&) = delete;
     274              :     task& operator=(task const&) = delete;
     275              : 
     276              :     // Movable
     277          366 :     task(task&& other) noexcept
     278          366 :         : h_(std::exchange(other.h_, nullptr))
     279              :     {
     280          366 :     }
     281              : 
     282              :     task& operator=(task&& other) noexcept
     283              :     {
     284              :         if(this != &other)
     285              :         {
     286              :             if(h_)
     287              :                 h_.destroy();
     288              :             h_ = std::exchange(other.h_, nullptr);
     289              :         }
     290              :         return *this;
     291              :     }
     292              : 
     293              : private:
     294          224 :     explicit task(std::coroutine_handle<promise_type> h)
     295          224 :         : h_(h)
     296              :     {
     297          224 :     }
     298              : };
     299              : 
     300              : } // namespace capy
     301              : } // namespace boost
     302              : 
     303              : #endif
        

Generated by: LCOV version 2.3