LCOV - code coverage report
Current view: top level - capy - task.hpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 96.2 % 78 75 3
Test Date: 2026-03-04 22:59:25 Functions: 90.9 % 1336 1215 121

           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                 : #include <boost/capy/detail/await_suspend_helper.hpp>
      20                 : 
      21                 : #include <exception>
      22                 : #include <optional>
      23                 : #include <type_traits>
      24                 : #include <utility>
      25                 : #include <variant>
      26                 : 
      27                 : namespace boost {
      28                 : namespace capy {
      29                 : 
      30                 : namespace detail {
      31                 : 
      32                 : // Helper base for result storage and return_void/return_value
      33                 : template<typename T>
      34                 : struct task_return_base
      35                 : {
      36                 :     std::optional<T> result_;
      37                 : 
      38 HIT        1263 :     void return_value(T value)
      39                 :     {
      40            1263 :         result_ = std::move(value);
      41            1263 :     }
      42                 : 
      43             144 :     T&& result() noexcept
      44                 :     {
      45             144 :         return std::move(*result_);
      46                 :     }
      47                 : };
      48                 : 
      49                 : template<>
      50                 : struct task_return_base<void>
      51                 : {
      52            1869 :     void return_void()
      53                 :     {
      54            1869 :     }
      55                 : };
      56                 : 
      57                 : } // namespace detail
      58                 : 
      59                 : /** Lazy coroutine task satisfying @ref IoRunnable.
      60                 : 
      61                 :     Use `task<T>` as the return type for coroutines that perform I/O
      62                 :     and return a value of type `T`. The coroutine body does not start
      63                 :     executing until the task is awaited, enabling efficient composition
      64                 :     without unnecessary eager execution.
      65                 : 
      66                 :     The task participates in the I/O awaitable protocol: when awaited,
      67                 :     it receives the caller's executor and stop token, propagating them
      68                 :     to nested `co_await` expressions. This enables cancellation and
      69                 :     proper completion dispatch across executor boundaries.
      70                 : 
      71                 :     @tparam T The result type. Use `task<>` for `task<void>`.
      72                 : 
      73                 :     @par Thread Safety
      74                 :     Distinct objects: Safe.
      75                 :     Shared objects: Unsafe.
      76                 : 
      77                 :     @par Example
      78                 : 
      79                 :     @code
      80                 :     task<int> compute_value()
      81                 :     {
      82                 :         auto [ec, n] = co_await stream.read_some( buf );
      83                 :         if( ec )
      84                 :             co_return 0;
      85                 :         co_return process( buf, n );
      86                 :     }
      87                 : 
      88                 :     task<> run_session( tcp_socket sock )
      89                 :     {
      90                 :         int result = co_await compute_value();
      91                 :         // ...
      92                 :     }
      93                 :     @endcode
      94                 : 
      95                 :     @see IoRunnable, IoAwaitable, run, run_async
      96                 : */
      97                 : template<typename T = void>
      98                 : struct [[nodiscard]] BOOST_CAPY_CORO_AWAIT_ELIDABLE
      99                 :     task
     100                 : {
     101                 :     struct promise_type
     102                 :         : io_awaitable_promise_base<promise_type>
     103                 :         , detail::task_return_base<T>
     104                 :     {
     105                 :     private:
     106                 :         friend task;
     107                 :         union { std::exception_ptr ep_; };
     108                 :         bool has_ep_;
     109                 : 
     110                 :     public:
     111            4707 :         promise_type() noexcept
     112            4707 :             : has_ep_(false)
     113                 :         {
     114            4707 :         }
     115                 : 
     116            4707 :         ~promise_type()
     117                 :         {
     118            4707 :             if(has_ep_)
     119            1567 :                 ep_.~exception_ptr();
     120            4707 :         }
     121                 : 
     122            3933 :         std::exception_ptr exception() const noexcept
     123                 :         {
     124            3933 :             if(has_ep_)
     125            2058 :                 return ep_;
     126            1875 :             return {};
     127                 :         }
     128                 : 
     129            4707 :         task get_return_object()
     130                 :         {
     131            4707 :             return task{std::coroutine_handle<promise_type>::from_promise(*this)};
     132                 :         }
     133                 : 
     134            4707 :         auto initial_suspend() noexcept
     135                 :         {
     136                 :             struct awaiter
     137                 :             {
     138                 :                 promise_type* p_;
     139                 : 
     140            4707 :                 bool await_ready() const noexcept
     141                 :                 {
     142            4707 :                     return false;
     143                 :                 }
     144                 : 
     145            4707 :                 void await_suspend(std::coroutine_handle<>) const noexcept
     146                 :                 {
     147            4707 :                 }
     148                 : 
     149            4704 :                 void await_resume() const noexcept
     150                 :                 {
     151                 :                     // Restore TLS when body starts executing
     152            4704 :                     set_current_frame_allocator(p_->environment()->frame_allocator);
     153            4704 :                 }
     154                 :             };
     155            4707 :             return awaiter{this};
     156                 :         }
     157                 : 
     158            4699 :         auto final_suspend() noexcept
     159                 :         {
     160                 :             struct awaiter
     161                 :             {
     162                 :                 promise_type* p_;
     163                 : 
     164            4699 :                 bool await_ready() const noexcept
     165                 :                 {
     166            4699 :                     return false;
     167                 :                 }
     168                 : 
     169            4699 :                 std::coroutine_handle<> await_suspend(std::coroutine_handle<>) const noexcept
     170                 :                 {
     171            4699 :                     return p_->continuation();
     172                 :                 }
     173                 : 
     174 MIS           0 :                 void await_resume() const noexcept
     175                 :                 {
     176               0 :                 }
     177                 :             };
     178 HIT        4699 :             return awaiter{this};
     179                 :         }
     180                 : 
     181            1567 :         void unhandled_exception()
     182                 :         {
     183            1567 :             new (&ep_) std::exception_ptr(std::current_exception());
     184            1567 :             has_ep_ = true;
     185            1567 :         }
     186                 : 
     187                 :         template<class Awaitable>
     188                 :         struct transform_awaiter
     189                 :         {
     190                 :             std::decay_t<Awaitable> a_;
     191                 :             promise_type* p_;
     192                 : 
     193            8607 :             bool await_ready() noexcept
     194                 :             {
     195            8607 :                 return a_.await_ready();
     196                 :             }
     197                 : 
     198            8602 :             decltype(auto) await_resume()
     199                 :             {
     200                 :                 // Restore TLS before body resumes
     201            8602 :                 set_current_frame_allocator(p_->environment()->frame_allocator);
     202            8602 :                 return a_.await_resume();
     203                 :             }
     204                 : 
     205                 :             template<class Promise>
     206            2227 :             auto await_suspend(std::coroutine_handle<Promise> h) noexcept
     207                 :             {
     208                 :                 using R = decltype(a_.await_suspend(h, p_->environment()));
     209                 :                 if constexpr (std::is_same_v<R, std::coroutine_handle<>>)
     210            2227 :                     return detail::symmetric_transfer(a_.await_suspend(h, p_->environment()));
     211                 :                 else
     212 MIS           0 :                     return a_.await_suspend(h, p_->environment());
     213                 :             }
     214                 :         };
     215                 : 
     216                 :         template<class Awaitable>
     217 HIT        8607 :         auto transform_awaitable(Awaitable&& a)
     218                 :         {
     219                 :             using A = std::decay_t<Awaitable>;
     220                 :             if constexpr (IoAwaitable<A>)
     221                 :             {
     222                 :                 return transform_awaiter<Awaitable>{
     223           10459 :                     std::forward<Awaitable>(a), this};
     224                 :             }
     225                 :             else
     226                 :             {
     227                 :                 static_assert(sizeof(A) == 0, "requires IoAwaitable");
     228                 :             }
     229            1852 :         }
     230                 :     };
     231                 : 
     232                 :     std::coroutine_handle<promise_type> h_;
     233                 : 
     234                 :     /// Destroy the task and its coroutine frame if owned.
     235           10366 :     ~task()
     236                 :     {
     237           10366 :         if(h_)
     238            1741 :             h_.destroy();
     239           10366 :     }
     240                 : 
     241                 :     /// Return false; tasks are never immediately ready.
     242            1613 :     bool await_ready() const noexcept
     243                 :     {
     244            1613 :         return false;
     245                 :     }
     246                 : 
     247                 :     /// Return the result or rethrow any stored exception.
     248            1738 :     auto await_resume()
     249                 :     {
     250            1738 :         if(h_.promise().has_ep_)
     251             537 :             std::rethrow_exception(h_.promise().ep_);
     252                 :         if constexpr (! std::is_void_v<T>)
     253            1117 :             return std::move(*h_.promise().result_);
     254                 :         else
     255              84 :             return;
     256                 :     }
     257                 : 
     258                 :     /// Start execution with the caller's context.
     259            1725 :     std::coroutine_handle<> await_suspend(std::coroutine_handle<> cont, io_env const* env)
     260                 :     {
     261            1725 :         h_.promise().set_continuation(cont);
     262            1725 :         h_.promise().set_environment(env);
     263            1725 :         return h_;
     264                 :     }
     265                 : 
     266                 :     /// Return the coroutine handle.
     267            2982 :     std::coroutine_handle<promise_type> handle() const noexcept
     268                 :     {
     269            2982 :         return h_;
     270                 :     }
     271                 : 
     272                 :     /** Release ownership of the coroutine frame.
     273                 : 
     274                 :         After calling this, destroying the task does not destroy the
     275                 :         coroutine frame. The caller becomes responsible for the frame's
     276                 :         lifetime.
     277                 : 
     278                 :         @par Postconditions
     279                 :         `handle()` returns the original handle, but the task no longer
     280                 :         owns it.
     281                 :     */
     282            2966 :     void release() noexcept
     283                 :     {
     284            2966 :         h_ = nullptr;
     285            2966 :     }
     286                 : 
     287                 :     task(task const&) = delete;
     288                 :     task& operator=(task const&) = delete;
     289                 : 
     290                 :     /// Move construct, transferring ownership.
     291            5659 :     task(task&& other) noexcept
     292            5659 :         : h_(std::exchange(other.h_, nullptr))
     293                 :     {
     294            5659 :     }
     295                 : 
     296                 :     /// Move assign, transferring ownership.
     297                 :     task& operator=(task&& other) noexcept
     298                 :     {
     299                 :         if(this != &other)
     300                 :         {
     301                 :             if(h_)
     302                 :                 h_.destroy();
     303                 :             h_ = std::exchange(other.h_, nullptr);
     304                 :         }
     305                 :         return *this;
     306                 :     }
     307                 : 
     308                 : private:
     309            4707 :     explicit task(std::coroutine_handle<promise_type> h)
     310            4707 :         : h_(h)
     311                 :     {
     312            4707 :     }
     313                 : };
     314                 : 
     315                 : } // namespace capy
     316                 : } // namespace boost
     317                 : 
     318                 : #endif
        

Generated by: LCOV version 2.3