LCOV - code coverage report
Current view: top level - capy/asio/detail - completion_handler.hpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 91.4 % 70 64 6
Test Date: 2026-05-01 02:54:45 Functions: 92.9 % 42 39 3

           TLA  Line data    Source code
       1                 : //
       2                 : // Copyright (c) 2026 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_ASIO_DETAIL_COMPLETION_HANDLER_HPP
      11                 : #define BOOST_CAPY_ASIO_DETAIL_COMPLETION_HANDLER_HPP
      12                 : 
      13                 : #include <boost/capy/asio/detail/asio_coroutine_unique_handle.hpp>
      14                 : #include <boost/capy/asio/executor_adapter.hpp>
      15                 : #include <boost/capy/ex/executor_ref.hpp>
      16                 : #include <boost/capy/ex/io_env.hpp>
      17                 : #include <boost/capy/io_result.hpp>
      18                 : 
      19                 : 
      20                 : #include <memory_resource>
      21                 : #include <optional>
      22                 : #include <system_error>
      23                 : #include <tuple>
      24                 : #include <type_traits>
      25                 : 
      26                 : namespace boost::capy::detail
      27                 : {
      28                 : 
      29                 : struct asio_immediate_executor_helper
      30                 : {
      31                 :   enum completed_immediately_t
      32                 :   {
      33                 :     no, maybe, yes, initiating
      34                 :   };
      35                 : 
      36                 :   executor_ref exec;
      37                 :   completed_immediately_t * completed_immediately = nullptr;
      38                 : 
      39                 :   template<typename Fn>
      40 HIT           2 :   void execute(Fn && fn) const
      41                 :   {
      42                 :     // only allow it when we're still initializing
      43               2 :     if (completed_immediately &&
      44               2 :         ((*completed_immediately == initiating)
      45 MIS           0 :       || (*completed_immediately == maybe)))
      46                 :     {
      47                 :       // only use this indicator if the fn will actually call our handler
      48                 :       // otherwise this was a single op in a composed operation
      49 HIT           2 :       *completed_immediately = maybe;
      50               2 :       fn();
      51                 :       
      52               2 :       if (*completed_immediately != yes)
      53 MIS           0 :         *completed_immediately = initiating;
      54                 :     }
      55                 :     else
      56                 :     {
      57               0 :       exec.post(
      58                 :         make_continuation(
      59                 :           std::forward<Fn>(fn), 
      60               0 :           std::pmr::polymorphic_allocator<void>(
      61               0 :             exec.context().get_frame_allocator())));
      62                 :     }
      63 HIT           2 :   }
      64                 :   
      65                 :   friend bool operator==(const asio_immediate_executor_helper& lhs,
      66                 :                          const asio_immediate_executor_helper& rhs) noexcept
      67                 :   {
      68                 :     return lhs.exec == rhs.exec;
      69                 :   }
      70                 : 
      71                 :   friend bool operator!=(const asio_immediate_executor_helper& lhs,
      72                 :                          const asio_immediate_executor_helper& rhs) noexcept
      73                 :   {
      74                 :     return lhs.exec != rhs.exec;
      75                 :   }
      76                 :   
      77                 :   asio_immediate_executor_helper(
      78                 :       const asio_immediate_executor_helper & rhs) noexcept = default;
      79                 :       
      80               2 :   asio_immediate_executor_helper(
      81                 :       executor_ref inner, 
      82                 :       completed_immediately_t * completed_immediately
      83               2 :       ) : exec(std::move(inner)), completed_immediately(completed_immediately)
      84                 :   {
      85               2 :   }
      86                 : };
      87                 : 
      88                 : 
      89                 : template<typename CancellationSlot, typename ... Args>
      90                 : struct asio_coroutine_completion_handler
      91                 : {
      92                 :   asio_coroutine_unique_handle handle;
      93                 :   std::optional<std::tuple<Args...>> & result;
      94                 :   const capy::io_env * env;
      95                 :   CancellationSlot slot;
      96                 :   using completed_immediately_t = asio_immediate_executor_helper::completed_immediately_t;
      97                 :   
      98                 :   completed_immediately_t * completed_immediately = nullptr;
      99                 :   
     100                 :   using allocator_type = std::pmr::polymorphic_allocator<void>;
     101              10 :   allocator_type get_allocator() const {return env->frame_allocator;}
     102                 : 
     103                 :   using executor_type = asio_executor_adapter<executor_ref>;
     104               5 :   executor_type get_executor() const {return env->executor;}
     105                 : 
     106                 :   using cancellation_slot_type = CancellationSlot;
     107               2 :   cancellation_slot_type get_cancellation_slot() const {return slot;}
     108                 : 
     109                 :   using immediate_executor_type = asio_immediate_executor_helper;
     110               2 :   immediate_executor_type get_immediate_executor() const
     111                 :   {
     112               2 :     return immediate_executor_type{env->executor, completed_immediately };
     113                 :   };
     114                 : 
     115               3 :   asio_coroutine_completion_handler(
     116                 :     std::coroutine_handle<void> h, 
     117                 :     std::optional<std::tuple<Args...>> & result,
     118                 :     const capy::io_env * env,
     119                 :     CancellationSlot slot = {},
     120                 :     completed_immediately_t * ci = nullptr)
     121               3 :     : handle(h)
     122               3 :     , result(result)
     123               3 :     , env(env)
     124               3 :     , slot(slot), completed_immediately(ci)
     125               3 :   {}
     126                 : 
     127              26 :   asio_coroutine_completion_handler(
     128                 :       asio_coroutine_completion_handler &&
     129                 :       ) noexcept = default;
     130                 :       
     131               3 :   void operator()(Args ... args)
     132                 :   {
     133               3 :     result.emplace(std::forward<Args>(args)...);
     134                 : 
     135               3 :     auto h = handle.release();
     136                 : 
     137               3 :     if (completed_immediately != nullptr
     138               3 :     && *completed_immediately == completed_immediately_t::maybe)
     139                 :     {
     140               2 :       *completed_immediately = completed_immediately_t::yes;
     141                 :     }
     142                 :     else
     143               1 :       h();
     144               3 :   }
     145                 : };
     146                 : 
     147                 : template<typename ... Ts>
     148                 : struct async_result_impl_result_tuple
     149                 : {
     150                 :   using type = std::tuple<Ts...>;
     151                 : };
     152                 : 
     153                 : 
     154                 : template<typename T0, typename ... Ts>
     155                 :   requires std::constructible_from<io_result<Ts...>, T0, Ts...>
     156                 : struct async_result_impl_result_tuple<T0, Ts...>
     157                 : {
     158                 :   using type = io_result<Ts...>;
     159                 : };
     160                 : 
     161               1 : inline std::tuple<> make_async_result(const std::tuple<> &) { return {}; }
     162                 : 
     163                 : template<typename E, typename ... Ts>
     164               2 : inline auto make_async_result(std::tuple<E, Ts...> && tup) 
     165                 : {
     166                 :   if constexpr (std::convertible_to<E, std::error_code>) 
     167                 :     return std::apply(
     168               2 :               [](auto &&e, auto &&... args)
     169                 :               {
     170               2 :                   return io_result(std::move(e), std::move(args)...);
     171                 :               },
     172               4 :               std::move(tup));
     173                 :   
     174                 :   else
     175                 :     return std::move(tup);
     176                 : }
     177                 : 
     178                 : 
     179                 : template<typename CancellationSignal, typename CancellationType, typename ... Ts> 
     180                 : struct async_result_impl
     181                 : {
     182                 :     template<typename Initiation, typename... Args>
     183                 :     struct awaitable_t
     184                 :     {
     185                 :         using completed_immediately_t 
     186                 :             = asio_immediate_executor_helper::completed_immediately_t;
     187                 :     
     188                 :         CancellationSignal signal;
     189                 :         completed_immediately_t completed_immediately;
     190                 :         
     191                 :         struct cb
     192                 :         {
     193                 :           CancellationSignal &signal;
     194               3 :           cb(CancellationSignal &signal) : signal(signal) {}
     195 MIS           0 :           void operator()() {signal.emit(CancellationType::terminal); }
     196                 :         };
     197                 :         std::optional<std::stop_callback<cb>> stopper;
     198                 :         
     199 HIT           3 :         bool await_ready() const {return false;}
     200                 : 
     201               3 :         bool await_suspend(std::coroutine_handle<> h, const capy::io_env * env)
     202                 :         {
     203               3 :           completed_immediately = completed_immediately_t::initiating;
     204               3 :           stopper.emplace(env->stop_token, signal);
     205                 :           using slot_t = std::decay_t<decltype(CancellationSignal().slot())>;
     206               3 :           capy::detail::asio_coroutine_completion_handler<slot_t, Ts...> ch(
     207               3 :             h, result_, env, 
     208                 :             signal.slot(), 
     209                 :             &completed_immediately);
     210                 : 
     211               3 :           std::apply(
     212               9 :             [&](auto ... args) 
     213                 :             {
     214               6 :               std::move(init_)(
     215               3 :                 std::move(ch), 
     216               3 :                 std::move(args)...);
     217                 :             }, 
     218               3 :             std::move(args_));
     219                 : 
     220               3 :           if (completed_immediately == completed_immediately_t::initiating)
     221               1 :             completed_immediately = completed_immediately_t::no;
     222               3 :           return completed_immediately != completed_immediately_t::yes;
     223               3 :         }
     224                 : 
     225               3 :         auto await_resume() 
     226                 :         {
     227               3 :           return make_async_result(std::move(*result_)); 
     228                 :         }
     229                 : 
     230                 : 
     231               3 :         awaitable_t(Initiation init, std::tuple<Args...> args) 
     232               3 :               : init_(std::move(init)), args_(std::move(args)) 
     233                 :         {
     234               3 :         }
     235                 :         
     236               1 :         awaitable_t(awaitable_t && rhs) noexcept 
     237               1 :             : init_(std::move(rhs.init_))
     238               1 :             , args_(std::move(rhs.args_))
     239               2 :             , result_(std::move(rhs.result_)) {}
     240                 :       private:
     241                 :         Initiation init_;
     242                 :         // if Args<0> == error_code this needs to be an io_result.
     243                 :         typename async_result_impl_result_tuple<Args...>::type args_;
     244                 :         std::optional<std::tuple<Ts...>> result_;
     245                 :     };
     246                 : 
     247                 :     template <typename Initiation, typename RawToken, typename... Args>
     248               3 :     static auto initiate(Initiation&& initiation,
     249                 :         RawToken&&, Args&&... args)
     250                 :     {
     251                 :       return awaitable_t<
     252                 :             std::decay_t<Initiation>, 
     253                 :             std::decay_t<Args>...>(
     254               2 :             std::forward<Initiation>(initiation),
     255               5 :             std::make_tuple(std::forward<Args>(args)...));
     256                 :     }
     257                 : };
     258                 : 
     259                 : 
     260                 : }
     261                 : 
     262                 : #endif //BOOST_CAPY_ASIO_DETAIL_COMPLETION_HANDLER
     263                 : 
        

Generated by: LCOV version 2.3