LCOV - code coverage report
Current view: top level - boost/capy - task.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 94.4 % 71 67
Test Date: 2026-01-23 03:27:33 Functions: 56.9 % 246 140

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

Generated by: LCOV version 2.3