78.21% Lines (122/156) 79.59% Functions (39/49)
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_BOOST_HPP
  11 + #define BOOST_CAPY_ASIO_BOOST_HPP
  12 +
  13 + /** @file boost.hpp
  14 + * @brief Boost.Asio integration for capy coroutines.
  15 + *
  16 + * This header provides complete integration between capy's coroutine framework
  17 + * and Boost.Asio. Include this header when using capy with Boost.Asio.
  18 + *
  19 + * @par Features
  20 + * - Property query/require support for `asio_executor_adapter`
  21 + * - `asio_boost_standard_executor` wrapper for Boost.Asio executors
  22 + * - `async_result` specialization for `as_io_awaitable_t`
  23 + * - Three-argument `asio_spawn` overloads with completion tokens
  24 + *
  25 + * @par Example
  26 + * @code
  27 + * #include <boost/capy/asio/boost.hpp>
  28 + * #include <boost/asio.hpp>
  29 + *
  30 + * capy::io_task<void> my_coro(boost::asio::ip::tcp::socket& sock) {
  31 + * char buf[1024];
  32 + * auto [n] = co_await sock.async_read_some(
  33 + * boost::asio::buffer(buf), capy::as_io_awaitable);
  34 + * // ...
  35 + * }
  36 + *
  37 + * int main() {
  38 + * boost::asio::io_context io;
  39 + * auto exec = capy::wrap_asio_executor(io.get_executor());
  40 + * capy::asio_spawn(exec, my_coro(socket))(boost::asio::detached);
  41 + * io.run();
  42 + * }
  43 + * @endcode
  44 + *
  45 + * @see standalone.hpp For standalone Asio support
  46 + * @ingroup asio
  47 + */
  48 +
  49 + #include <boost/capy/concept/io_awaitable.hpp>
  50 + #include <boost/capy/concept/decomposes_to.hpp>
  51 +
  52 + #include <boost/capy/asio/as_io_awaitable.hpp>
  53 + #include <boost/capy/asio/buffers.hpp>
  54 + #include <boost/capy/asio/detail/completion_handler.hpp>
  55 + #include <boost/capy/asio/executor_adapter.hpp>
  56 + #include <boost/capy/asio/spawn.hpp>
  57 + #include <boost/capy/asio/executor_from_asio.hpp>
  58 +
  59 +
  60 + #include <boost/asio/append.hpp>
  61 + #include <boost/asio/associated_allocator.hpp>
  62 + #include <boost/asio/associated_cancellation_slot.hpp>
  63 + #include <boost/asio/associated_immediate_executor.hpp>
  64 + #include <boost/asio/buffer.hpp>
  65 + #include <boost/asio/dispatch.hpp>
  66 + #include <boost/asio/cancellation_signal.hpp>
  67 + #include <boost/asio/cancellation_type.hpp>
  68 + #include <boost/asio/execution_context.hpp>
  69 + #include <boost/asio/execution/allocator.hpp>
  70 + #include <boost/asio/execution/blocking.hpp>
  71 + #include <boost/asio/execution/context.hpp>
  72 + #include <boost/asio/execution/outstanding_work.hpp>
  73 + #include <boost/asio/execution/relationship.hpp>
  74 + #include <tuple>
  75 + #include <type_traits>
  76 +
  77 + namespace boost::capy
  78 + {
  79 +
  80 + /** @addtogroup asio
  81 + * @{
  82 + */
  83 +
  84 + /// @name Execution Property Queries for asio_executor_adapter
  85 + /// @{
  86 +
  87 + /** @brief Queries the execution context from an asio_executor_adapter.
  88 + *
  89 + * Returns the Boost.Asio execution context associated with the adapter.
  90 + * The context is provided via an adapter service that bridges capy's
  91 + * execution_context with Asio's.
  92 + *
  93 + * @param exec The executor adapter to query
  94 + * @return Reference to the associated `boost::asio::execution_context`
  95 + */
  96 + template<typename Executor, typename Allocator, int Bits>
  97 + boost::asio::execution_context&
HITGNC   98 + 1 query(const asio_executor_adapter<Executor, Allocator, Bits> & exec,
  99 + boost::asio::execution::context_t) noexcept
  100 + {
  101 + using service = detail::asio_adapter_context_service<
  102 + boost::asio::execution_context>;
HITGNC   103 + 1 return exec.context().
HITGNC   104 + 1 template use_service<service>();
  105 + }
  106 +
  107 + /** @brief Queries the blocking property from an asio_executor_adapter.
  108 + * @param exec The executor adapter to query
  109 + * @return The current blocking property value
  110 + */
  111 + template<typename Executor, typename Allocator, int Bits>
  112 + constexpr boost::asio::execution::blocking_t
HITGNC   113 + 4 query(const asio_executor_adapter<Executor, Allocator, Bits> &,
  114 + boost::asio::execution::blocking_t) noexcept
  115 + {
  116 + using ex = asio_executor_adapter<Executor, Allocator, Bits>;
  117 +
  118 + switch (Bits & ex::blocking_mask)
  119 + {
  120 + case ex::blocking_never:
MISUNC   121 + return boost::asio::execution::blocking.never;
  122 + case ex::blocking_always:
  123 + return boost::asio::execution::blocking.always;
  124 + case ex::blocking_possibly:
HITGNC   125 + 4 return boost::asio::execution::blocking.possibly;
  126 + default: return {};
  127 + }
  128 + }
  129 +
  130 + /// @}
  131 +
  132 + /// @name Execution Property Requirements for asio_executor_adapter
  133 + /// @{
  134 +
  135 + /** @brief Requires blocking.possibly property.
  136 + * @return New adapter with blocking.possibly set
  137 + */
  138 + template<typename Executor, typename Allocator, int Bits>
  139 + constexpr auto
MISUNC   140 + require(const asio_executor_adapter<Executor, Allocator, Bits> & exec,
  141 + boost::asio::execution::blocking_t::possibly_t)
  142 + {
  143 + using ex = asio_executor_adapter<Executor, Allocator, Bits>;
MISUNC   144 + constexpr int new_bits = (Bits & ~ex::blocking_mask) | ex::blocking_possibly;
MISUNC   145 + return asio_executor_adapter<Executor, Allocator, new_bits>(exec);
  146 + }
  147 +
  148 + /** @brief Requires blocking.never property.
  149 + * @return New adapter that never blocks
  150 + */
  151 + template<typename Executor, typename Allocator, int Bits>
  152 + constexpr auto
HITGNC   153 + 6 require(const asio_executor_adapter<Executor, Allocator, Bits> & exec,
  154 + boost::asio::execution::blocking_t::never_t)
  155 + {
  156 + using ex = asio_executor_adapter<Executor, Allocator, Bits>;
HITGNC   157 + 6 constexpr int new_bits = (Bits & ~ex::blocking_mask) | ex::blocking_never;
HITGNC   158 + 6 return asio_executor_adapter<Executor, Allocator, new_bits>(exec);
  159 + }
  160 +
  161 + /** @brief Requires blocking.always property.
  162 + * @return New adapter that always blocks until execution completes
  163 + */
  164 + template<typename Executor, typename Allocator, int Bits>
  165 + constexpr auto
  166 + require(const asio_executor_adapter<Executor, Allocator, Bits> & exec,
  167 + boost::asio::execution::blocking_t::always_t)
  168 + {
  169 + using ex = asio_executor_adapter<Executor, Allocator, Bits>;
  170 + constexpr int new_bits = (Bits & ~ex::blocking_mask) | ex::blocking_always;
  171 + return asio_executor_adapter<Executor, Allocator, new_bits>(exec);
  172 + }
  173 +
  174 + /** @brief Queries the outstanding_work property.
  175 + * @param exec The executor adapter to query
  176 + * @return The current work tracking setting
  177 + */
  178 + template<typename Executor, typename Allocator, int Bits>
MISUNC   179 + static constexpr boost::asio::execution::outstanding_work_t query(
  180 + const asio_executor_adapter<Executor, Allocator, Bits> &,
  181 + boost::asio::execution::outstanding_work_t) noexcept
  182 + {
  183 + using ex = asio_executor_adapter<Executor, Allocator, Bits>;
  184 + switch (Bits & ex::work_mask)
  185 + {
  186 + case ex::work_tracked:
MISUNC   187 + return boost::asio::execution::outstanding_work.tracked;
  188 + case ex::work_untracked:
MISUNC   189 + return boost::asio::execution::outstanding_work.untracked;
  190 + default: return {};
  191 + }
  192 + }
  193 +
  194 + /** @brief Requires outstanding_work.tracked property.
  195 + * @return New adapter that tracks outstanding work
  196 + */
  197 + template<typename Executor, typename Allocator, int Bits>
  198 + constexpr auto
HITGNC   199 + 8 require(const asio_executor_adapter<Executor, Allocator, Bits> & exec,
  200 + boost::asio::execution::outstanding_work_t::tracked_t)
  201 + {
  202 + using ex = asio_executor_adapter<Executor, Allocator, Bits>;
HITGNC   203 + 8 constexpr int new_bits = (Bits & ~ex::work_mask) | ex::work_tracked;
HITGNC   204 + 8 return asio_executor_adapter<Executor, Allocator, new_bits>(exec);
  205 + }
  206 +
  207 + /** @brief Requires outstanding_work.untracked property.
  208 + * @return New adapter that does not track outstanding work
  209 + */
  210 + template<typename Executor, typename Allocator, int Bits>
  211 + constexpr auto
MISUNC   212 + require(const asio_executor_adapter<Executor, Allocator, Bits> & exec,
  213 + boost::asio::execution::outstanding_work_t::untracked_t)
  214 + {
  215 + using ex = asio_executor_adapter<Executor, Allocator, Bits>;
MISUNC   216 + constexpr int new_bits = (Bits & ~ex::work_mask) | ex::work_untracked;
MISUNC   217 + return asio_executor_adapter<Executor, Allocator, new_bits>(exec);
  218 + }
  219 +
  220 + /** @brief Queries the allocator property.
  221 + * @return The adapter's current allocator
  222 + */
  223 + template <typename Executor, typename Allocator, int Bits, typename OtherAllocator>
  224 + constexpr Allocator query(
  225 + const asio_executor_adapter<Executor, Allocator, Bits> & exec,
  226 + boost::asio::execution::allocator_t<OtherAllocator>) noexcept
  227 + {
  228 + return exec.get_allocator();
  229 + }
  230 +
  231 + /** @brief Requires a specific allocator.
  232 + * @param a The allocator property containing the new allocator
  233 + * @return New adapter using the specified allocator
  234 + */
  235 + template <typename Executor, typename Allocator, int Bits, typename OtherAllocator>
  236 + constexpr auto
HITGNC   237 + 10 require(const asio_executor_adapter<Executor, Allocator, Bits> & exec,
  238 + boost::asio::execution::allocator_t<OtherAllocator> a)
  239 + {
  240 + return asio_executor_adapter<Executor, OtherAllocator, Bits>(
HITGNC   241 + 10 exec, a.value()
HITGNC   242 + 10 );
  243 + }
  244 +
  245 + /** @brief Requires the default allocator (uses frame allocator).
  246 + * @return New adapter using the frame allocator from the context
  247 + */
  248 + template <typename Executor, typename Allocator, int Bits>
  249 + constexpr auto
  250 + require(const asio_executor_adapter<Executor, Allocator, Bits> & exec,
  251 + boost::asio::execution::allocator_t<void>)
  252 + noexcept(std::is_nothrow_move_constructible_v<Executor>)
  253 + {
  254 + return asio_executor_adapter<
  255 + Executor,
  256 + std::pmr::polymorphic_allocator<void>,
  257 + Bits>
  258 + (
  259 + exec,
  260 + exec.context().get_frame_allocator()
  261 + );
  262 + }
  263 +
  264 + /// @}
  265 +
  266 + namespace detail
  267 + {
  268 +
  269 + template<typename Executor>
  270 + struct asio_work_tracker_service : boost::asio::execution_context::service
  271 + {
  272 + static boost::asio::execution_context::id id;
  273 +
HITGNC   274 + 1 asio_work_tracker_service(boost::asio::execution_context & ctx)
HITGNC   275 + 1 : boost::asio::execution_context::service(ctx) {}
  276 +
  277 + using tracked_executor =
  278 + typename boost::asio::prefer_result<
  279 + Executor,
  280 + boost::asio::execution::outstanding_work_t::tracked_t
  281 + >::type;
  282 +
  283 + alignas(tracked_executor) char buffer[sizeof(tracked_executor) ];
  284 +
  285 +
  286 + std::mutex mutex;
  287 + std::size_t work = 0u;
  288 +
HITGNC   289 + 2 void shutdown()
  290 + {
HITGNC   291 + 2 std::lock_guard _(mutex);
HITGNC   292 + 2 if (std::exchange(work, 0) > 0u)
MISUNC   293 + reinterpret_cast<tracked_executor*>(buffer)->~tracked_executor();
HITGNC   294 + 2 }
  295 +
  296 +
HITGNC   297 + 1 void work_started(const Executor & exec)
  298 + {
HITGNC   299 + 1 std::lock_guard _(mutex);
HITGNC   300 + 1 if (work ++ == 0u)
HITGNC   301 + 1 new (buffer) tracked_executor(
  302 + boost::asio::prefer(exec,
  303 + boost::asio::execution::outstanding_work.tracked));
HITGNC   304 + 1 }
  305 +
HITGNC   306 + 1 void work_finished()
  307 + {
HITGNC   308 + 1 std::lock_guard _(mutex);
HITGNC   309 + 1 if (--work == 0u)
HITGNC   310 + 1 reinterpret_cast<tracked_executor*>(buffer)->~tracked_executor();
HITGNC   311 + 1 }
  312 +
  313 + };
  314 +
  315 +
  316 + template<typename Executor>
  317 + boost::asio::execution_context::id asio_work_tracker_service<Executor>::id;
  318 +
  319 +
  320 + }
  321 +
  322 + /** @brief Wraps a Boost.Asio standard executor for use with capy.
  323 + *
  324 + * This class adapts Boost.Asio executors that follow the P0443/P2300
  325 + * standard executor model to be usable as capy executors. It provides
  326 + * work tracking through an `asio_work_tracker_service` and integrates
  327 + * with capy's execution context system.
  328 + *
  329 + * @tparam Executor A Boost.Asio executor satisfying `AsioBoostStandardExecutor`
  330 + *
  331 + * @par Example
  332 + * @code
  333 + * boost::asio::io_context io;
  334 + * auto wrapped = asio_boost_standard_executor(io.get_executor());
  335 + *
  336 + * // Use with capy coroutines
  337 + * capy::run(wrapped, my_io_task());
  338 + * @endcode
  339 + *
  340 + * @see wrap_asio_executor For automatic executor type detection
  341 + * @see asio_net_ts_executor For legacy Networking TS executors
  342 + */
  343 + template<detail::AsioBoostStandardExecutor Executor>
  344 + struct asio_boost_standard_executor
  345 + {
  346 + /** @brief Constructs from a Boost.Asio executor.
  347 + * @param executor The Boost.Asio executor to wrap
  348 + */
HITGNC   349 + 1 asio_boost_standard_executor(Executor executor)
  350 + noexcept(std::is_nothrow_move_constructible_v<Executor>)
HITGNC   351 + 1 : executor_(std::move(executor))
  352 + {
HITGNC   353 + 1 }
  354 +
  355 + /** @brief Move constructor. */
HITGNC   356 + 5 asio_boost_standard_executor(asio_boost_standard_executor && rhs)
  357 + noexcept(std::is_nothrow_move_constructible_v<Executor>)
HITGNC   358 + 5 : executor_(std::move(rhs.executor_))
  359 + {
HITGNC   360 + 5 }
  361 +
  362 + /** @brief Copy constructor. */
HITGNC   363 + 1 asio_boost_standard_executor(const asio_boost_standard_executor & rhs)
  364 + noexcept(std::is_nothrow_copy_constructible_v<Executor>)
HITGNC   365 + 1 : executor_(rhs.executor_)
  366 + {
HITGNC   367 + 1 }
  368 +
  369 + /** @brief Returns the associated capy execution context.
  370 + * @return Reference to the capy execution_context
  371 + */
HITGNC   372 + 2 execution_context& context() const noexcept
  373 + {
HITGNC   374 + 2 auto & ec = boost::asio::query(executor_, boost::asio::execution::context);
  375 + return boost::asio::use_service<
  376 + detail::asio_context_service<boost::asio::execution_context>
HITGNC   377 + 2 >(ec);
  378 + }
  379 +
  380 + /** @brief Notifies that work has started.
  381 + *
  382 + * Delegates to the work tracker service to maintain a tracked executor
  383 + * while work is outstanding.
  384 + */
HITGNC   385 + 1 void on_work_started() const noexcept
  386 + {
HITGNC   387 + 1 auto & ec = boost::asio::query(executor_, boost::asio::execution::context);
  388 + boost::asio::use_service<
  389 + detail::asio_work_tracker_service<Executor>
HITGNC   390 + 1 >(ec).work_started(executor_);
HITGNC   391 + 1 }
  392 +
  393 + /** @brief Notifies that work has finished.
  394 + *
  395 + * Decrements the work counter in the tracker service.
  396 + */
HITGNC   397 + 1 void on_work_finished() const noexcept
  398 + {
HITGNC   399 + 1 auto & ec = boost::asio::query(executor_, boost::asio::execution::context);
  400 + boost::asio::use_service<
  401 + detail::asio_work_tracker_service<Executor>
HITGNC   402 + 1 >(ec).work_finished();
HITGNC   403 + 1 }
  404 +
  405 + /** @brief Dispatches a continuation for execution.
  406 + *
  407 + * May execute inline if the executor allows, otherwise posts.
  408 + * Uses the context's frame allocator for handler allocation.
  409 + *
  410 + * @param c The continuation to dispatch
  411 + * @return A noop coroutine handle
  412 + */
HITGNC   413 + 1 std::coroutine_handle<> dispatch(continuation & c) const
  414 + {
HITGNC   415 + 2 boost::asio::prefer(
HITGNC   416 + 1 executor_,
HITGNC   417 + 1 boost::asio::execution::allocator(
HITGNC   418 + 1 std::pmr::polymorphic_allocator<void>(
HITGNC   419 + 1 context().get_frame_allocator()
  420 + )
  421 + )
HITGNC   422 + 1 ).execute(detail::asio_coroutine_unique_handle(c.h));
HITGNC   423 + 1 return std::noop_coroutine();
  424 + }
  425 +
  426 + /** @brief Posts a continuation for deferred execution.
  427 + *
  428 + * The continuation will never be executed inline. Uses blocking.never
  429 + * and relationship.fork properties for proper async behavior.
  430 + *
  431 + * @param c The continuation to post
  432 + */
MISUNC   433 + void post(continuation & c) const
  434 + {
  435 + boost::asio::prefer(
MISUNC   436 + boost::asio::require(executor_, boost::asio::execution::blocking.never),
  437 + boost::asio::execution::relationship.fork,
MISUNC   438 + boost::asio::execution::allocator(
MISUNC   439 + std::pmr::polymorphic_allocator<void>(
MISUNC   440 + context().get_frame_allocator()
  441 + )
  442 + )
MISUNC   443 + ).execute(detail::asio_coroutine_unique_handle(c.h));
MISUNC   444 + }
  445 +
  446 + /** @brief Equality comparison. */
MISUNC   447 + bool operator==(const asio_boost_standard_executor & rhs) const noexcept
  448 + {
MISUNC   449 + return executor_ == rhs.executor_;
  450 + }
  451 +
  452 + /** @brief Inequality comparison. */
  453 + bool operator!=(const asio_boost_standard_executor & rhs) const noexcept
  454 + {
  455 + return executor_ != rhs.executor_;
  456 + }
  457 +
  458 + private:
  459 + Executor executor_;
  460 + };
  461 +
  462 +
  463 + /** @brief Spawns a capy coroutine with a Boost.Asio completion token (executor overload).
  464 + *
  465 + * Convenience overload that combines the two-step spawn process into one call.
  466 + * Equivalent to `asio_spawn(exec, awaitable)(token)`.
  467 + *
  468 + * @tparam ExecutorType The executor type
  469 + * @tparam Awaitable The coroutine type
  470 + * @tparam Token A Boost.Asio completion token
  471 + * @param exec The executor to run on
  472 + * @param awaitable The coroutine to spawn
  473 + * @param token The completion token
  474 + * @return Depends on the token type
  475 + */
  476 + template<Executor ExecutorType,
  477 + IoAwaitable Awaitable,
  478 + boost::asio::completion_token_for<
  479 + detail::completion_signature_for_io_awaitable<Awaitable>
  480 + > Token>
HITGNC   481 + 4 auto asio_spawn(ExecutorType exec, Awaitable && awaitable, Token token)
  482 + {
HITGNC   483 + 4 return asio_spawn(exec, std::forward<Awaitable>(awaitable))(std::move(token));
  484 + }
  485 +
  486 + /** @brief Spawns a capy coroutine with a Boost.Asio completion token (context overload).
  487 + *
  488 + * Convenience overload that extracts the executor from a context.
  489 + *
  490 + * @tparam Context The execution context type
  491 + * @tparam Awaitable The coroutine type
  492 + * @tparam Token A Boost.Asio completion token
  493 + * @param ctx The execution context
  494 + * @param awaitable The coroutine to spawn
  495 + * @param token The completion token
  496 + * @return Depends on the token type
  497 + */
  498 + template<ExecutionContext Context,
  499 + IoAwaitable Awaitable,
  500 + boost::asio::completion_token_for<
  501 + detail::completion_signature_for_io_awaitable<Awaitable>
  502 + > Token>
  503 + auto asio_spawn(Context & ctx, Awaitable && awaitable, Token token)
  504 + {
  505 + return asio_spawn(ctx.get_executor(), std::forward<Awaitable>(awaitable))(std::move(token));
  506 + }
  507 +
  508 + /** @} */ // end of asio group
  509 +
  510 + }
  511 +
  512 + template<typename ... Ts>
  513 + struct boost::asio::async_result<boost::capy::as_io_awaitable_t, void(Ts...)>
  514 + : boost::capy::detail::async_result_impl<
  515 + boost::asio::cancellation_signal,
  516 + boost::asio::cancellation_type,
  517 + Ts...>
  518 + {
  519 + };
  520 +
  521 +
  522 + namespace boost::capy::detail
  523 + {
  524 +
  525 +
  526 + struct boost_asio_init;
  527 +
  528 +
  529 + template<typename Allocator>
  530 + struct boost_asio_promise_type_allocator_base
  531 + {
  532 + template<typename Handler, Executor Ex, IoAwaitable Awaitable>
  533 + void * operator new (std::size_t n, boost_asio_init &,
  534 + Handler & handler,
  535 + Ex &, Awaitable &)
  536 + {
  537 + using allocator_type = std::allocator_traits<Allocator>
  538 + ::template rebind_alloc<char>;
  539 + allocator_type allocator(boost::asio::get_associated_allocator(handler));
  540 +
  541 + // round n up to max_align
  542 + if (const auto d = n % sizeof(std::max_align_t); d > 0u)
  543 + n += (sizeof(std::max_align_t) - d);
  544 +
  545 + auto mem = std::allocator_traits<allocator_type>::
  546 + template rebind_traits<char>::
  547 + allocate(allocator, n + sizeof(allocator_type));
  548 +
  549 + void* p = static_cast<char*>(mem) + n;
  550 + new (p) allocator_type(std::move(allocator));
  551 +
  552 + return mem;
  553 + }
  554 + void operator delete(void * ptr, std::size_t n)
  555 + {
  556 + if (const auto d = n % sizeof(std::max_align_t); d > 0u)
  557 + n += (sizeof(std::max_align_t) - d);
  558 +
  559 + using allocator_type = std::allocator_traits<Allocator>
  560 + ::template rebind_alloc<char>;
  561 + auto allocator_p = reinterpret_cast<allocator_type*>(
  562 + static_cast<char*>(ptr) + n);
  563 + auto allocator = std::move(*allocator_p);
  564 +
  565 + allocator_p->~allocator_type();
  566 + allocator.deallocate(static_cast<char*>(ptr), n + sizeof(allocator_type));
  567 + }
  568 + };
  569 +
  570 + template<>
  571 + struct boost_asio_promise_type_allocator_base<std::allocator<void>>
  572 + {
  573 + };
  574 +
  575 + template<typename Handler, Executor Ex, IoAwaitable Awaitable>
  576 + struct boost_asio_init_promise_type
  577 + : boost_asio_promise_type_allocator_base<
  578 + boost::asio::associated_allocator_t<Handler>>
  579 + {
  580 + using args_type = completion_tuple_for_io_awaitable<Awaitable>;
  581 +
HITGNC   582 + 4 boost_asio_init_promise_type(
  583 + boost_asio_init &,
  584 + Handler & h,
  585 + Ex & exec,
  586 + Awaitable &)
HITGNC   587 + 4 : handler(h), ex(exec)
  588 + {
HITGNC   589 + 4 }
  590 +
  591 + Handler & handler;
  592 + Ex &ex;
  593 +
HITGNC   594 + 4 void get_return_object() {}
MISUNC   595 + void unhandled_exception() {throw;}
MISUNC   596 + void return_void() {}
  597 +
HITGNC   598 + 4 std::suspend_never initial_suspend() noexcept {return {};}
MISUNC   599 + std::suspend_never final_suspend() noexcept {return {};}
  600 +
  601 + struct completer
  602 + {
  603 + Handler handler;
  604 + Ex ex;
  605 + args_type args;
  606 +
  607 +
HITGNC   608 + 4 bool await_ready() const {return false;}
  609 +
HITGNC   610 + 4 auto await_suspend(
  611 + std::coroutine_handle<boost_asio_init_promise_type> h)
  612 + {
HITGNC   613 + 4 auto handler_ =
  614 + std::apply(
HITGNC   615 + 8 [&](auto ... args)
  616 + {
HITGNC   617 + 4 return boost::asio::append(std::move(handler),
HITGNC   618 + 8 std::move(args)...);
  619 + },
HITGNC   620 + 4 detail::decomposed_types(std::move(args)));
  621 +
HITGNC   622 + 4 auto exec = boost::asio::get_associated_immediate_executor(
  623 + handler_,
HITGNC   624 + 8 asio_executor_adapter(std::move(ex)));
HITGNC   625 + 4 h.destroy();
HITGNC   626 + 4 boost::asio::dispatch(exec, std::move(handler_));
HITGNC   627 + 4 }
MISUNC   628 + void await_resume() const {}
  629 + };
  630 +
HITGNC   631 + 4 completer yield_value(args_type value)
  632 + {
HITGNC   633 + 8 return {std::move(handler), std::move(ex), std::move(value)};
HITGNC   634 + 1 }
  635 +
  636 + struct wrapper
  637 + {
  638 + Awaitable r;
  639 + const Ex &ex;
  640 + io_env env;
  641 + std::stop_source stop_src;
  642 + boost::asio::cancellation_slot cancel_slot;
  643 +
  644 + continuation c;
  645 +
HITGNC   646 + 4 wrapper(Awaitable && r, const Ex &ex)
HITGNC   647 + 4 : r(std::move(r)), ex(ex)
  648 + {
HITGNC   649 + 4 }
  650 +
HITGNC   651 + 4 bool await_ready() {return r.await_ready(); }
  652 +
HITGNC   653 + 1 auto await_suspend(
  654 + std::coroutine_handle<boost_asio_init_promise_type> tr)
  655 + {
HITGNC   656 + 1 env.executor = ex;
HITGNC   657 + 1 env.stop_token = stop_src.get_token();
  658 + cancel_slot =
HITGNC   659 + 1 boost::asio::get_associated_cancellation_slot(tr.promise().handler);
  660 +
HITGNC   661 + 1 if (cancel_slot.is_connected())
MISUNC   662 + cancel_slot.assign(
MISUNC   663 + [this](boost::asio::cancellation_type ct)
  664 + {
MISUNC   665 + if ((ct & boost::asio::cancellation_type::terminal)
MISUNC   666 + != boost::asio::cancellation_type::none)
MISUNC   667 + stop_src.request_stop();
  668 + });
HITGNC   669 + 1 env.frame_allocator = get_current_frame_allocator();
  670 +
  671 + using suspend_kind = decltype(r.await_suspend(tr, &env));
  672 + if constexpr (std::is_void_v<suspend_kind>)
MISUNC   673 + r.await_suspend(tr, &env);
  674 + else if constexpr (std::same_as<suspend_kind, bool>)
  675 + return r.await_suspend(tr, &env);
  676 + else
  677 + {
HITGNC   678 + 1 c.h = r.await_suspend(tr, &env);
HITGNC   679 + 1 return ex.dispatch(c);
  680 + }
MISUNC   681 + }
  682 +
HITGNC   683 + 4 completion_tuple_for_io_awaitable<Awaitable> await_resume()
  684 + {
HITGNC   685 + 4 if (cancel_slot.is_connected())
MISUNC   686 + cancel_slot.clear();
  687 +
  688 + using type = decltype(r.await_resume());
  689 + if constexpr (!noexcept(r.await_resume()))
  690 + {
  691 + if constexpr (std::is_void_v<type>)
  692 + try
  693 + {
HITGNC   694 + 1 r.await_resume();
HITGNC   695 + 1 return {std::exception_ptr()};
  696 + }
MISUNC   697 + catch (...)
  698 + {
MISUNC   699 + return std::current_exception();
  700 + }
  701 + else
  702 + try
  703 + {
  704 + return {std::current_exception(), r.await_resume()};
  705 + }
  706 + catch (...)
  707 + {
  708 + return {std::current_exception(), type()};
  709 + }
  710 + }
  711 + else
  712 + {
  713 + if constexpr (std::is_void_v<type>)
  714 + {
  715 + r.await_resume();
  716 + return {};
  717 + }
  718 + else
HITGNC   719 + 3 return {r.await_resume()};
  720 + }
  721 + }
  722 + };
  723 +
HITGNC   724 + 4 wrapper await_transform(Awaitable & r)
  725 + {
HITGNC   726 + 4 return wrapper{std::move(r), ex};
  727 + }
  728 + };
  729 +
  730 +
  731 + struct boost_asio_init
  732 + {
  733 + template<typename Handler, Executor Ex, IoAwaitable Awaitable>
HITGNC   734 + 4 void operator()(
  735 + Handler ,
  736 + Ex,
  737 + Awaitable awaitable)
  738 + {
  739 + auto res = co_await awaitable;
  740 + co_yield std::move(res);
HITGNC   741 + 8 }
  742 + };
  743 +
  744 + template<typename Awaitable, typename Token>
  745 + requires
  746 + boost::asio::completion_token_for<
  747 + Token,
  748 + completion_signature_for_io_awaitable<Awaitable>
  749 + >
  750 + struct initialize_asio_spawn_helper<Awaitable, Token>
  751 + {
  752 + template<typename Executor>
HITGNC   753 + 4 static auto init(Executor ex, Awaitable r, Token && tk)
  754 + -> decltype( boost::asio::async_initiate<
  755 + Token,
  756 + completion_signature_for_io_awaitable<Awaitable>>(
  757 + boost_asio_init{},
  758 + tk, std::move(ex), std::move(r)
  759 + ))
  760 + {
  761 + return boost::asio::async_initiate<
  762 + Token,
HITGNC   763 + 3 completion_signature_for_io_awaitable<Awaitable>>(
  764 + boost_asio_init{},
HITGNC   765 + 4 tk, std::move(ex), std::move(r)
HITGNC   766 + 4 );
  767 + }
  768 + };
  769 +
  770 +
  771 + }
  772 +
  773 +
  774 + template<typename Handler, typename Executor, typename Awaitable>
  775 + struct std::coroutine_traits<void,
  776 + boost::capy::detail::boost_asio_init&,
  777 + Handler,
  778 + Executor,
  779 + Awaitable>
  780 + {
  781 + using promise_type
  782 + = boost::capy::detail::boost_asio_init_promise_type<
  783 + Handler,
  784 + Executor,
  785 + Awaitable>;
  786 + };
  787 +
  788 +
  789 +
  790 + namespace boost::capy
  791 + {
  792 +
  793 +
  794 + namespace detail
  795 + {
  796 +
  797 + template<typename Sequence>
  798 + class asio_boost_buffer_range
  799 + {
  800 + public:
  801 + using sequence_type = Sequence;
  802 + using iterator = asio_buffer_iterator<
  803 + decltype(boost::asio::buffer_sequence_begin(std::declval<const Sequence&>()))>;
  804 + using const_iterator = iterator;
  805 +
  806 + asio_boost_buffer_range() = default;
  807 +
HITGNC   808 + 3 explicit asio_boost_buffer_range(Sequence seq)
  809 + noexcept(std::is_nothrow_move_constructible_v<Sequence>)
HITGNC   810 + 3 : seq_(std::move(seq))
  811 + {
HITGNC   812 + 3 }
  813 +
  814 + asio_boost_buffer_range(const asio_boost_buffer_range&) = default;
  815 + asio_boost_buffer_range(asio_boost_buffer_range&&) = default;
  816 + asio_boost_buffer_range& operator=(const asio_boost_buffer_range&) = default;
  817 + asio_boost_buffer_range& operator=(asio_boost_buffer_range&&) = default;
  818 +
HITGNC   819 + 6 iterator begin() const
  820 + noexcept(noexcept(boost::asio::buffer_sequence_begin(std::declval<const Sequence&>())))
  821 + {
HITGNC   822 + 6 return iterator(boost::asio::buffer_sequence_begin(seq_));
  823 + }
  824 +
HITGNC   825 + 6 iterator end() const
  826 + noexcept(noexcept(boost::asio::buffer_sequence_end(std::declval<const Sequence&>())))
  827 + {
HITGNC   828 + 6 return iterator(boost::asio::buffer_sequence_end(seq_));
  829 + }
  830 +
  831 + /// Returns the underlying sequence.
  832 + const Sequence& base() const noexcept { return seq_; }
  833 +
  834 + private:
  835 + Sequence seq_{};
  836 + };
  837 +
  838 + // Deduction guide
  839 + template<typename Sequence>
  840 + asio_boost_buffer_range(Sequence) -> asio_boost_buffer_range<Sequence>;
  841 +
  842 +
HITGNC   843 + 3 asio_mutable_buffer asio_buffer_transformer_t::
  844 + operator()(const boost::asio::mutable_buffer &mb) const noexcept
  845 + {
HITGNC   846 + 3 return {mb.data(), mb.size()};
  847 + }
  848 +
HITGNC   849 + 3 asio_const_buffer asio_buffer_transformer_t::
  850 + operator()(const boost::asio::const_buffer &cb) const noexcept
  851 + {
HITGNC   852 + 3 return {cb.data(), cb.size()};
  853 + }
  854 +
  855 + }
  856 +
  857 + /** Convert a Boost.Asio buffer sequence for bidirectional Asio/capy compatibility.
  858 +
  859 + Wraps a Boost.Asio buffer sequence in a transforming range that converts
  860 + each buffer element to asio_const_buffer or asio_mutable_buffer.
  861 + The returned range satisfies both Boost.Asio's buffer sequence requirements
  862 + and capy's buffer sequence concepts.
  863 +
  864 + This overload uses `boost::asio::buffer_sequence_begin` and
  865 + `boost::asio::buffer_sequence_end` for iteration.
  866 +
  867 + @par Example: Asio to Capy
  868 + @code
  869 + std::vector<boost::asio::mutable_buffer> asio_bufs = ...;
  870 + auto seq = capy::as_asio_buffer_sequence(asio_bufs);
  871 + std::size_t total = capy::buffer_size(seq); // Use capy algorithms
  872 + @endcode
  873 +
  874 + @param seq The Boost.Asio buffer sequence to convert
  875 + @return A transforming range over the buffer sequence
  876 +
  877 + @see as_asio_buffer_sequence in buffers.hpp for capy buffer sequences
  878 + */
  879 + template<typename T>
  880 + requires requires (const T & seq)
  881 + {
  882 + {boost::asio::buffer_sequence_begin(seq)} -> std::bidirectional_iterator;
  883 + {boost::asio::buffer_sequence_end(seq)} -> std::bidirectional_iterator;
  884 + {*boost::asio::buffer_sequence_begin(seq)} -> std::convertible_to<boost::asio::const_buffer>;
  885 + {*boost::asio::buffer_sequence_end(seq)} -> std::convertible_to<boost::asio::const_buffer>;
  886 + }
HITGNC   887 + 3 auto as_asio_buffer_sequence(const T & seq)
  888 + {
HITGNC   889 + 3 return detail::asio_boost_buffer_range(seq);
  890 + }
  891 +
HITGNC   892 + 2 asio_const_buffer::operator boost::asio::const_buffer() const
  893 + {
HITGNC   894 + 2 return {data(), size()};
  895 + }
  896 +
  897 + asio_mutable_buffer::operator boost::asio::const_buffer() const
  898 + {
  899 + return {data(), size()};
  900 + }
  901 +
HITGNC   902 + 2 asio_mutable_buffer::operator boost::asio::mutable_buffer() const
  903 + {
HITGNC   904 + 2 return {data(), size()};
  905 + }
  906 +
  907 + }
  908 +
  909 + #endif //BOOST_CAPY_ASIO_BOOST_HPP
  910 +