91.43% Lines (64/70) 95.24% Functions (20/21)
TLA Baseline Branch
Line Hits Code Line Hits 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>
HITGNC   40 + 2 void execute(Fn && fn) const
  41 + {
  42 + // only allow it when we're still initializing
HITGNC   43 + 2 if (completed_immediately &&
HITGNC   44 + 2 ((*completed_immediately == initiating)
MISUNC   45 + || (*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
HITGNC   49 + 2 *completed_immediately = maybe;
HITGNC   50 + 2 fn();
  51 +
HITGNC   52 + 2 if (*completed_immediately != yes)
MISUNC   53 + *completed_immediately = initiating;
  54 + }
  55 + else
  56 + {
MISUNC   57 + exec.post(
  58 + make_continuation(
  59 + std::forward<Fn>(fn),
MISUNC   60 + std::pmr::polymorphic_allocator<void>(
MISUNC   61 + exec.context().get_frame_allocator())));
  62 + }
HITGNC   63 + 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 +
HITGNC   80 + 2 asio_immediate_executor_helper(
  81 + executor_ref inner,
  82 + completed_immediately_t * completed_immediately
HITGNC   83 + 2 ) : exec(std::move(inner)), completed_immediately(completed_immediately)
  84 + {
HITGNC   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>;
HITGNC   101 + 10 allocator_type get_allocator() const {return env->frame_allocator;}
  102 +
  103 + using executor_type = asio_executor_adapter<executor_ref>;
HITGNC   104 + 5 executor_type get_executor() const {return env->executor;}
  105 +
  106 + using cancellation_slot_type = CancellationSlot;
HITGNC   107 + 2 cancellation_slot_type get_cancellation_slot() const {return slot;}
  108 +
  109 + using immediate_executor_type = asio_immediate_executor_helper;
HITGNC   110 + 2 immediate_executor_type get_immediate_executor() const
  111 + {
HITGNC   112 + 2 return immediate_executor_type{env->executor, completed_immediately };
  113 + };
  114 +
HITGNC   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)
HITGNC   121 + 3 : handle(h)
HITGNC   122 + 3 , result(result)
HITGNC   123 + 3 , env(env)
HITGNC   124 + 3 , slot(slot), completed_immediately(ci)
HITGNC   125 + 3 {}
  126 +
HITGNC   127 + 26 asio_coroutine_completion_handler(
  128 + asio_coroutine_completion_handler &&
  129 + ) noexcept = default;
  130 +
HITGNC   131 + 3 void operator()(Args ... args)
  132 + {
HITGNC   133 + 3 result.emplace(std::forward<Args>(args)...);
  134 +
HITGNC   135 + 3 auto h = handle.release();
  136 +
HITGNC   137 + 3 if (completed_immediately != nullptr
HITGNC   138 + 3 && *completed_immediately == completed_immediately_t::maybe)
  139 + {
HITGNC   140 + 2 *completed_immediately = completed_immediately_t::yes;
  141 + }
  142 + else
HITGNC   143 + 1 h();
HITGNC   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 +
HITGNC   161 + 1 inline std::tuple<> make_async_result(const std::tuple<> &) { return {}; }
  162 +
  163 + template<typename E, typename ... Ts>
HITGNC   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(
HITGNC   168 + 2 [](auto &&e, auto &&... args)
  169 + {
HITGNC   170 + 2 return io_result(std::move(e), std::move(args)...);
  171 + },
HITGNC   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;
HITGNC   194 + 3 cb(CancellationSignal &signal) : signal(signal) {}
MISUNC   195 + void operator()() {signal.emit(CancellationType::terminal); }
  196 + };
  197 + std::optional<std::stop_callback<cb>> stopper;
  198 +
HITGNC   199 + 3 bool await_ready() const {return false;}
  200 +
HITGNC   201 + 3 bool await_suspend(std::coroutine_handle<> h, const capy::io_env * env)
  202 + {
HITGNC   203 + 3 completed_immediately = completed_immediately_t::initiating;
HITGNC   204 + 3 stopper.emplace(env->stop_token, signal);
  205 + using slot_t = std::decay_t<decltype(CancellationSignal().slot())>;
HITGNC   206 + 3 capy::detail::asio_coroutine_completion_handler<slot_t, Ts...> ch(
HITGNC   207 + 3 h, result_, env,
  208 + signal.slot(),
  209 + &completed_immediately);
  210 +
HITGNC   211 + 3 std::apply(
HITGNC   212 + 9 [&](auto ... args)
  213 + {
HITGNC   214 + 6 std::move(init_)(
HITGNC   215 + 3 std::move(ch),
HITGNC   216 + 3 std::move(args)...);
  217 + },
HITGNC   218 + 3 std::move(args_));
  219 +
HITGNC   220 + 3 if (completed_immediately == completed_immediately_t::initiating)
HITGNC   221 + 1 completed_immediately = completed_immediately_t::no;
HITGNC   222 + 3 return completed_immediately != completed_immediately_t::yes;
HITGNC   223 + 3 }
  224 +
HITGNC   225 + 3 auto await_resume()
  226 + {
HITGNC   227 + 3 return make_async_result(std::move(*result_));
  228 + }
  229 +
  230 +
HITGNC   231 + 3 awaitable_t(Initiation init, std::tuple<Args...> args)
HITGNC   232 + 3 : init_(std::move(init)), args_(std::move(args))
  233 + {
HITGNC   234 + 3 }
  235 +
HITGNC   236 + 1 awaitable_t(awaitable_t && rhs) noexcept
HITGNC   237 + 1 : init_(std::move(rhs.init_))
HITGNC   238 + 1 , args_(std::move(rhs.args_))
HITGNC   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>
HITGNC   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>...>(
HITGNC   254 + 2 std::forward<Initiation>(initiation),
HITGNC   255 + 5 std::make_tuple(std::forward<Args>(args)...));
  256 + }
  257 + };
  258 +
  259 +
  260 + }
  261 +
  262 + #endif //BOOST_CAPY_ASIO_DETAIL_COMPLETION_HANDLER
  263 +