94.12% Lines (32/34)
90.00% Functions (9/10)
| 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_EXECUTOR_FROM_ASIO_HPP | ||||||
| 11 | + | #define BOOST_CAPY_ASIO_EXECUTOR_FROM_ASIO_HPP | ||||||
| 12 | + | |||||||
| 13 | + | #include <boost/capy/asio/detail/asio_context_service.hpp> | ||||||
| 14 | + | #include <boost/capy/asio/detail/asio_coroutine_unique_handle.hpp> | ||||||
| 15 | + | #include <boost/capy/asio/detail/fwd.hpp> | ||||||
| 16 | + | #include <boost/capy/ex/frame_allocator.hpp> | ||||||
| 17 | + | |||||||
| 18 | + | |||||||
| 19 | + | #include <memory_resource> | ||||||
| 20 | + | #include <type_traits> | ||||||
| 21 | + | |||||||
| 22 | + | namespace boost { | ||||||
| 23 | + | namespace capy { | ||||||
| 24 | + | |||||||
| 25 | + | /** @addtogroup asio | ||||||
| 26 | + | * @{ | ||||||
| 27 | + | */ | ||||||
| 28 | + | |||||||
| 29 | + | namespace detail | ||||||
| 30 | + | { | ||||||
| 31 | + | |||||||
| 32 | + | /** @brief Concept for legacy Networking TS style Asio executors. | ||||||
| 33 | + | * @internal | ||||||
| 34 | + | * | ||||||
| 35 | + | * Matches executors that provide the original Networking TS executor interface | ||||||
| 36 | + | * with explicit work counting (`on_work_started`/`on_work_finished`) and | ||||||
| 37 | + | * `dispatch`/`post` methods that take a handler and allocator. | ||||||
| 38 | + | * | ||||||
| 39 | + | * @tparam Executor The type to check | ||||||
| 40 | + | */ | ||||||
| 41 | + | template<typename Executor> | ||||||
| 42 | + | concept AsioNetTsExecutor = requires (Executor exec, | ||||||
| 43 | + | std::coroutine_handle<> h, | ||||||
| 44 | + | std::pmr::polymorphic_allocator<void> a) | ||||||
| 45 | + | { | ||||||
| 46 | + | exec.on_work_started(); | ||||||
| 47 | + | exec.on_work_finished(); | ||||||
| 48 | + | exec.dispatch(h, a); | ||||||
| 49 | + | exec.post(h, a); | ||||||
| 50 | + | exec.context(); | ||||||
| 51 | + | } ; | ||||||
| 52 | + | |||||||
| 53 | + | /** @brief Concept for Boost.Asio standard executors. | ||||||
| 54 | + | * @internal | ||||||
| 55 | + | * | ||||||
| 56 | + | * Matches executors compatible with the P0443/P2300 executor model as | ||||||
| 57 | + | * implemented in Boost.Asio. These executors use the property query/require | ||||||
| 58 | + | * mechanism and return `boost::asio::execution_context&` from context queries. | ||||||
| 59 | + | * | ||||||
| 60 | + | * @tparam Executor The type to check | ||||||
| 61 | + | */ | ||||||
| 62 | + | template<typename Executor> | ||||||
| 63 | + | concept AsioBoostStandardExecutor = std::same_as< | ||||||
| 64 | + | typename boost::asio::query_result< | ||||||
| 65 | + | Executor, | ||||||
| 66 | + | boost::asio::execution::detail::context_t<0>>::type, | ||||||
| 67 | + | boost::asio::execution_context&>; | ||||||
| 68 | + | |||||||
| 69 | + | /** @brief Concept for standalone Asio standard executors. | ||||||
| 70 | + | * @internal | ||||||
| 71 | + | * | ||||||
| 72 | + | * Matches executors compatible with the P0443/P2300 executor model as | ||||||
| 73 | + | * implemented in standalone Asio. These executors return | ||||||
| 74 | + | * `::asio::execution_context&` from context queries. | ||||||
| 75 | + | * | ||||||
| 76 | + | * @tparam Executor The type to check | ||||||
| 77 | + | */ | ||||||
| 78 | + | template<typename Executor> | ||||||
| 79 | + | concept AsioStandaloneStandardExecutor = std::same_as< | ||||||
| 80 | + | typename ::asio::query_result< | ||||||
| 81 | + | Executor, | ||||||
| 82 | + | ::asio::execution::detail::context_t<0>>::type, | ||||||
| 83 | + | ::asio::execution_context&>; | ||||||
| 84 | + | |||||||
| 85 | + | } | ||||||
| 86 | + | |||||||
| 87 | + | |||||||
| 88 | + | /** @brief Wraps a legacy Networking TS executor for use with capy. | ||||||
| 89 | + | * | ||||||
| 90 | + | * This class adapts Asio executors that follow the original Networking TS | ||||||
| 91 | + | * executor model (with `on_work_started`/`on_work_finished` and | ||||||
| 92 | + | * `dispatch`/`post` methods) to be usable as capy executors. | ||||||
| 93 | + | * | ||||||
| 94 | + | * @tparam Executor An executor type satisfying `AsioNetTsExecutor` | ||||||
| 95 | + | * | ||||||
| 96 | + | * @par Example | ||||||
| 97 | + | * @code | ||||||
| 98 | + | * boost::asio::io_context io; | ||||||
| 99 | + | * auto wrapped = asio_net_ts_executor(io.get_executor()); | ||||||
| 100 | + | * | ||||||
| 101 | + | * // Use with capy coroutines | ||||||
| 102 | + | * capy::run(wrapped, my_coroutine()); | ||||||
| 103 | + | * @endcode | ||||||
| 104 | + | * | ||||||
| 105 | + | * @see wrap_asio_executor For automatic executor type detection | ||||||
| 106 | + | */ | ||||||
| 107 | + | template<detail::AsioNetTsExecutor Executor> | ||||||
| 108 | + | struct asio_net_ts_executor | ||||||
| 109 | + | { | ||||||
| 110 | + | /** @brief Constructs from an Asio Net.TS executor. | ||||||
| 111 | + | * @param executor The Asio executor to wrap | ||||||
| 112 | + | */ | ||||||
| HITGNC | 113 | + | 2 | asio_net_ts_executor(Executor executor) | ||||
| 114 | + | noexcept(std::is_nothrow_move_constructible_v<Executor>) | ||||||
| HITGNC | 115 | + | 2 | : executor_(std::move(executor)) | ||||
| 116 | + | { | ||||||
| HITGNC | 117 | + | 2 | } | ||||
| 118 | + | |||||||
| 119 | + | /** @brief Move constructor. */ | ||||||
| HITGNC | 120 | + | 10 | asio_net_ts_executor(asio_net_ts_executor && rhs) | ||||
| 121 | + | noexcept(std::is_nothrow_move_constructible_v<Executor>) | ||||||
| HITGNC | 122 | + | 10 | : executor_(std::move(rhs.executor_)) | ||||
| 123 | + | { | ||||||
| HITGNC | 124 | + | 10 | } | ||||
| 125 | + | |||||||
| 126 | + | /** @brief Copy constructor. */ | ||||||
| HITGNC | 127 | + | 1 | asio_net_ts_executor(const asio_net_ts_executor & rhs) | ||||
| 128 | + | noexcept(std::is_nothrow_copy_constructible_v<Executor>) | ||||||
| HITGNC | 129 | + | 1 | : executor_(rhs.executor_) | ||||
| 130 | + | { | ||||||
| HITGNC | 131 | + | 1 | } | ||||
| 132 | + | |||||||
| 133 | + | /** @brief Returns the associated capy execution context. | ||||||
| 134 | + | * | ||||||
| 135 | + | * The context is obtained via an `asio_context_service` registered | ||||||
| 136 | + | * with the underlying Asio execution context. | ||||||
| 137 | + | * | ||||||
| 138 | + | * @return Reference to the capy execution_context | ||||||
| 139 | + | */ | ||||||
| HITGNC | 140 | + | 6 | execution_context& context() const noexcept | ||||
| 141 | + | { | ||||||
| 142 | + | using ex_t = std::remove_reference_t<decltype(executor_.context())>; | ||||||
| 143 | + | return use_service<detail::asio_context_service<ex_t>> | ||||||
| HITGNC | 144 | + | 6 | ( | ||||
| 145 | + | executor_.context() | ||||||
| HITGNC | 146 | + | 6 | ); | ||||
| 147 | + | } | ||||||
| 148 | + | |||||||
| 149 | + | /** @brief Notifies that work has started. | ||||||
| 150 | + | * | ||||||
| 151 | + | * Forwards to the underlying executor's `on_work_started()`. | ||||||
| 152 | + | */ | ||||||
| HITGNC | 153 | + | 10 | void on_work_started() const noexcept | ||||
| 154 | + | { | ||||||
| HITGNC | 155 | + | 10 | executor_.on_work_started(); | ||||
| HITGNC | 156 | + | 10 | } | ||||
| 157 | + | |||||||
| 158 | + | /** @brief Notifies that work has finished. | ||||||
| 159 | + | * | ||||||
| 160 | + | * Forwards to the underlying executor's `on_work_finished()`. | ||||||
| 161 | + | */ | ||||||
| HITGNC | 162 | + | 12 | void on_work_finished() const noexcept | ||||
| 163 | + | { | ||||||
| HITGNC | 164 | + | 12 | executor_.on_work_finished(); | ||||
| HITGNC | 165 | + | 12 | } | ||||
| 166 | + | |||||||
| 167 | + | /** @brief Dispatches a continuation for execution. | ||||||
| 168 | + | * | ||||||
| 169 | + | * May execute inline if allowed by the executor, otherwise posts. | ||||||
| 170 | + | * | ||||||
| 171 | + | * @param c The continuation to dispatch | ||||||
| 172 | + | * @return A noop coroutine handle (execution is delegated to Asio) | ||||||
| 173 | + | */ | ||||||
| HITGNC | 174 | + | 5 | std::coroutine_handle<> dispatch(continuation & c) const | ||||
| 175 | + | { | ||||||
| HITGNC | 176 | + | 5 | executor_.dispatch( | ||||
| HITGNC | 177 | + | 10 | detail::asio_coroutine_unique_handle(c.h), | ||||
| HITGNC | 178 | + | 5 | std::pmr::polymorphic_allocator<void>( | ||||
| 179 | + | boost::capy::get_current_frame_allocator())); | ||||||
| 180 | + | |||||||
| HITGNC | 181 | + | 5 | return std::noop_coroutine(); | ||||
| 182 | + | } | ||||||
| 183 | + | |||||||
| 184 | + | /** @brief Posts a continuation for deferred execution. | ||||||
| 185 | + | * | ||||||
| 186 | + | * The continuation will never be executed inline. | ||||||
| 187 | + | * | ||||||
| 188 | + | * @param c The continuation to post | ||||||
| 189 | + | */ | ||||||
| HITGNC | 190 | + | 2 | void post(continuation & c) const | ||||
| 191 | + | { | ||||||
| HITGNC | 192 | + | 2 | executor_.post( | ||||
| HITGNC | 193 | + | 4 | detail::asio_coroutine_unique_handle(c.h), | ||||
| HITGNC | 194 | + | 2 | std::pmr::polymorphic_allocator<void>( | ||||
| 195 | + | boost::capy::get_current_frame_allocator())); | ||||||
| HITGNC | 196 | + | 2 | } | ||||
| 197 | + | |||||||
| 198 | + | /** @brief Equality comparison. */ | ||||||
| MISUNC | 199 | + | ✗ | bool operator==(const asio_net_ts_executor & rhs) const noexcept | ||||
| 200 | + | { | ||||||
| MISUNC | 201 | + | ✗ | return executor_ == rhs.executor_; | ||||
| 202 | + | } | ||||||
| 203 | + | |||||||
| 204 | + | /** @brief Inequality comparison. */ | ||||||
| 205 | + | bool operator!=(const asio_net_ts_executor & rhs) const noexcept | ||||||
| 206 | + | { | ||||||
| 207 | + | return executor_ != rhs.executor_; | ||||||
| 208 | + | } | ||||||
| 209 | + | |||||||
| 210 | + | private: | ||||||
| 211 | + | Executor executor_; | ||||||
| 212 | + | }; | ||||||
| 213 | + | |||||||
| 214 | + | |||||||
| 215 | + | /** @brief Wraps a Boost.Asio standard executor for use with capy. | ||||||
| 216 | + | * | ||||||
| 217 | + | * Forward declaration; defined in `boost.hpp`. | ||||||
| 218 | + | * | ||||||
| 219 | + | * @tparam Executor An executor type satisfying `AsioBoostStandardExecutor` | ||||||
| 220 | + | * @see asio_net_ts_executor For legacy executor wrapping | ||||||
| 221 | + | */ | ||||||
| 222 | + | template<detail::AsioBoostStandardExecutor Executor> | ||||||
| 223 | + | struct asio_boost_standard_executor; | ||||||
| 224 | + | |||||||
| 225 | + | /** @brief Wraps a standalone Asio standard executor for use with capy. | ||||||
| 226 | + | * | ||||||
| 227 | + | * Forward declaration; defined in `standalone.hpp`. | ||||||
| 228 | + | * | ||||||
| 229 | + | * @tparam Executor An executor type satisfying `AsioStandaloneStandardExecutor` | ||||||
| 230 | + | * @see asio_net_ts_executor For legacy executor wrapping | ||||||
| 231 | + | */ | ||||||
| 232 | + | template<detail::AsioStandaloneStandardExecutor Executor> | ||||||
| 233 | + | struct asio_standalone_standard_executor; | ||||||
| 234 | + | |||||||
| 235 | + | |||||||
| 236 | + | /** @brief Automatically wraps any Asio executor for use with capy. | ||||||
| 237 | + | * | ||||||
| 238 | + | * This function detects the type of Asio executor and returns the appropriate | ||||||
| 239 | + | * capy-compatible wrapper: | ||||||
| 240 | + | * - Legacy Net.TS executors -> `asio_net_ts_executor` | ||||||
| 241 | + | * - Boost.Asio standard executors -> `asio_boost_standard_executor` | ||||||
| 242 | + | * - Standalone Asio standard executors -> `asio_standalone_standard_executor` | ||||||
| 243 | + | * | ||||||
| 244 | + | * @tparam Executor The Asio executor type (deduced) | ||||||
| 245 | + | * @param exec The Asio executor to wrap | ||||||
| 246 | + | * @return A capy-compatible executor wrapping the input | ||||||
| 247 | + | * | ||||||
| 248 | + | * @par Example | ||||||
| 249 | + | * @code | ||||||
| 250 | + | * boost::asio::io_context io; | ||||||
| 251 | + | * auto capy_exec = wrap_asio_executor(io.get_executor()); | ||||||
| 252 | + | * | ||||||
| 253 | + | * // Now use with capy | ||||||
| 254 | + | * capy::run(capy_exec, my_io_task()); | ||||||
| 255 | + | * @endcode | ||||||
| 256 | + | * | ||||||
| 257 | + | * @note Fails to compile with a static_assert if the executor type is not recognized. | ||||||
| 258 | + | */ | ||||||
| 259 | + | template<typename Executor> | ||||||
| HITGNC | 260 | + | 3 | auto wrap_asio_executor(Executor && exec) | ||||
| 261 | + | { | ||||||
| 262 | + | using executor_t = std::decay_t<Executor>; | ||||||
| 263 | + | if constexpr (detail::AsioNetTsExecutor<executor_t>) | ||||||
| 264 | + | return asio_net_ts_executor<executor_t>( | ||||||
| 265 | + | std::forward<Executor>(exec) | ||||||
| HITGNC | 266 | + | 2 | ); | ||||
| 267 | + | else if constexpr (detail::AsioBoostStandardExecutor<executor_t>) | ||||||
| 268 | + | return asio_boost_standard_executor<executor_t>( | ||||||
| HITGNC | 269 | + | 1 | std::forward<Executor>(exec) | ||||
| HITGNC | 270 | + | 1 | ); | ||||
| 271 | + | else if constexpr (detail::AsioStandaloneStandardExecutor<executor_t>) | ||||||
| 272 | + | return asio_standalone_standard_executor<executor_t>( | ||||||
| 273 | + | std::forward<Executor>(exec) | ||||||
| 274 | + | ); | ||||||
| 275 | + | else | ||||||
| 276 | + | static_assert(sizeof(Executor) == 0, "Unknown executor type"); | ||||||
| 277 | + | }; | ||||||
| 278 | + | |||||||
| 279 | + | |||||||
| 280 | + | /** @brief Type alias for the result of `wrap_asio_executor`. | ||||||
| 281 | + | * | ||||||
| 282 | + | * Given an Asio executor type, this alias yields the corresponding | ||||||
| 283 | + | * capy wrapper type. | ||||||
| 284 | + | * | ||||||
| 285 | + | * @tparam Executor The Asio executor type | ||||||
| 286 | + | */ | ||||||
| 287 | + | template<typename Executor> | ||||||
| 288 | + | using wrap_asio_executor_t | ||||||
| 289 | + | = decltype(wrap_asio_executor(std::declval<const Executor &>())); | ||||||
| 290 | + | |||||||
| 291 | + | /** @} */ // end of asio group | ||||||
| 292 | + | |||||||
| 293 | + | |||||||
| 294 | + | |||||||
| 295 | + | } | ||||||
| 296 | + | } | ||||||
| 297 | + | |||||||
| 298 | + | |||||||
| 299 | + | #endif //BOOST_CAPY_ASIO_EXECUTOR_ADAPTER_HPP | ||||||