LCOV - code coverage report
Current view: top level - boost/capy/ex - any_dispatcher.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 100.0 % 15 15
Test Date: 2026-01-17 12:16:19 Functions: 85.7 % 21 18

            Line data    Source code
       1              : //
       2              : // Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot 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_ANY_DISPATCHER_HPP
      11              : #define BOOST_CAPY_ANY_DISPATCHER_HPP
      12              : 
      13              : #include <boost/capy/detail/config.hpp>
      14              : #include <boost/capy/ex/any_coro.hpp>
      15              : #include <boost/capy/concept/dispatcher.hpp>
      16              : #include <boost/capy/concept/executor.hpp>
      17              : 
      18              : #include <concepts>
      19              : #include <type_traits>
      20              : #include <utility>
      21              : 
      22              : namespace boost {
      23              : namespace capy {
      24              : 
      25              : /** A type-erased wrapper for dispatcher objects.
      26              : 
      27              :     This class provides type erasure for any type satisfying the `dispatcher`
      28              :     concept, enabling runtime polymorphism without virtual functions. It stores
      29              :     a pointer to the original dispatcher and a function pointer to invoke it,
      30              :     allowing dispatchers of different types to be stored uniformly.
      31              : 
      32              :     @par Thread Safety
      33              :     The `any_dispatcher` itself is not thread-safe for concurrent modification,
      34              :     but `operator()` is const and safe to call concurrently if the underlying
      35              :     dispatcher supports concurrent dispatch.
      36              : 
      37              :     @par Lifetime
      38              :     The `any_dispatcher` stores a pointer to the original dispatcher object.
      39              :     The caller must ensure the referenced dispatcher outlives the `any_dispatcher`
      40              :     instance. This is typically satisfied when the dispatcher is an executor
      41              :     stored in a coroutine promise or service provider.
      42              : 
      43              :     @see dispatcher
      44              : */
      45              : class any_dispatcher
      46              : {
      47              :     void const* d_ = nullptr;
      48              :     any_coro(*f_)(void const*, any_coro) = nullptr;
      49              : 
      50              : public:
      51              :     /** Default constructor.
      52              : 
      53              :         Constructs an empty `any_dispatcher`. Calling `operator()` on a
      54              :         default-constructed instance results in undefined behavior.
      55              :     */
      56          533 :     any_dispatcher() = default;
      57              : 
      58              :     /** Copy constructor.
      59              : 
      60              :         Copies the internal pointer and function, preserving identity.
      61              :         This enables the same-dispatcher optimization when passing
      62              :         any_dispatcher through coroutine chains.
      63              :     */
      64              :     any_dispatcher(any_dispatcher const&) = default;
      65              : 
      66              :     /** Copy assignment operator. */
      67              :     any_dispatcher& operator=(any_dispatcher const&) = default;
      68              : 
      69              :     /** Constructs from any dispatcher type.
      70              : 
      71              :         Captures a reference to the given dispatcher and stores a type-erased
      72              :         invocation function. The dispatcher must remain valid for the lifetime
      73              :         of this `any_dispatcher` instance.
      74              : 
      75              :         @param d The dispatcher to wrap. Must satisfy the `dispatcher` concept.
      76              :                  A pointer to this object is stored internally; the dispatcher
      77              :                  must outlive this wrapper.
      78              :     */
      79              :     template<dispatcher D>
      80              :         requires (!std::same_as<std::decay_t<D>, any_dispatcher>)
      81          454 :     any_dispatcher(D const& d)
      82          454 :         : d_(&d)
      83          563 :         , f_([](void const* pd, any_coro h) {
      84          123 :                 return static_cast<D const*>(pd)->operator()(h);
      85              :             })
      86              :     {
      87          454 :     }
      88              : 
      89              :     /** Returns true if this instance holds a valid dispatcher.
      90              : 
      91              :         @return `true` if constructed with a dispatcher, `false` if
      92              :                 default-constructed.
      93              :     */
      94              :     explicit operator bool() const noexcept
      95              :     {
      96              :         return d_ != nullptr;
      97              :     }
      98              : 
      99              :     /** Compares two dispatchers for identity.
     100              : 
     101              :         Two `any_dispatcher` instances are equal if they wrap the same
     102              :         underlying dispatcher object (pointer equality). This enables
     103              :         the affinity optimization: when `caller_dispatcher == my_dispatcher`,
     104              :         symmetric transfer can proceed without a `running_in_this_thread()`
     105              :         check.
     106              : 
     107              :         @param other The dispatcher to compare against.
     108              : 
     109              :         @return `true` if both wrap the same dispatcher object.
     110              :     */
     111              :     bool operator==(any_dispatcher const& other) const noexcept
     112              :     {
     113              :         return d_ == other.d_;
     114              :     }
     115              : 
     116              :     /** Dispatches a coroutine handle through the wrapped dispatcher.
     117              : 
     118              :         Invokes the stored dispatcher with the given coroutine handle,
     119              :         returning a handle suitable for symmetric transfer.
     120              : 
     121              :         @param h The coroutine handle to dispatch for resumption.
     122              : 
     123              :         @return A coroutine handle that the caller may use for symmetric
     124              :                 transfer, or `std::noop_coroutine()` if the dispatcher
     125              :                 posted the work for later execution.
     126              : 
     127              :         @pre This instance was constructed with a valid dispatcher
     128              :              (not default-constructed).
     129              :     */
     130          123 :     any_coro operator()(any_coro h) const
     131              :     {
     132          123 :         return f_(d_, h);
     133              :     }
     134              : };
     135              : 
     136              : //------------------------------------------------------------------------------
     137              : 
     138              : /** A dispatcher that calls executor::post().
     139              : 
     140              :     Adapts an executor's post() operation to the dispatcher
     141              :     interface. When invoked, posts the coroutine and returns
     142              :     noop_coroutine for the caller to transfer to.
     143              : 
     144              :     @tparam Executor The executor type.
     145              : */
     146              : template<executor Executor>
     147              : class post_dispatcher
     148              : {
     149              :     Executor ex_;
     150              : 
     151              : public:
     152           24 :     explicit post_dispatcher(Executor ex) noexcept
     153           24 :         : ex_(std::move(ex))
     154           24 :     {}
     155              : 
     156            6 :     Executor const& get_inner_executor() const noexcept { return ex_; }
     157              : 
     158           14 :     any_coro operator()(any_coro h) const
     159              :     {
     160           14 :         ex_.post(h);
     161           14 :         return std::noop_coroutine();
     162              :     }
     163              : };
     164              : 
     165              : /** A dispatcher that calls executor::defer().
     166              : 
     167              :     Adapts an executor's defer() operation to the dispatcher
     168              :     interface. When invoked, defers the coroutine and returns
     169              :     noop_coroutine for the caller to transfer to.
     170              : 
     171              :     @tparam Executor The executor type.
     172              : */
     173              : template<executor Executor>
     174              : class defer_dispatcher
     175              : {
     176              :     Executor ex_;
     177              : 
     178              : public:
     179              :     explicit defer_dispatcher(Executor ex) noexcept
     180              :         : ex_(std::move(ex))
     181              :     {}
     182              : 
     183              :     Executor const& get_inner_executor() const noexcept { return ex_; }
     184              : 
     185              :     any_coro operator()(any_coro h) const
     186              :     {
     187              :         ex_.defer(h);
     188              :         return std::noop_coroutine();
     189              :     }
     190              : };
     191              : 
     192              : template<executor E> post_dispatcher(E) -> post_dispatcher<E>;
     193              : template<executor E> defer_dispatcher(E) -> defer_dispatcher<E>;
     194              : 
     195              : } // capy
     196              : } // boost
     197              : 
     198              : #endif
        

Generated by: LCOV version 2.3