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 | + | |||||||