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-24 00:02:10 Functions: 60.4 % 278 168

            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           98 :     void return_value(T value)
      37              :     {
      38           98 :         result_ = std::move(value);
      39           98 :     }
      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           57 :     void return_void()
      51              :     {
      52           57 :     }
      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          108 :         std::exception_ptr exception() const noexcept
      91              :         {
      92          108 :             return ep_;
      93              :         }
      94              : 
      95          178 :         task get_return_object()
      96              :         {
      97          178 :             return task{std::coroutine_handle<promise_type>::from_promise(*this)};
      98              :         }
      99              : 
     100          178 :         auto initial_suspend() noexcept
     101              :         {
     102              :             struct awaiter
     103              :             {
     104              :                 promise_type* p_;
     105              : 
     106          178 :                 bool await_ready() const noexcept
     107              :                 {
     108          178 :                     return false;
     109              :                 }
     110              : 
     111          178 :                 void await_suspend(coro) const noexcept
     112              :                 {
     113              :                     // Capture TLS allocator while it's still valid
     114          178 :                     p_->alloc_ = get_frame_allocator();
     115          178 :                 }
     116              : 
     117          177 :                 void await_resume() const noexcept
     118              :                 {
     119              :                     // Restore TLS when body starts executing
     120          177 :                     if(p_->alloc_)
     121            0 :                         set_frame_allocator(*p_->alloc_);
     122          177 :                 }
     123              :             };
     124          178 :             return awaiter{this};
     125              :         }
     126              : 
     127          177 :         auto final_suspend() noexcept
     128              :         {
     129              :             struct awaiter
     130              :             {
     131              :                 promise_type* p_;
     132              : 
     133          177 :                 bool await_ready() const noexcept
     134              :                 {
     135          177 :                     return false;
     136              :                 }
     137              : 
     138          177 :                 coro await_suspend(coro) const noexcept
     139              :                 {
     140          177 :                     return p_->complete();
     141              :                 }
     142              : 
     143            0 :                 void await_resume() const noexcept
     144              :                 {
     145            0 :                 }
     146              :             };
     147          177 :             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          102 :             bool await_ready()
     164              :             {
     165          102 :                 return a_.await_ready();
     166              :             }
     167              : 
     168          102 :             auto await_resume()
     169              :             {
     170              :                 // Restore TLS before body resumes
     171          102 :                 if(p_->alloc_)
     172            0 :                     set_frame_allocator(*p_->alloc_);
     173          102 :                 return a_.await_resume();
     174              :             }
     175              : 
     176              :             template<class Promise>
     177           65 :             auto await_suspend(std::coroutine_handle<Promise> h)
     178              :             {
     179           65 :                 return a_.await_suspend(h, p_->executor(), p_->stop_token());
     180              :             }
     181              :         };
     182              : 
     183              :         template<class Awaitable>
     184          102 :         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          153 :                     std::forward<Awaitable>(a), this};
     192              :             }
     193              :             else
     194              :             {
     195              :                 static_assert(sizeof(A) == 0, "requires IoAwaitable");
     196              :             }
     197           51 :         }
     198              :     };
     199              : 
     200              :     std::coroutine_handle<promise_type> h_;
     201              : 
     202          277 :     ~task()
     203              :     {
     204          277 :         if(h_)
     205           51 :             h_.destroy();
     206          277 :     }
     207              : 
     208           51 :     bool await_ready() const noexcept
     209              :     {
     210           51 :         return false;
     211              :     }
     212              : 
     213           50 :     auto await_resume()
     214              :     {
     215           50 :         if(h_.promise().ep_)
     216            4 :             std::rethrow_exception(h_.promise().ep_);
     217              :         if constexpr (! std::is_void_v<T>)
     218           38 :             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           50 :     coro await_suspend(coro cont, Ex const& caller_ex, std::stop_token token)
     226              :     {
     227           50 :         h_.promise().set_continuation(cont, caller_ex);
     228           50 :         h_.promise().set_executor(caller_ex);
     229           50 :         h_.promise().set_stop_token(token);
     230           50 :         return h_;
     231              :     }
     232              : 
     233              :     /** Return the coroutine handle.
     234              : 
     235              :         @return The coroutine handle.
     236              :     */
     237          130 :     std::coroutine_handle<promise_type> handle() const noexcept
     238              :     {
     239          130 :         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          127 :     void release() noexcept
     248              :     {
     249          127 :         h_ = nullptr;
     250          127 :     }
     251              : 
     252              :     // Non-copyable
     253              :     task(task const&) = delete;
     254              :     task& operator=(task const&) = delete;
     255              : 
     256              :     // Movable
     257           99 :     task(task&& other) noexcept
     258           99 :         : h_(std::exchange(other.h_, nullptr))
     259              :     {
     260           99 :     }
     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          178 :     explicit task(std::coroutine_handle<promise_type> h)
     275          178 :         : h_(h)
     276              :     {
     277          178 :     }
     278              : };
     279              : 
     280              : } // namespace capy
     281              : } // namespace boost
     282              : 
     283              : #endif
        

Generated by: LCOV version 2.3