LCOV - code coverage report
Current view: top level - capy - task.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 97.2 % 71 69
Test Date: 2026-02-03 03:38:31 Functions: 91.0 % 598 544

            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          896 :     void return_value(T value)
      38              :     {
      39          896 :         result_ = std::move(value);
      40          896 :     }
      41              : 
      42           73 :     T&& result() noexcept
      43              :     {
      44           73 :         return std::move(*result_);
      45              :     }
      46              : };
      47              : 
      48              : template<>
      49              : struct task_return_base<void>
      50              : {
      51          964 :     void return_void()
      52              :     {
      53          964 :     }
      54              : };
      55              : 
      56              : } // namespace detail
      57              : 
      58              : /** Lazy coroutine task satisfying @ref IoLaunchableTask.
      59              : 
      60              :     Use `task<T>` as the return type for coroutines that perform I/O
      61              :     and return a value of type `T`. The coroutine body does not start
      62              :     executing until the task is awaited, enabling efficient composition
      63              :     without unnecessary eager execution.
      64              : 
      65              :     The task participates in the I/O awaitable protocol: when awaited,
      66              :     it receives the caller's executor and stop token, propagating them
      67              :     to nested `co_await` expressions. This enables cancellation and
      68              :     proper completion dispatch across executor boundaries.
      69              : 
      70              :     @tparam T The result type. Use `task<>` for `task<void>`.
      71              : 
      72              :     @par Thread Safety
      73              :     Distinct objects: Safe.
      74              :     Shared objects: Unsafe.
      75              : 
      76              :     @par Example
      77              : 
      78              :     @code
      79              :     task<int> compute_value()
      80              :     {
      81              :         auto [ec, n] = co_await stream.read_some( buf );
      82              :         if( ec.failed() )
      83              :             co_return 0;
      84              :         co_return process( buf, n );
      85              :     }
      86              : 
      87              :     task<> run_session( tcp_socket sock )
      88              :     {
      89              :         int result = co_await compute_value();
      90              :         // ...
      91              :     }
      92              :     @endcode
      93              : 
      94              :     @see IoLaunchableTask, IoAwaitableTask, run, run_async
      95              : */
      96              : template<typename T = void>
      97              : struct [[nodiscard]] BOOST_CAPY_CORO_AWAIT_ELIDABLE
      98              :     task
      99              : {
     100              :     struct promise_type
     101              :         : io_awaitable_support<promise_type>
     102              :         , detail::task_return_base<T>
     103              :     {
     104              :         std::exception_ptr ep_;
     105              : 
     106         2230 :         std::exception_ptr exception() const noexcept
     107              :         {
     108         2230 :             return ep_;
     109              :         }
     110              : 
     111         2956 :         task get_return_object()
     112              :         {
     113         2956 :             return task{std::coroutine_handle<promise_type>::from_promise(*this)};
     114              :         }
     115              : 
     116         2956 :         auto initial_suspend() noexcept
     117              :         {
     118              :             struct awaiter
     119              :             {
     120              :                 promise_type* p_;
     121              : 
     122         2956 :                 bool await_ready() const noexcept
     123              :                 {
     124         2956 :                     return false;
     125              :                 }
     126              : 
     127         2956 :                 void await_suspend(coro) const noexcept
     128              :                 {
     129              :                     // Capture TLS allocator while it's still valid
     130         2956 :                     p_->set_frame_allocator(current_frame_allocator());
     131         2956 :                 }
     132              : 
     133         2954 :                 void await_resume() const noexcept
     134              :                 {
     135              :                     // Restore TLS when body starts executing
     136         2954 :                     if(p_->frame_allocator())
     137         2881 :                         current_frame_allocator() = p_->frame_allocator();
     138         2954 :                 }
     139              :             };
     140         2956 :             return awaiter{this};
     141              :         }
     142              : 
     143         2953 :         auto final_suspend() noexcept
     144              :         {
     145              :             struct awaiter
     146              :             {
     147              :                 promise_type* p_;
     148              : 
     149         2953 :                 bool await_ready() const noexcept
     150              :                 {
     151         2953 :                     return false;
     152              :                 }
     153              : 
     154         2953 :                 coro await_suspend(coro) const noexcept
     155              :                 {
     156         2953 :                     return p_->complete();
     157              :                 }
     158              : 
     159            0 :                 void await_resume() const noexcept
     160              :                 {
     161            0 :                 }
     162              :             };
     163         2953 :             return awaiter{this};
     164              :         }
     165              : 
     166         1093 :         void unhandled_exception()
     167              :         {
     168         1093 :             ep_ = std::current_exception();
     169         1093 :         }
     170              : 
     171              :         template<class Awaitable>
     172              :         struct transform_awaiter
     173              :         {
     174              :             std::decay_t<Awaitable> a_;
     175              :             promise_type* p_;
     176              : 
     177         6876 :             bool await_ready()
     178              :             {
     179         6876 :                 return a_.await_ready();
     180              :             }
     181              : 
     182         6875 :             decltype(auto) await_resume()
     183              :             {
     184              :                 // Restore TLS before body resumes
     185         6875 :                 if(p_->frame_allocator())
     186         6811 :                     current_frame_allocator() = p_->frame_allocator();
     187         6875 :                 return a_.await_resume();
     188              :             }
     189              : 
     190              :             template<class Promise>
     191         1844 :             auto await_suspend(std::coroutine_handle<Promise> h)
     192              :             {
     193         1844 :                 return a_.await_suspend(h, p_->executor(), p_->stop_token());
     194              :             }
     195              :         };
     196              : 
     197              :         template<class Awaitable>
     198         6876 :         auto transform_awaitable(Awaitable&& a)
     199              :         {
     200              :             using A = std::decay_t<Awaitable>;
     201              :             if constexpr (IoAwaitable<A>)
     202              :             {
     203              :                 return transform_awaiter<Awaitable>{
     204         8124 :                     std::forward<Awaitable>(a), this};
     205              :             }
     206              :             else
     207              :             {
     208              :                 static_assert(sizeof(A) == 0, "requires IoAwaitable");
     209              :             }
     210         1248 :         }
     211              :     };
     212              : 
     213              :     std::coroutine_handle<promise_type> h_;
     214              : 
     215              :     /// Destroy the task and its coroutine frame if owned.
     216         6068 :     ~task()
     217              :     {
     218         6068 :         if(h_)
     219         1298 :             h_.destroy();
     220         6068 :     }
     221              : 
     222              :     /// Return false; tasks are never immediately ready.
     223         1170 :     bool await_ready() const noexcept
     224              :     {
     225         1170 :         return false;
     226              :     }
     227              : 
     228              :     /// Return the result or rethrow any stored exception.
     229         1295 :     auto await_resume()
     230              :     {
     231         1295 :         if(h_.promise().ep_)
     232          474 :             std::rethrow_exception(h_.promise().ep_);
     233              :         if constexpr (! std::is_void_v<T>)
     234          806 :             return std::move(*h_.promise().result_);
     235              :         else
     236           15 :             return;
     237              :     }
     238              : 
     239              :     /// Start execution with the caller's context.
     240         1283 :     coro await_suspend(coro cont, executor_ref caller_ex, std::stop_token token)
     241              :     {
     242         1283 :         h_.promise().set_continuation(cont, caller_ex);
     243         1283 :         h_.promise().set_executor(caller_ex);
     244         1283 :         h_.promise().set_stop_token(token);
     245         1283 :         return h_;
     246              :     }
     247              : 
     248              :     /// Return the coroutine handle.
     249         1674 :     std::coroutine_handle<promise_type> handle() const noexcept
     250              :     {
     251         1674 :         return h_;
     252              :     }
     253              : 
     254              :     /** Release ownership of the coroutine frame.
     255              : 
     256              :         After calling this, destroying the task does not destroy the
     257              :         coroutine frame. The caller becomes responsible for the frame's
     258              :         lifetime.
     259              : 
     260              :         @par Postconditions
     261              :         `handle()` returns the original handle, but the task no longer
     262              :         owns it.
     263              :     */
     264         1658 :     void release() noexcept
     265              :     {
     266         1658 :         h_ = nullptr;
     267         1658 :     }
     268              : 
     269              :     task(task const&) = delete;
     270              :     task& operator=(task const&) = delete;
     271              : 
     272              :     /// Move construct, transferring ownership.
     273         3112 :     task(task&& other) noexcept
     274         3112 :         : h_(std::exchange(other.h_, nullptr))
     275              :     {
     276         3112 :     }
     277              : 
     278              :     /// Move assign, transferring ownership.
     279              :     task& operator=(task&& other) noexcept
     280              :     {
     281              :         if(this != &other)
     282              :         {
     283              :             if(h_)
     284              :                 h_.destroy();
     285              :             h_ = std::exchange(other.h_, nullptr);
     286              :         }
     287              :         return *this;
     288              :     }
     289              : 
     290              : private:
     291         2956 :     explicit task(std::coroutine_handle<promise_type> h)
     292         2956 :         : h_(h)
     293              :     {
     294         2956 :     }
     295              : };
     296              : 
     297              : } // namespace capy
     298              : } // namespace boost
     299              : 
     300              : #endif
        

Generated by: LCOV version 2.3