LCOV - code coverage report
Current view: top level - capy - task.hpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 97.4 % 77 75 2
Test Date: 2026-02-17 18:14:47 Functions: 90.9 % 1218 1107 111

           TLA  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 HIT        1259 :     void return_value(T value)
      38                 :     {
      39            1259 :         result_ = std::move(value);
      40            1259 :     }
      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            1865 :     void return_void()
      52                 :     {
      53            1865 :     }
      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            4699 :         promise_type() noexcept
     111            4699 :             : has_ep_(false)
     112                 :         {
     113            4699 :         }
     114                 : 
     115            4699 :         ~promise_type()
     116                 :         {
     117            4699 :             if(has_ep_)
     118            1567 :                 ep_.~exception_ptr();
     119            4699 :         }
     120                 : 
     121            3931 :         std::exception_ptr exception() const noexcept
     122                 :         {
     123            3931 :             if(has_ep_)
     124            2058 :                 return ep_;
     125            1873 :             return {};
     126                 :         }
     127                 : 
     128            4699 :         task get_return_object()
     129                 :         {
     130            4699 :             return task{std::coroutine_handle<promise_type>::from_promise(*this)};
     131                 :         }
     132                 : 
     133            4699 :         auto initial_suspend() noexcept
     134                 :         {
     135                 :             struct awaiter
     136                 :             {
     137                 :                 promise_type* p_;
     138                 : 
     139            4699 :                 bool await_ready() const noexcept
     140                 :                 {
     141            4699 :                     return false;
     142                 :                 }
     143                 : 
     144            4699 :                 void await_suspend(std::coroutine_handle<>) const noexcept
     145                 :                 {
     146            4699 :                 }
     147                 : 
     148            4696 :                 void await_resume() const noexcept
     149                 :                 {
     150                 :                     // Restore TLS when body starts executing
     151            4696 :                     set_current_frame_allocator(p_->environment()->allocator);
     152            4696 :                 }
     153                 :             };
     154            4699 :             return awaiter{this};
     155                 :         }
     156                 : 
     157            4691 :         auto final_suspend() noexcept
     158                 :         {
     159                 :             struct awaiter
     160                 :             {
     161                 :                 promise_type* p_;
     162                 : 
     163            4691 :                 bool await_ready() const noexcept
     164                 :                 {
     165            4691 :                     return false;
     166                 :                 }
     167                 : 
     168            4691 :                 std::coroutine_handle<> await_suspend(std::coroutine_handle<>) const noexcept
     169                 :                 {
     170            4691 :                     return p_->continuation();
     171                 :                 }
     172                 : 
     173 MIS           0 :                 void await_resume() const noexcept
     174                 :                 {
     175               0 :                 }
     176                 :             };
     177 HIT        4691 :             return awaiter{this};
     178                 :         }
     179                 : 
     180            1567 :         void unhandled_exception()
     181                 :         {
     182            1567 :             new (&ep_) std::exception_ptr(std::current_exception());
     183            1567 :             has_ep_ = true;
     184            1567 :         }
     185                 : 
     186                 :         template<class Awaitable>
     187                 :         struct transform_awaiter
     188                 :         {
     189                 :             std::decay_t<Awaitable> a_;
     190                 :             promise_type* p_;
     191                 : 
     192            8603 :             bool await_ready() noexcept
     193                 :             {
     194            8603 :                 return a_.await_ready();
     195                 :             }
     196                 : 
     197            8598 :             decltype(auto) await_resume()
     198                 :             {
     199                 :                 // Restore TLS before body resumes
     200            8598 :                 set_current_frame_allocator(p_->environment()->allocator);
     201            8598 :                 return a_.await_resume();
     202                 :             }
     203                 : 
     204                 :             template<class Promise>
     205            2223 :             auto await_suspend(std::coroutine_handle<Promise> h) noexcept
     206                 :             {
     207            2223 :                 return a_.await_suspend(h, p_->environment());
     208                 :             }
     209                 :         };
     210                 : 
     211                 :         template<class Awaitable>
     212            8603 :         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           10453 :                     std::forward<Awaitable>(a), this};
     219                 :             }
     220                 :             else
     221                 :             {
     222                 :                 static_assert(sizeof(A) == 0, "requires IoAwaitable");
     223                 :             }
     224            1850 :         }
     225                 :     };
     226                 : 
     227                 :     std::coroutine_handle<promise_type> h_;
     228                 : 
     229                 :     /// Destroy the task and its coroutine frame if owned.
     230           10336 :     ~task()
     231                 :     {
     232           10336 :         if(h_)
     233            1735 :             h_.destroy();
     234           10336 :     }
     235                 : 
     236                 :     /// Return false; tasks are never immediately ready.
     237            1607 :     bool await_ready() const noexcept
     238                 :     {
     239            1607 :         return false;
     240                 :     }
     241                 : 
     242                 :     /// Return the result or rethrow any stored exception.
     243            1732 :     auto await_resume()
     244                 :     {
     245            1732 :         if(h_.promise().has_ep_)
     246             537 :             std::rethrow_exception(h_.promise().ep_);
     247                 :         if constexpr (! std::is_void_v<T>)
     248            1114 :             return std::move(*h_.promise().result_);
     249                 :         else
     250              81 :             return;
     251                 :     }
     252                 : 
     253                 :     /// Start execution with the caller's context.
     254            1719 :     std::coroutine_handle<> await_suspend(std::coroutine_handle<> cont, io_env const* env)
     255                 :     {
     256            1719 :         h_.promise().set_continuation(cont);
     257            1719 :         h_.promise().set_environment(env);
     258            1719 :         return h_;
     259                 :     }
     260                 : 
     261                 :     /// Return the coroutine handle.
     262            2980 :     std::coroutine_handle<promise_type> handle() const noexcept
     263                 :     {
     264            2980 :         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            2964 :     void release() noexcept
     278                 :     {
     279            2964 :         h_ = nullptr;
     280            2964 :     }
     281                 : 
     282                 :     task(task const&) = delete;
     283                 :     task& operator=(task const&) = delete;
     284                 : 
     285                 :     /// Move construct, transferring ownership.
     286            5637 :     task(task&& other) noexcept
     287            5637 :         : h_(std::exchange(other.h_, nullptr))
     288                 :     {
     289            5637 :     }
     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            4699 :     explicit task(std::coroutine_handle<promise_type> h)
     305            4699 :         : h_(h)
     306                 :     {
     307            4699 :     }
     308                 : };
     309                 : 
     310                 : } // namespace capy
     311                 : } // namespace boost
     312                 : 
     313                 : #endif
        

Generated by: LCOV version 2.3