LCOV - code coverage report
Current view: top level - capy - io_task.hpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 100.0 % 36 36
Test Date: 2026-06-04 15:25:39 Functions: 97.0 % 66 64 2

           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/capy
       8                 : //
       9                 : 
      10                 : #ifndef BOOST_CAPY_IO_TASK_HPP
      11                 : #define BOOST_CAPY_IO_TASK_HPP
      12                 : 
      13                 : #include <boost/capy/io_result.hpp>
      14                 : #include <boost/capy/task.hpp>
      15                 : #include <coroutine>
      16                 : 
      17                 : namespace boost {
      18                 : namespace capy {
      19                 : 
      20                 : /** A task type for I/O operations yielding io_result.
      21                 : 
      22                 :     This is a convenience alias for `task<io_result<Ts...>>`.
      23                 :     The converting constructor on `io_result<>` allows direct
      24                 :     `co_return` of error codes:
      25                 : 
      26                 :     @code
      27                 :     io_task<> connect_to_server(socket& s, endpoint ep)
      28                 :     {
      29                 :         co_return co_await s.connect(ep);  // returns io_result<>
      30                 :     }
      31                 : 
      32                 :     io_task<> handler(route_params& rp)
      33                 :     {
      34                 :         co_return route::next;  // error_code converts to io_result<>
      35                 :     }
      36                 :     @endcode
      37                 : 
      38                 :     @tparam Ts Additional value types beyond error_code.
      39                 : */
      40                 : template<class... Ts>
      41                 : struct io_task
      42                 : {
      43                 :     struct promise_type : task<io_result<Ts...>>::promise_type
      44                 :     {
      45 HIT        1614 :         io_task get_return_object()
      46                 :         {
      47            1614 :             return io_task{std::coroutine_handle<promise_type>::from_promise(*this)};
      48                 :         }
      49                 : 
      50                 :         friend io_task;
      51                 : 
      52                 :         // An io_task can yield an io_result value. That means the coroutie suspend if the error is set.
      53                 :         template<typename ... Us>
      54                 :             requires (std::constructible_from<Ts> && ...)
      55               6 :         auto yield_value(io_result<Us...> res)
      56                 :         {
      57                 :             struct awaiter
      58                 :             {
      59                 :                 io_result<Us...>  res;
      60               6 :                 bool await_ready() {return !res.ec;}
      61                 : 
      62               2 :                 std::coroutine_handle<> await_suspend(std::coroutine_handle<promise_type> h)
      63                 :                 {
      64               2 :                     auto &p = h.promise();
      65               2 :                     p.return_value({res.ec, Ts()...});
      66                 :                     
      67               2 :                     return p.continuation();
      68                 :                 }
      69                 : 
      70               4 :                 std::tuple<Us...> await_resume() 
      71                 :                 {
      72               4 :                     return std::move(res.values);
      73                 :                 }
      74                 :             };
      75                 : 
      76               6 :             return awaiter{std::move(res)};
      77                 :         }
      78                 :         
      79                 : 
      80                 :     };
      81                 : 
      82                 :     /// Destroy the task and its coroutine frame if owned.
      83                 : 
      84            4056 :     ~io_task()
      85                 :     {
      86            4056 :         if (h_)
      87            1607 :              h_.destroy();
      88            4056 :     }
      89                 :     /// Return false; tasks are never immediately ready.
      90            1481 :     bool await_ready() const noexcept
      91                 :     {
      92            1481 :         return false;
      93                 :     }
      94                 : 
      95                 :     /// Return the result or rethrow any stored exception.
      96            1607 :     auto await_resume()
      97                 :     {
      98            1607 :         if(h_.promise().has_ep_)
      99             533 :             std::rethrow_exception(h_.promise().ep_);
     100            1074 :         return std::move(*h_.promise().result_);
     101                 :     }
     102                 : 
     103                 : 
     104                 : 
     105                 :     /// Start execution with the caller's context.
     106            1607 :     std::coroutine_handle<> await_suspend(std::coroutine_handle<> cont, io_env const* env)
     107                 :     {
     108            1607 :         h_.promise().set_continuation(cont);
     109            1607 :         h_.promise().set_environment(env);
     110            1607 :         return h_;
     111                 :     }
     112                 : 
     113                 :     /** Return the coroutine handle.
     114                 : 
     115                 :         @note Do not call `destroy()` on the returned handle while the
     116                 :         task is being awaited. The task's lifetime is normally managed
     117                 :         by `run_async`, `run`, or the awaiting parent; manually
     118                 :         destroying a suspended task that another coroutine is awaiting
     119                 :         produces undefined behavior. For cooperative cancellation, use
     120                 :         `std::stop_token`.
     121                 : 
     122                 :         @return The coroutine handle.
     123                 :     */
     124               7 :     std::coroutine_handle<promise_type> handle() const noexcept
     125                 :     {
     126               7 :         return h_;
     127                 :     }
     128                 : 
     129                 :     /** Release ownership of the coroutine frame.
     130                 : 
     131                 :         After calling this, destroying the task does not destroy the
     132                 :         coroutine frame. The caller becomes responsible for the frame's
     133                 :         lifetime.
     134                 : 
     135                 :         @note If the caller intends to call `destroy()` on the
     136                 :         released handle, it must do so only when the task has not
     137                 :         started or has fully completed. Destroying a suspended task
     138                 :         that is being awaited produces undefined behavior.
     139                 : 
     140                 :         @par Postconditions
     141                 :         `handle()` returns the original handle, but the task no longer
     142                 :         owns it.
     143                 :     */
     144               7 :     void release() noexcept
     145                 :     {
     146               7 :         h_ = nullptr;
     147               7 :     }
     148                 : 
     149                 :     io_task(io_task const&) = delete;
     150                 :     io_task& operator=(io_task const&) = delete;
     151                 : 
     152                 :     /// Construct by moving, transferring ownership.
     153            2442 :     io_task(io_task&& other) noexcept
     154            2442 :         : h_(std::exchange(other.h_, nullptr))
     155                 :     {
     156            2442 :     }
     157                 : 
     158                 :     /// Assign by moving, transferring ownership.
     159                 :     io_task& operator=(io_task&& other) noexcept
     160                 :     {
     161                 :         if(this != &other)
     162                 :         {
     163                 :             if(h_)
     164                 :                 h_.destroy();
     165                 :             h_ = std::exchange(other.h_, nullptr);
     166                 :         }
     167                 :         return *this;
     168                 :     }
     169                 : 
     170                 : 
     171                 :   private:
     172            1614 :     explicit io_task(std::coroutine_handle<promise_type> h)
     173            1614 :         : h_(h)
     174                 :     {
     175            1614 :     }
     176                 :   
     177                 :     std::coroutine_handle<promise_type> h_;
     178                 : 
     179                 :   
     180                 : };
     181                 : 
     182                 : } // namespace capy
     183                 : } // namespace boost
     184                 : 
     185                 : #endif
        

Generated by: LCOV version 2.3