LCOV - code coverage report
Current view: top level - capy/ex - executor_ref.hpp (source / functions) Coverage Total Hit
Test: coverage_remapped.info Lines: 78.9 % 38 30
Test Date: 2026-02-12 22:58:59 Functions: 37.1 % 70 26

            Line data    Source code
       1              : //
       2              : // Copyright (c) 2025 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_EXECUTOR_REF_HPP
      11              : #define BOOST_CAPY_EXECUTOR_REF_HPP
      12              : 
      13              : #include <boost/capy/detail/config.hpp>
      14              : #include <boost/capy/detail/type_id.hpp>
      15              : #include <concepts>
      16              : #include <coroutine>
      17              : #include <type_traits>
      18              : #include <utility>
      19              : 
      20              : namespace boost {
      21              : namespace capy {
      22              : 
      23              : class execution_context;
      24              : 
      25              : namespace detail {
      26              : 
      27              : /** Virtual function table for type-erased executor operations. */
      28              : struct executor_vtable
      29              : {
      30              :     execution_context& (*context)(void const*) noexcept;
      31              :     void (*on_work_started)(void const*) noexcept;
      32              :     void (*on_work_finished)(void const*) noexcept;
      33              :     void (*post)(void const*, std::coroutine_handle<>);
      34              :     std::coroutine_handle<> (*dispatch)(void const*, std::coroutine_handle<>);
      35              :     bool (*equals)(void const*, void const*) noexcept;
      36              :     detail::type_info const* type_id;
      37              : };
      38              : 
      39              : /** Vtable instance for a specific executor type. */
      40              : template<class Ex>
      41              : inline constexpr executor_vtable vtable_for = {
      42              :     // context
      43            0 :     [](void const* p) noexcept -> execution_context& {
      44            0 :         return const_cast<Ex*>(static_cast<Ex const*>(p))->context();
      45              :     },
      46              :     // on_work_started
      47            0 :     [](void const* p) noexcept {
      48            0 :         const_cast<Ex*>(static_cast<Ex const*>(p))->on_work_started();
      49              :     },
      50              :     // on_work_finished
      51            0 :     [](void const* p) noexcept {
      52            0 :         const_cast<Ex*>(static_cast<Ex const*>(p))->on_work_finished();
      53              :     },
      54              :     // post
      55          734 :     [](void const* p, std::coroutine_handle<> h) {
      56          367 :         static_cast<Ex const*>(p)->post(h);
      57              :     },
      58              :     // dispatch
      59          109 :     [](void const* p, std::coroutine_handle<> h) -> std::coroutine_handle<> {
      60          109 :         return static_cast<Ex const*>(p)->dispatch(h);
      61              :     },
      62              :     // equals
      63            1 :     [](void const* a, void const* b) noexcept -> bool {
      64            1 :         return *static_cast<Ex const*>(a) == *static_cast<Ex const*>(b);
      65              :     },
      66              :     // type_id
      67              :     &detail::type_id<Ex>()
      68              : };
      69              : 
      70              : } // detail
      71              : 
      72              : /** A type-erased reference wrapper for executor objects.
      73              : 
      74              :     This class provides type erasure for any executor type, enabling
      75              :     runtime polymorphism without virtual functions or allocation.
      76              :     It stores a pointer to the original executor and a pointer to a
      77              :     static vtable, allowing executors of different types to be stored
      78              :     uniformly while satisfying the full `Executor` concept.
      79              : 
      80              :     @par Reference Semantics
      81              :     This class has reference semantics: it does not allocate or own
      82              :     the wrapped executor. Copy operations simply copy the internal
      83              :     pointers. The caller must ensure the referenced executor outlives
      84              :     all `executor_ref` instances that wrap it.
      85              : 
      86              :     @par Thread Safety
      87              :     The `executor_ref` itself is not thread-safe for concurrent
      88              :     modification, but its executor operations are safe to call
      89              :     concurrently if the underlying executor supports it.
      90              : 
      91              :     @par Executor Concept
      92              :     This class satisfies the `Executor` concept, making it usable
      93              :     anywhere a concrete executor is expected.
      94              : 
      95              :     @par Example
      96              :     @code
      97              :     void store_executor(executor_ref ex)
      98              :     {
      99              :         if(ex)
     100              :             ex.post(my_coroutine);
     101              :     }
     102              : 
     103              :     io_context ctx;
     104              :     store_executor(ctx.get_executor());
     105              :     @endcode
     106              : 
     107              :     @see any_executor, Executor
     108              : */
     109              : class executor_ref
     110              : {
     111              :     void const* ex_ = nullptr;
     112              :     detail::executor_vtable const* vt_ = nullptr;
     113              : 
     114              : public:
     115              :     /** Default constructor.
     116              : 
     117              :         Constructs an empty `executor_ref`. Calling any executor
     118              :         operations on a default-constructed instance results in
     119              :         undefined behavior.
     120              :     */
     121         2400 :     executor_ref() = default;
     122              : 
     123              :     /** Copy constructor.
     124              : 
     125              :         Copies the internal pointers, preserving identity.
     126              :         This enables the same-executor optimization when passing
     127              :         executor_ref through coroutine chains.
     128              :     */
     129              :     executor_ref(executor_ref const&) = default;
     130              : 
     131              :     /** Copy assignment operator. */
     132              :     executor_ref& operator=(executor_ref const&) = default;
     133              : 
     134              :     /** Constructs from any executor type.
     135              : 
     136              :         Captures a reference to the given executor and stores a pointer
     137              :         to the type-specific vtable. The executor must remain valid for
     138              :         the lifetime of this `executor_ref` instance.
     139              : 
     140              :         @param ex The executor to wrap. Must satisfy the `Executor`
     141              :                   concept. A pointer to this object is stored
     142              :                   internally; the executor must outlive this wrapper.
     143              :     */
     144              : #if defined(__GNUC__) && !defined(__clang__)
     145              :     // GCC constraint satisfaction caching bug workaround
     146              :     template<class Ex,
     147              :         std::enable_if_t<!std::is_same_v<
     148              :             std::decay_t<Ex>, executor_ref>, int> = 0>
     149              : #else
     150              :     template<class Ex>
     151              :         requires (!std::same_as<std::decay_t<Ex>, executor_ref>)
     152              : #endif
     153         2409 :     executor_ref(Ex const& ex) noexcept
     154         2409 :         : ex_(&ex)
     155         2409 :         , vt_(&detail::vtable_for<Ex>)
     156              :     {
     157         2409 :     }
     158              : 
     159              :     /** Returns true if this instance holds a valid executor.
     160              : 
     161              :         @return `true` if constructed with an executor, `false` if
     162              :                 default-constructed.
     163              :     */
     164            6 :     explicit operator bool() const noexcept
     165              :     {
     166            6 :         return ex_ != nullptr;
     167              :     }
     168              : 
     169              :     /** Returns a reference to the associated execution context.
     170              : 
     171              :         @return A reference to the execution context.
     172              : 
     173              :         @pre This instance was constructed with a valid executor.
     174              :     */
     175              :     execution_context& context() const noexcept
     176              :     {
     177              :         return vt_->context(ex_);
     178              :     }
     179              : 
     180              :     /** Informs the executor that work is beginning.
     181              : 
     182              :         Must be paired with a subsequent call to `on_work_finished()`.
     183              : 
     184              :         @pre This instance was constructed with a valid executor.
     185              :     */
     186              :     void on_work_started() const noexcept
     187              :     {
     188              :         vt_->on_work_started(ex_);
     189              :     }
     190              : 
     191              :     /** Informs the executor that work has completed.
     192              : 
     193              :         @pre A preceding call to `on_work_started()` was made.
     194              :         @pre This instance was constructed with a valid executor.
     195              :     */
     196              :     void on_work_finished() const noexcept
     197              :     {
     198              :         vt_->on_work_finished(ex_);
     199              :     }
     200              : 
     201              :     /** Dispatches a coroutine handle through the wrapped executor.
     202              : 
     203              :         Returns a handle for symmetric transfer. If running in the
     204              :         executor's thread, returns `h`. Otherwise, posts the coroutine
     205              :         for later execution and returns `std::noop_coroutine()`.
     206              : 
     207              :         @param h The coroutine handle to dispatch for resumption.
     208              : 
     209              :         @return A handle for symmetric transfer or `std::noop_coroutine()`.
     210              : 
     211              :         @pre This instance was constructed with a valid executor.
     212              :     */
     213          109 :     std::coroutine_handle<> dispatch(std::coroutine_handle<> h) const
     214              :     {
     215          109 :         return vt_->dispatch(ex_, h);
     216              :     }
     217              : 
     218              :     /** Posts a coroutine handle to the wrapped executor.
     219              : 
     220              :         Posts the coroutine handle to the executor for later execution
     221              :         and returns. The caller should transfer to `std::noop_coroutine()`
     222              :         after calling this.
     223              : 
     224              :         @param h The coroutine handle to post for resumption.
     225              : 
     226              :         @pre This instance was constructed with a valid executor.
     227              :     */
     228          367 :     void post(std::coroutine_handle<> h) const
     229              :     {
     230          367 :         vt_->post(ex_, h);
     231          367 :     }
     232              : 
     233              :     /** Compares two executor references for equality.
     234              : 
     235              :         Two `executor_ref` instances are equal if they wrap
     236              :         executors of the same type that compare equal.
     237              : 
     238              :         @param other The executor reference to compare against.
     239              : 
     240              :         @return `true` if both wrap equal executors of the same type.
     241              :     */
     242            6 :     bool operator==(executor_ref const& other) const noexcept
     243              :     {
     244            6 :         if (ex_ == other.ex_)
     245            5 :             return true;
     246            1 :         if (vt_ != other.vt_)
     247            0 :             return false;
     248            1 :         return vt_->equals(ex_, other.ex_);
     249              :     }
     250              : 
     251              :     /** Return a pointer to the wrapped executor if it matches
     252              :         the requested type.
     253              : 
     254              :         Performs a type check against the stored executor and
     255              :         returns a typed pointer when the types match, or
     256              :         `nullptr` otherwise. Analogous to
     257              :         `std::any_cast< Executor >( &a )`.
     258              : 
     259              :         @tparam Executor The executor type to retrieve.
     260              : 
     261              :         @return A pointer to the underlying executor, or
     262              :             `nullptr` if the type does not match.
     263              :     */
     264              :     template< typename Executor >
     265            1 :     const Executor* target() const
     266              :     {
     267            1 :         if ( *vt_->type_id == detail::type_id< Executor >() )
     268            1 :            return static_cast< Executor const* >( ex_ );
     269            0 :         return nullptr;
     270              :     }
     271              : 
     272              :     /// @copydoc target() const
     273              :     template< typename Executor>
     274            2 :     Executor* target()
     275              :     {
     276            2 :         if ( *vt_->type_id == detail::type_id< Executor >() )
     277              :            return const_cast< Executor* >(
     278            1 :                static_cast< Executor const* >( ex_ ));
     279            1 :         return nullptr;
     280              :     }
     281              : };
     282              : 
     283              : } // capy
     284              : } // boost
     285              : 
     286              : #endif
        

Generated by: LCOV version 2.3