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