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

Generated by: LCOV version 2.3