LCOV - code coverage report
Current view: top level - capy - task.hpp (source / functions) Coverage Total Hit
Test: coverage_remapped.info Lines: 97.4 % 77 75
Test Date: 2026-02-13 23:13:33 Functions: 91.8 % 1138 1045

            Line data    Source code
       1              : //
       2              : // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.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_promise_base.hpp>
      17              : #include <boost/capy/ex/io_env.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         1230 :     void return_value(T value)
      38              :     {
      39         1230 :         result_ = std::move(value);
      40         1230 :     }
      41              : 
      42          143 :     T&& result() noexcept
      43              :     {
      44          143 :         return std::move(*result_);
      45              :     }
      46              : };
      47              : 
      48              : template<>
      49              : struct task_return_base<void>
      50              : {
      51         1245 :     void return_void()
      52              :     {
      53         1245 :     }
      54              : };
      55              : 
      56              : } // namespace detail
      57              : 
      58              : /** Lazy coroutine task satisfying @ref IoRunnable.
      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 )
      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 IoRunnable, IoAwaitable, 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_promise_base<promise_type>
     102              :         , detail::task_return_base<T>
     103              :     {
     104              :     private:
     105              :         friend task;
     106              :         union { std::exception_ptr ep_; };
     107              :         bool has_ep_;
     108              : 
     109              :     public:
     110         3721 :         promise_type() noexcept
     111         3721 :             : has_ep_(false)
     112              :         {
     113         3721 :         }
     114              : 
     115         3721 :         ~promise_type()
     116              :         {
     117         3721 :             if(has_ep_)
     118         1238 :                 ep_.~exception_ptr();
     119         3721 :         }
     120              : 
     121         2764 :         std::exception_ptr exception() const noexcept
     122              :         {
     123         2764 :             if(has_ep_)
     124         1454 :                 return ep_;
     125         1310 :             return {};
     126              :         }
     127              : 
     128         3721 :         task get_return_object()
     129              :         {
     130         3721 :             return task{std::coroutine_handle<promise_type>::from_promise(*this)};
     131              :         }
     132              : 
     133         3721 :         auto initial_suspend() noexcept
     134              :         {
     135              :             struct awaiter
     136              :             {
     137              :                 promise_type* p_;
     138              : 
     139         3721 :                 bool await_ready() const noexcept
     140              :                 {
     141         3721 :                     return false;
     142              :                 }
     143              : 
     144         3721 :                 void await_suspend(std::coroutine_handle<>) const noexcept
     145              :                 {
     146         3721 :                 }
     147              : 
     148         3718 :                 void await_resume() const noexcept
     149              :                 {
     150              :                     // Restore TLS when body starts executing
     151         3718 :                     set_current_frame_allocator(p_->environment()->allocator);
     152         3718 :                 }
     153              :             };
     154         3721 :             return awaiter{this};
     155              :         }
     156              : 
     157         3713 :         auto final_suspend() noexcept
     158              :         {
     159              :             struct awaiter
     160              :             {
     161              :                 promise_type* p_;
     162              : 
     163         3713 :                 bool await_ready() const noexcept
     164              :                 {
     165         3713 :                     return false;
     166              :                 }
     167              : 
     168         3713 :                 std::coroutine_handle<> await_suspend(std::coroutine_handle<>) const noexcept
     169              :                 {
     170         3713 :                     return p_->continuation();
     171              :                 }
     172              : 
     173            0 :                 void await_resume() const noexcept
     174              :                 {
     175            0 :                 }
     176              :             };
     177         3713 :             return awaiter{this};
     178              :         }
     179              : 
     180         1238 :         void unhandled_exception()
     181              :         {
     182         1238 :             new (&ep_) std::exception_ptr(std::current_exception());
     183         1238 :             has_ep_ = true;
     184         1238 :         }
     185              : 
     186              :         template<class Awaitable>
     187              :         struct transform_awaiter
     188              :         {
     189              :             std::decay_t<Awaitable> a_;
     190              :             promise_type* p_;
     191              : 
     192         7270 :             bool await_ready() noexcept
     193              :             {
     194         7270 :                 return a_.await_ready();
     195              :             }
     196              : 
     197         7265 :             decltype(auto) await_resume()
     198              :             {
     199              :                 // Restore TLS before body resumes
     200         7265 :                 set_current_frame_allocator(p_->environment()->allocator);
     201         7265 :                 return a_.await_resume();
     202              :             }
     203              : 
     204              :             template<class Promise>
     205         2110 :             auto await_suspend(std::coroutine_handle<Promise> h) noexcept
     206              :             {
     207         2110 :                 return a_.await_suspend(h, p_->environment());
     208              :             }
     209              :         };
     210              : 
     211              :         template<class Awaitable>
     212         7270 :         auto transform_awaitable(Awaitable&& a)
     213              :         {
     214              :             using A = std::decay_t<Awaitable>;
     215              :             if constexpr (IoAwaitable<A>)
     216              :             {
     217              :                 return transform_awaiter<Awaitable>{
     218         9057 :                     std::forward<Awaitable>(a), this};
     219              :             }
     220              :             else
     221              :             {
     222              :                 static_assert(sizeof(A) == 0, "requires IoAwaitable");
     223              :             }
     224         1787 :         }
     225              :     };
     226              : 
     227              :     std::coroutine_handle<promise_type> h_;
     228              : 
     229              :     /// Destroy the task and its coroutine frame if owned.
     230         8180 :     ~task()
     231              :     {
     232         8180 :         if(h_)
     233         1622 :             h_.destroy();
     234         8180 :     }
     235              : 
     236              :     /// Return false; tasks are never immediately ready.
     237         1494 :     bool await_ready() const noexcept
     238              :     {
     239         1494 :         return false;
     240              :     }
     241              : 
     242              :     /// Return the result or rethrow any stored exception.
     243         1619 :     auto await_resume()
     244              :     {
     245         1619 :         if(h_.promise().has_ep_)
     246          510 :             std::rethrow_exception(h_.promise().ep_);
     247              :         if constexpr (! std::is_void_v<T>)
     248         1085 :             return std::move(*h_.promise().result_);
     249              :         else
     250           24 :             return;
     251              :     }
     252              : 
     253              :     /// Start execution with the caller's context.
     254         1606 :     std::coroutine_handle<> await_suspend(std::coroutine_handle<> cont, io_env const* env)
     255              :     {
     256         1606 :         h_.promise().set_continuation(cont);
     257         1606 :         h_.promise().set_environment(env);
     258         1606 :         return h_;
     259              :     }
     260              : 
     261              :     /// Return the coroutine handle.
     262         2115 :     std::coroutine_handle<promise_type> handle() const noexcept
     263              :     {
     264         2115 :         return h_;
     265              :     }
     266              : 
     267              :     /** Release ownership of the coroutine frame.
     268              : 
     269              :         After calling this, destroying the task does not destroy the
     270              :         coroutine frame. The caller becomes responsible for the frame's
     271              :         lifetime.
     272              : 
     273              :         @par Postconditions
     274              :         `handle()` returns the original handle, but the task no longer
     275              :         owns it.
     276              :     */
     277         2099 :     void release() noexcept
     278              :     {
     279         2099 :         h_ = nullptr;
     280         2099 :     }
     281              : 
     282              :     task(task const&) = delete;
     283              :     task& operator=(task const&) = delete;
     284              : 
     285              :     /// Move construct, transferring ownership.
     286         4459 :     task(task&& other) noexcept
     287         4459 :         : h_(std::exchange(other.h_, nullptr))
     288              :     {
     289         4459 :     }
     290              : 
     291              :     /// Move assign, transferring ownership.
     292              :     task& operator=(task&& other) noexcept
     293              :     {
     294              :         if(this != &other)
     295              :         {
     296              :             if(h_)
     297              :                 h_.destroy();
     298              :             h_ = std::exchange(other.h_, nullptr);
     299              :         }
     300              :         return *this;
     301              :     }
     302              : 
     303              : private:
     304         3721 :     explicit task(std::coroutine_handle<promise_type> h)
     305         3721 :         : h_(h)
     306              :     {
     307         3721 :     }
     308              : };
     309              : 
     310              : } // namespace capy
     311              : } // namespace boost
     312              : 
     313              : #endif
        

Generated by: LCOV version 2.3