LCOV - code coverage report
Current view: top level - boost/capy - task.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 97.2 % 71 69
Test Date: 2026-01-30 23:43:15 Functions: 72.6 % 522 379

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

Generated by: LCOV version 2.3