LCOV - code coverage report
Current view: top level - capy/asio - boost.hpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 78.2 % 156 122 34
Test Date: 2026-05-01 02:54:45 Functions: 65.3 % 121 79 42

           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_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&
      98 HIT           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>;
     103               1 :   return exec.context().
     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
     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:
     121 MIS           0 :         return boost::asio::execution::blocking.never;
     122                 :     case ex::blocking_always:
     123                 :         return boost::asio::execution::blocking.always;
     124                 :     case ex::blocking_possibly:
     125 HIT           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
     140 MIS           0 :     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>;
     144               0 :   constexpr int new_bits = (Bits & ~ex::blocking_mask) | ex::blocking_possibly;
     145               0 :   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
     153 HIT           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>;
     157               6 :   constexpr int new_bits = (Bits & ~ex::blocking_mask) | ex::blocking_never;
     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>
     179 MIS           0 : 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:
     187               0 :       return boost::asio::execution::outstanding_work.tracked;
     188                 :     case ex::work_untracked:
     189               0 :       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
     199 HIT           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>;
     203               8 :   constexpr int new_bits = (Bits & ~ex::work_mask) | ex::work_tracked;
     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
     212 MIS           0 :     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>;
     216               0 :   constexpr int new_bits = (Bits & ~ex::work_mask) | ex::work_untracked;
     217               0 :   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
     237 HIT          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>(
     241              10 :       exec, a.value()
     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                 : 
     274               1 :   asio_work_tracker_service(boost::asio::execution_context & ctx)
     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                 : 
     289               2 :   void shutdown()
     290                 :   {
     291               2 :     std::lock_guard _(mutex);
     292               2 :     if (std::exchange(work, 0) > 0u)
     293 MIS           0 :       reinterpret_cast<tracked_executor*>(buffer)->~tracked_executor();
     294 HIT           2 :   }
     295                 : 
     296                 : 
     297               1 :   void work_started(const Executor & exec)
     298                 :   {
     299               1 :     std::lock_guard _(mutex);
     300               1 :     if (work ++ == 0u)
     301               1 :       new (buffer) tracked_executor(
     302                 :         boost::asio::prefer(exec, 
     303                 :         boost::asio::execution::outstanding_work.tracked));
     304               1 :   }
     305                 : 
     306               1 :   void work_finished() 
     307                 :   {
     308               1 :       std::lock_guard _(mutex);
     309               1 :       if (--work == 0u)
     310               1 :         reinterpret_cast<tracked_executor*>(buffer)->~tracked_executor();
     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                 :    */
     349               1 :   asio_boost_standard_executor(Executor executor)
     350                 :       noexcept(std::is_nothrow_move_constructible_v<Executor>)
     351               1 :       : executor_(std::move(executor))
     352                 :   {
     353               1 :   }
     354                 : 
     355                 :   /** @brief Move constructor. */
     356               5 :   asio_boost_standard_executor(asio_boost_standard_executor && rhs)
     357                 :       noexcept(std::is_nothrow_move_constructible_v<Executor>)
     358               5 :       : executor_(std::move(rhs.executor_))
     359                 :   {
     360               5 :   }
     361                 : 
     362                 :   /** @brief Copy constructor. */
     363               1 :   asio_boost_standard_executor(const asio_boost_standard_executor & rhs)
     364                 :       noexcept(std::is_nothrow_copy_constructible_v<Executor>)
     365               1 :       : executor_(rhs.executor_)
     366                 :   {
     367               1 :   }
     368                 : 
     369                 :   /** @brief Returns the associated capy execution context.
     370                 :    *  @return Reference to the capy execution_context
     371                 :    */
     372               2 :   execution_context& context() const noexcept
     373                 :   {
     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>
     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                 :    */
     385               1 :   void on_work_started() const noexcept
     386                 :   {
     387               1 :     auto & ec = boost::asio::query(executor_, boost::asio::execution::context);
     388                 :     boost::asio::use_service<
     389                 :         detail::asio_work_tracker_service<Executor>
     390               1 :       >(ec).work_started(executor_);
     391               1 :   }
     392                 : 
     393                 :   /** @brief Notifies that work has finished.
     394                 :    *
     395                 :    *  Decrements the work counter in the tracker service.
     396                 :    */
     397               1 :   void on_work_finished() const noexcept
     398                 :   {
     399               1 :     auto & ec = boost::asio::query(executor_, boost::asio::execution::context);
     400                 :     boost::asio::use_service<
     401                 :         detail::asio_work_tracker_service<Executor>
     402               1 :       >(ec).work_finished();
     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                 :    */
     413               1 :   std::coroutine_handle<> dispatch(continuation & c) const
     414                 :   {
     415               2 :     boost::asio::prefer(
     416               1 :         executor_,
     417               1 :         boost::asio::execution::allocator(
     418               1 :           std::pmr::polymorphic_allocator<void>(
     419               1 :             context().get_frame_allocator()
     420                 :             )
     421                 :           )
     422               1 :         ).execute(detail::asio_coroutine_unique_handle(c.h));
     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                 :    */
     433 MIS           0 :   void post(continuation & c) const
     434                 :   {
     435                 :     boost::asio::prefer(
     436               0 :       boost::asio::require(executor_, boost::asio::execution::blocking.never),
     437                 :       boost::asio::execution::relationship.fork,
     438               0 :       boost::asio::execution::allocator(
     439               0 :         std::pmr::polymorphic_allocator<void>(
     440               0 :           context().get_frame_allocator()
     441                 :           )
     442                 :         )
     443               0 :       ).execute(detail::asio_coroutine_unique_handle(c.h));
     444               0 :   }
     445                 : 
     446                 :   /** @brief Equality comparison. */
     447               0 :   bool operator==(const asio_boost_standard_executor & rhs) const noexcept
     448                 :   {
     449               0 :     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>
     481 HIT           4 : auto asio_spawn(ExecutorType exec, Awaitable && awaitable, Token token)
     482                 : {
     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                 :   
     582               4 :     boost_asio_init_promise_type(
     583                 :       boost_asio_init &, 
     584                 :       Handler & h, 
     585                 :       Ex & exec, 
     586                 :       Awaitable &)
     587               4 :         : handler(h), ex(exec) 
     588                 :     {
     589               4 :     }
     590                 : 
     591                 :     Handler & handler;
     592                 :     Ex &ex;
     593                 : 
     594               4 :     void get_return_object() {}
     595 MIS           0 :     void unhandled_exception() {throw;}
     596               0 :     void return_void() {}
     597                 : 
     598 HIT           4 :     std::suspend_never initial_suspend() noexcept {return {};}
     599 MIS           0 :     std::suspend_never   final_suspend() noexcept {return {};}
     600                 : 
     601                 :     struct completer 
     602                 :     {
     603                 :       Handler handler;
     604                 :       Ex ex;
     605                 :       args_type args;
     606                 : 
     607                 : 
     608 HIT           4 :       bool await_ready() const {return false;}
     609                 : 
     610               4 :       auto await_suspend(
     611                 :             std::coroutine_handle<boost_asio_init_promise_type> h) 
     612                 :       {
     613               4 :         auto handler_ =         
     614                 :             std::apply( 
     615               8 :               [&](auto ... args) 
     616                 :               {
     617               4 :                 return boost::asio::append(std::move(handler), 
     618               8 :                                            std::move(args)...);
     619                 :               },
     620               4 :               detail::decomposed_types(std::move(args)));
     621                 : 
     622               4 :         auto exec = boost::asio::get_associated_immediate_executor(
     623                 :                         handler_, 
     624               8 :                         asio_executor_adapter(std::move(ex)));
     625               4 :         h.destroy();
     626               4 :         boost::asio::dispatch(exec, std::move(handler_));
     627               4 :       }
     628 MIS           0 :       void await_resume() const {}
     629                 :     };
     630                 : 
     631 HIT           4 :     completer yield_value(args_type value)
     632                 :     {
     633               8 :       return {std::move(handler), std::move(ex), std::move(value)};
     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                 : 
     646               4 :       wrapper(Awaitable && r, const Ex &ex) 
     647               4 :           : r(std::move(r)), ex(ex)
     648                 :       {
     649               4 :       }
     650                 : 
     651               4 :       bool await_ready() {return r.await_ready(); }
     652                 : 
     653               1 :       auto await_suspend(
     654                 :           std::coroutine_handle<boost_asio_init_promise_type> tr)
     655                 :       {
     656               1 :         env.executor = ex;
     657               1 :         env.stop_token = stop_src.get_token();
     658                 :         cancel_slot = 
     659               1 :           boost::asio::get_associated_cancellation_slot(tr.promise().handler);
     660                 :           
     661               1 :         if (cancel_slot.is_connected())
     662 MIS           0 :           cancel_slot.assign(
     663               0 :               [this](boost::asio::cancellation_type ct)
     664                 :               {
     665               0 :                 if ((ct & boost::asio::cancellation_type::terminal)
     666               0 :                   != boost::asio::cancellation_type::none)
     667               0 :                   stop_src.request_stop(); 
     668                 :               });
     669 HIT           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>)
     673 MIS           0 :           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                 :         {
     678 HIT           1 :           c.h = r.await_suspend(tr, &env);
     679               1 :           return ex.dispatch(c);
     680                 :         }        
     681 MIS           0 :       }
     682                 :       
     683 HIT           4 :       completion_tuple_for_io_awaitable<Awaitable> await_resume()
     684                 :       {
     685               4 :         if (cancel_slot.is_connected())
     686 MIS           0 :           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                 :             {
     694 HIT           1 :               r.await_resume();
     695               1 :               return {std::exception_ptr()};
     696                 :             }
     697 MIS           0 :             catch (...)
     698                 :             {
     699               0 :               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
     719 HIT           3 :             return {r.await_resume()};
     720                 :         }
     721                 :       }
     722                 :     };
     723                 : 
     724               4 :     wrapper await_transform(Awaitable & r)
     725                 :     {
     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>
     734               4 :   void operator()(
     735                 :                   Handler ,
     736                 :                   Ex,
     737                 :                   Awaitable awaitable)
     738                 :   {
     739                 :     auto res = co_await awaitable;
     740                 :     co_yield std::move(res);
     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>
     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,
     763               3 :         completion_signature_for_io_awaitable<Awaitable>>(
     764                 :           boost_asio_init{},
     765               4 :           tk, std::move(ex), std::move(r)
     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                 : 
     808               3 :     explicit asio_boost_buffer_range(Sequence seq)
     809                 :         noexcept(std::is_nothrow_move_constructible_v<Sequence>)
     810               3 :         : seq_(std::move(seq))
     811                 :     {
     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                 : 
     819               6 :     iterator begin() const
     820                 :         noexcept(noexcept(boost::asio::buffer_sequence_begin(std::declval<const Sequence&>())))
     821                 :     {
     822               6 :         return iterator(boost::asio::buffer_sequence_begin(seq_));
     823                 :     }
     824                 : 
     825               6 :     iterator end() const
     826                 :         noexcept(noexcept(boost::asio::buffer_sequence_end(std::declval<const Sequence&>())))
     827                 :     {
     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                 : 
     843               3 : asio_mutable_buffer asio_buffer_transformer_t::
     844                 :     operator()(const boost::asio::mutable_buffer &mb) const noexcept
     845                 : {
     846               3 :   return {mb.data(), mb.size()};
     847                 : }
     848                 : 
     849               3 : asio_const_buffer asio_buffer_transformer_t::
     850                 :     operator()(const boost::asio::const_buffer &cb) const noexcept
     851                 : {
     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                 :   }
     887               3 : auto as_asio_buffer_sequence(const T & seq)
     888                 : {
     889               3 :   return detail::asio_boost_buffer_range(seq);
     890                 : }
     891                 : 
     892               2 : asio_const_buffer::operator boost::asio::const_buffer() const 
     893                 : {
     894               2 :   return {data(), size()};
     895                 : }
     896                 : 
     897                 : asio_mutable_buffer::operator boost::asio::const_buffer() const 
     898                 : {
     899                 :   return {data(), size()};
     900                 : }
     901                 : 
     902               2 : asio_mutable_buffer::operator boost::asio::mutable_buffer() const 
     903                 : {
     904               2 :   return {data(), size()};
     905                 : }
     906                 : 
     907                 : }
     908                 : 
     909                 : #endif //BOOST_CAPY_ASIO_BOOST_HPP
     910                 : 
        

Generated by: LCOV version 2.3