LCOV - code coverage report
Current view: top level - capy/asio - executor_adapter.hpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 90.9 % 33 30 3
Test Date: 2026-05-01 02:54:45 Functions: 64.6 % 65 42 23

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

Generated by: LCOV version 2.3