TLA Line data Source 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 : */
113 HIT 2 : asio_net_ts_executor(Executor executor)
114 : noexcept(std::is_nothrow_move_constructible_v<Executor>)
115 2 : : executor_(std::move(executor))
116 : {
117 2 : }
118 :
119 : /** @brief Move constructor. */
120 10 : asio_net_ts_executor(asio_net_ts_executor && rhs)
121 : noexcept(std::is_nothrow_move_constructible_v<Executor>)
122 10 : : executor_(std::move(rhs.executor_))
123 : {
124 10 : }
125 :
126 : /** @brief Copy constructor. */
127 1 : asio_net_ts_executor(const asio_net_ts_executor & rhs)
128 : noexcept(std::is_nothrow_copy_constructible_v<Executor>)
129 1 : : executor_(rhs.executor_)
130 : {
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 : */
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>>
144 6 : (
145 : executor_.context()
146 6 : );
147 : }
148 :
149 : /** @brief Notifies that work has started.
150 : *
151 : * Forwards to the underlying executor's `on_work_started()`.
152 : */
153 10 : void on_work_started() const noexcept
154 : {
155 10 : executor_.on_work_started();
156 10 : }
157 :
158 : /** @brief Notifies that work has finished.
159 : *
160 : * Forwards to the underlying executor's `on_work_finished()`.
161 : */
162 12 : void on_work_finished() const noexcept
163 : {
164 12 : executor_.on_work_finished();
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 : */
174 5 : std::coroutine_handle<> dispatch(continuation & c) const
175 : {
176 5 : executor_.dispatch(
177 10 : detail::asio_coroutine_unique_handle(c.h),
178 5 : std::pmr::polymorphic_allocator<void>(
179 : boost::capy::get_current_frame_allocator()));
180 :
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 : */
190 2 : void post(continuation & c) const
191 : {
192 2 : executor_.post(
193 4 : detail::asio_coroutine_unique_handle(c.h),
194 2 : std::pmr::polymorphic_allocator<void>(
195 : boost::capy::get_current_frame_allocator()));
196 2 : }
197 :
198 : /** @brief Equality comparison. */
199 MIS 0 : bool operator==(const asio_net_ts_executor & rhs) const noexcept
200 : {
201 0 : 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>
260 HIT 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)
266 2 : );
267 : else if constexpr (detail::AsioBoostStandardExecutor<executor_t>)
268 : return asio_boost_standard_executor<executor_t>(
269 1 : std::forward<Executor>(exec)
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
|