90.91% Lines (30/33)
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_ADAPTER_HPP | ||||||
| 11 | + | #define BOOST_CAPY_ASIO_EXECUTOR_ADAPTER_HPP | ||||||
| 12 | + | |||||||
| 13 | + | #include <boost/capy/asio/detail/continuation.hpp> | ||||||
| 14 | + | #include <boost/capy/ex/any_executor.hpp> | ||||||
| 15 | + | #include <boost/capy/ex/execution_context.hpp> | ||||||
| 16 | + | #include <memory_resource> | ||||||
| 17 | + | |||||||
| 18 | + | |||||||
| 19 | + | namespace boost { | ||||||
| 20 | + | namespace capy { | ||||||
| 21 | + | |||||||
| 22 | + | /** @addtogroup asio | ||||||
| 23 | + | * @{ | ||||||
| 24 | + | */ | ||||||
| 25 | + | |||||||
| 26 | + | namespace detail | ||||||
| 27 | + | { | ||||||
| 28 | + | |||||||
| 29 | + | /** @brief Service that bridges capy's execution_context with Asio's. | ||||||
| 30 | + | * @internal | ||||||
| 31 | + | * | ||||||
| 32 | + | * This service inherits from both capy's service and the target Asio | ||||||
| 33 | + | * execution context, allowing capy executors wrapped in `asio_executor_adapter` | ||||||
| 34 | + | * to be queried for their Asio execution context. | ||||||
| 35 | + | * | ||||||
| 36 | + | * @tparam ExecutionContext The Asio execution_context type (boost::asio or standalone) | ||||||
| 37 | + | */ | ||||||
| 38 | + | template<typename ExecutionContext> | ||||||
| 39 | + | struct asio_adapter_context_service | ||||||
| 40 | + | : execution_context::service, | ||||||
| 41 | + | // shutdown is protected | ||||||
| 42 | + | ExecutionContext | ||||||
| 43 | + | { | ||||||
| HITGNC | 44 | + | 1 | asio_adapter_context_service(boost::capy::execution_context &) {} | ||||
| HITGNC | 45 | + | 1 | void shutdown() override {ExecutionContext::shutdown();} | ||||
| 46 | + | }; | ||||||
| 47 | + | |||||||
| 48 | + | } | ||||||
| 49 | + | |||||||
| 50 | + | |||||||
| 51 | + | /** @brief Adapts a capy executor to be usable with Asio. | ||||||
| 52 | + | * | ||||||
| 53 | + | * `asio_executor_adapter` wraps a capy executor and exposes it as an | ||||||
| 54 | + | * Asio-compatible executor. This allows capy coroutines and executors to | ||||||
| 55 | + | * interoperate seamlessly with Asio's async operations. | ||||||
| 56 | + | * | ||||||
| 57 | + | * The adapter tracks execution properties (blocking behavior and work tracking) | ||||||
| 58 | + | * as compile-time template parameters for zero-overhead property queries. | ||||||
| 59 | + | * | ||||||
| 60 | + | * @tparam Executor The underlying capy executor type (default: `capy::any_executor`) | ||||||
| 61 | + | * @tparam Allocator The allocator type for handler allocation | ||||||
| 62 | + | * (default: `std::pmr::polymorphic_allocator<void>`) | ||||||
| 63 | + | * @tparam Bits Compile-time bitfield encoding blocking and work-tracking properties | ||||||
| 64 | + | * | ||||||
| 65 | + | * @par Execution Properties | ||||||
| 66 | + | * The adapter supports the standard Asio execution properties: | ||||||
| 67 | + | * - `blocking`: `possibly` (default), `never`, or `always` | ||||||
| 68 | + | * - `outstanding_work`: `untracked` (default) or `tracked` | ||||||
| 69 | + | * - `allocator`: Custom allocator for handler allocation | ||||||
| 70 | + | * - `context`: Returns the associated capy execution_context | ||||||
| 71 | + | * | ||||||
| 72 | + | * @par Example | ||||||
| 73 | + | * @code | ||||||
| 74 | + | * // Wrap a capy executor for use with Asio | ||||||
| 75 | + | * capy::any_executor capy_exec = ...; | ||||||
| 76 | + | * asio_executor_adapter<> asio_exec(capy_exec); | ||||||
| 77 | + | * | ||||||
| 78 | + | * // Use with Asio operations | ||||||
| 79 | + | * asio::post(asio_exec, []{ std::cout << "Hello from capy!\n"; }); | ||||||
| 80 | + | * | ||||||
| 81 | + | * // Require non-blocking execution | ||||||
| 82 | + | * auto never_blocking = asio::require(asio_exec, | ||||||
| 83 | + | * asio::execution::blocking.never); | ||||||
| 84 | + | * @endcode | ||||||
| 85 | + | * | ||||||
| 86 | + | * @see wrap_asio_executor For the reverse direction (Asio to capy) | ||||||
| 87 | + | */ | ||||||
| 88 | + | template<typename Executor = capy::any_executor, | ||||||
| 89 | + | typename Allocator = std::pmr::polymorphic_allocator<void>, | ||||||
| 90 | + | int Bits = 0> | ||||||
| 91 | + | struct asio_executor_adapter | ||||||
| 92 | + | { | ||||||
| 93 | + | /// @name Blocking Property Constants | ||||||
| 94 | + | /// @{ | ||||||
| 95 | + | constexpr static int blocking_possibly = 0b000; ///< May block the caller | ||||||
| 96 | + | constexpr static int blocking_never = 0b001; ///< Never blocks the caller | ||||||
| 97 | + | constexpr static int blocking_always = 0b010; ///< Always blocks until complete | ||||||
| 98 | + | constexpr static int blocking_mask = 0b011; ///< Mask for blocking bits | ||||||
| 99 | + | /// @} | ||||||
| 100 | + | |||||||
| 101 | + | /// @name Work Tracking Property Constants | ||||||
| 102 | + | /// @{ | ||||||
| 103 | + | constexpr static int work_untracked = 0b000; ///< Work is not tracked | ||||||
| 104 | + | constexpr static int work_tracked = 0b100; ///< Outstanding work is tracked | ||||||
| 105 | + | constexpr static int work_mask = 0b100; ///< Mask for work tracking bit | ||||||
| 106 | + | /// @} | ||||||
| 107 | + | |||||||
| 108 | + | |||||||
| 109 | + | /// @name Constructors | ||||||
| 110 | + | /// @{ | ||||||
| 111 | + | |||||||
| 112 | + | /** @brief Copy constructor from adapter with different property bits. | ||||||
| 113 | + | * | ||||||
| 114 | + | * Creates a copy with potentially different execution properties. | ||||||
| 115 | + | * If this adapter tracks work, `on_work_started()` is called. | ||||||
| 116 | + | * | ||||||
| 117 | + | * @tparam Bits_ The source adapter's property bits | ||||||
| 118 | + | * @param rhs The source adapter to copy from | ||||||
| 119 | + | */ | ||||||
| 120 | + | template<int Bits_> | ||||||
| HITGNC | 121 | + | 14 | asio_executor_adapter( | ||||
| 122 | + | const asio_executor_adapter<Executor, Allocator, Bits_> & rhs) | ||||||
| 123 | + | noexcept(std::is_nothrow_copy_constructible_v<Executor>) | ||||||
| HITGNC | 124 | + | 14 | : executor_(rhs.executor_), allocator_(rhs.allocator_) | ||||
| 125 | + | { | ||||||
| 126 | + | if constexpr((Bits & work_mask) == work_tracked) | ||||||
| HITGNC | 127 | + | 8 | executor_.on_work_started(); | ||||
| HITGNC | 128 | + | 14 | } | ||||
| 129 | + | |||||||
| 130 | + | /** @brief Move constructor from adapter with different property bits. | ||||||
| 131 | + | * | ||||||
| 132 | + | * Moves from another adapter with potentially different properties. | ||||||
| 133 | + | * If this adapter tracks work, `on_work_started()` is called. | ||||||
| 134 | + | * | ||||||
| 135 | + | * @tparam Bits_ The source adapter's property bits | ||||||
| 136 | + | * @param rhs The source adapter to move from | ||||||
| 137 | + | */ | ||||||
| 138 | + | template<int Bits_> | ||||||
| HITGNC | 139 | + | 27 | asio_executor_adapter( | ||||
| 140 | + | asio_executor_adapter<Executor, Allocator, Bits_> && rhs) | ||||||
| 141 | + | noexcept(std::is_nothrow_move_constructible_v<Executor>) | ||||||
| HITGNC | 142 | + | 27 | : executor_(std::move(rhs.executor_)) | ||||
| HITGNC | 143 | + | 27 | , allocator_(std::move(rhs.allocator_)) | ||||
| 144 | + | { | ||||||
| 145 | + | if constexpr((Bits & work_mask) == work_tracked) | ||||||
| HITGNC | 146 | + | 12 | executor_.on_work_started(); | ||||
| HITGNC | 147 | + | 27 | } | ||||
| 148 | + | |||||||
| 149 | + | /** @brief Constructs from executor and allocator. | ||||||
| 150 | + | * | ||||||
| 151 | + | * @param executor The capy executor to wrap | ||||||
| 152 | + | * @param alloc The allocator for handler allocation | ||||||
| 153 | + | */ | ||||||
| 154 | + | asio_executor_adapter(Executor executor, const Allocator & alloc) | ||||||
| 155 | + | noexcept(std::is_nothrow_move_constructible_v<Executor> | ||||||
| 156 | + | && std::is_nothrow_copy_constructible_v<Allocator>) | ||||||
| 157 | + | : executor_(std::move(executor)), allocator_(alloc) | ||||||
| 158 | + | { | ||||||
| 159 | + | if constexpr((Bits & work_mask) == work_tracked) | ||||||
| 160 | + | executor_.on_work_started(); | ||||||
| 161 | + | } | ||||||
| 162 | + | |||||||
| 163 | + | /** @brief Constructs from adapter with different allocator. | ||||||
| 164 | + | * | ||||||
| 165 | + | * @tparam OtherAllocator The source adapter's allocator type | ||||||
| 166 | + | * @param executor The source adapter | ||||||
| 167 | + | * @param alloc The new allocator to use | ||||||
| 168 | + | */ | ||||||
| 169 | + | template<typename OtherAllocator> | ||||||
| HITGNC | 170 | + | 10 | explicit asio_executor_adapter( | ||||
| 171 | + | asio_executor_adapter<Executor, OtherAllocator, Bits> executor, | ||||||
| 172 | + | const Allocator & alloc) | ||||||
| 173 | + | noexcept(std::is_nothrow_move_constructible_v<Executor> && | ||||||
| 174 | + | std::is_nothrow_copy_constructible_v<Allocator>) | ||||||
| HITGNC | 175 | + | 10 | : executor_(std::move(executor.executor_)), allocator_(alloc) | ||||
| 176 | + | { | ||||||
| 177 | + | if constexpr((Bits & work_mask) == work_tracked) | ||||||
| HITGNC | 178 | + | 3 | executor_.on_work_started(); | ||||
| HITGNC | 179 | + | 10 | } | ||||
| 180 | + | |||||||
| 181 | + | |||||||
| 182 | + | /** @brief Constructs from a capy executor. | ||||||
| 183 | + | * | ||||||
| 184 | + | * The allocator is obtained from the executor's context frame allocator. | ||||||
| 185 | + | * | ||||||
| 186 | + | * @param executor The capy executor to wrap | ||||||
| 187 | + | */ | ||||||
| HITGNC | 188 | + | 12 | asio_executor_adapter(Executor executor) | ||||
| 189 | + | noexcept(std::is_nothrow_move_constructible_v<Executor>) | ||||||
| HITGNC | 190 | + | 12 | : executor_(std::move(executor)) | ||||
| HITGNC | 191 | + | 12 | , allocator_(executor_.context().get_frame_allocator()) | ||||
| 192 | + | { | ||||||
| 193 | + | if constexpr((Bits & work_mask) == work_tracked) | ||||||
| 194 | + | executor_.on_work_started(); | ||||||
| HITGNC | 195 | + | 12 | } | ||||
| 196 | + | |||||||
| 197 | + | /** @brief Destructor. | ||||||
| 198 | + | * | ||||||
| 199 | + | * If work tracking is enabled, calls `on_work_finished()`. | ||||||
| 200 | + | */ | ||||||
| HITGNC | 201 | + | 82 | ~asio_executor_adapter() | ||||
| 202 | + | { | ||||||
| 203 | + | if constexpr((Bits & work_mask) == work_tracked) | ||||||
| HITGNC | 204 | + | 26 | executor_.on_work_finished(); | ||||
| HITGNC | 205 | + | 82 | } | ||||
| 206 | + | |||||||
| 207 | + | /// @} | ||||||
| 208 | + | |||||||
| 209 | + | /// @name Assignment | ||||||
| 210 | + | /// @{ | ||||||
| 211 | + | |||||||
| 212 | + | /** @brief Copy assignment from adapter with different property bits. | ||||||
| 213 | + | * | ||||||
| 214 | + | * Properly handles work tracking when changing executors. | ||||||
| 215 | + | * | ||||||
| 216 | + | * @tparam Bits_ The source adapter's property bits | ||||||
| 217 | + | * @param rhs The source adapter | ||||||
| 218 | + | * @return Reference to this adapter | ||||||
| 219 | + | */ | ||||||
| 220 | + | template<int Bits_> | ||||||
| 221 | + | asio_executor_adapter & operator=( | ||||||
| 222 | + | const asio_executor_adapter<Executor, Allocator, Bits_> & rhs) | ||||||
| 223 | + | { | ||||||
| 224 | + | |||||||
| 225 | + | if constexpr((Bits & work_mask) == work_tracked) | ||||||
| 226 | + | if (rhs.executor_ != executor_) | ||||||
| 227 | + | { | ||||||
| 228 | + | rhs.executor_.on_work_started(); | ||||||
| 229 | + | executor_.on_work_finished(); | ||||||
| 230 | + | } | ||||||
| 231 | + | |||||||
| 232 | + | executor_ = rhs.executor_; | ||||||
| 233 | + | allocator_ = rhs.allocator_; | ||||||
| 234 | + | } | ||||||
| 235 | + | |||||||
| 236 | + | /// @} | ||||||
| 237 | + | |||||||
| 238 | + | /// @name Comparison | ||||||
| 239 | + | /// @{ | ||||||
| 240 | + | |||||||
| 241 | + | /** @brief Equality comparison. | ||||||
| 242 | + | * @param rhs The adapter to compare with | ||||||
| 243 | + | * @return `true` if both executor and allocator are equal | ||||||
| 244 | + | */ | ||||||
| MISUNC | 245 | + | ✗ | bool operator==(const asio_executor_adapter & rhs) const noexcept | ||||
| 246 | + | { | ||||||
| MISUNC | 247 | + | ✗ | return executor_ == rhs.executor_ | ||||
| MISUNC | 248 | + | ✗ | && allocator_ == rhs.allocator_; | ||||
| 249 | + | } | ||||||
| 250 | + | |||||||
| 251 | + | /** @brief Inequality comparison. | ||||||
| 252 | + | * @param rhs The adapter to compare with | ||||||
| 253 | + | * @return `true` if executor or allocator differs | ||||||
| 254 | + | */ | ||||||
| 255 | + | bool operator!=(const asio_executor_adapter & rhs) const noexcept | ||||||
| 256 | + | { | ||||||
| 257 | + | return executor_ != rhs.executor_ | ||||||
| 258 | + | && allocator_ != rhs.allocator_; | ||||||
| 259 | + | } | ||||||
| 260 | + | |||||||
| 261 | + | /// @} | ||||||
| 262 | + | |||||||
| 263 | + | /// @name Execution | ||||||
| 264 | + | /// @{ | ||||||
| 265 | + | |||||||
| 266 | + | /** @brief Executes a function according to the blocking property. | ||||||
| 267 | + | * | ||||||
| 268 | + | * The execution behavior depends on the `Bits` template parameter: | ||||||
| 269 | + | * - `blocking_never`: Posts the function for deferred execution | ||||||
| 270 | + | * - `blocking_possibly`: Dispatches (may run inline or post) | ||||||
| 271 | + | * - `blocking_always`: Executes the function inline immediately | ||||||
| 272 | + | * | ||||||
| 273 | + | * @tparam Function The callable type | ||||||
| 274 | + | * @param f The function to execute | ||||||
| 275 | + | */ | ||||||
| 276 | + | template <typename Function> | ||||||
| HITGNC | 277 | + | 11 | void execute(Function&& f) const | ||||
| 278 | + | { | ||||||
| 279 | + | if constexpr ((Bits & blocking_mask) == blocking_never) | ||||||
| HITGNC | 280 | + | 6 | executor_.post( | ||||
| HITGNC | 281 | + | 6 | detail::make_continuation(std::forward<Function>(f), allocator_)); | ||||
| 282 | + | else if constexpr((Bits & blocking_mask) == blocking_possibly) | ||||||
| HITGNC | 283 | + | 5 | executor_.dispatch( | ||||
| HITGNC | 284 | + | 5 | detail::make_continuation(std::forward<Function>(f), allocator_) | ||||
| HITGNC | 285 | + | 5 | ).resume(); | ||||
| 286 | + | else if constexpr((Bits & blocking_mask) == blocking_always) | ||||||
| 287 | + | std::forward<Function>(f)(); | ||||||
| HITGNC | 288 | + | 11 | } | ||||
| 289 | + | |||||||
| 290 | + | /// @} | ||||||
| 291 | + | |||||||
| 292 | + | /// @name Accessors | ||||||
| 293 | + | /// @{ | ||||||
| 294 | + | |||||||
| 295 | + | /** @brief Returns the associated execution context. | ||||||
| 296 | + | * @return Reference to the capy execution_context | ||||||
| 297 | + | */ | ||||||
| HITGNC | 298 | + | 1 | execution_context & context() const {return executor_.context(); } | ||||
| 299 | + | |||||||
| 300 | + | /** @brief Returns the associated allocator. | ||||||
| 301 | + | * @return Copy of the allocator | ||||||
| 302 | + | */ | ||||||
| 303 | + | Allocator get_allocator() const noexcept {return allocator_;} | ||||||
| 304 | + | |||||||
| 305 | + | /** @brief Returns the underlying capy executor. | ||||||
| 306 | + | * @return Copy of the wrapped executor | ||||||
| 307 | + | */ | ||||||
| 308 | + | const Executor get_capy_executor() const {return executor_;} | ||||||
| 309 | + | |||||||
| 310 | + | /// @} | ||||||
| 311 | + | private: | ||||||
| 312 | + | |||||||
| 313 | + | template<typename, typename, int> | ||||||
| 314 | + | friend struct asio_executor_adapter; | ||||||
| 315 | + | Executor executor_; | ||||||
| 316 | + | Allocator allocator_; | ||||||
| 317 | + | |||||||
| 318 | + | }; | ||||||
| 319 | + | |||||||
| 320 | + | /** @} */ // end of asio group | ||||||
| 321 | + | |||||||
| 322 | + | } | ||||||
| 323 | + | } | ||||||
| 324 | + | |||||||
| 325 | + | |||||||
| 326 | + | #endif //BOOST_CAPY_ASIO_EXECUTOR_ADAPTER_HPP | ||||||
| 327 | + | |||||||