LCOV - code coverage report
Current view: top level - capy/ex - executor_ref.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 78.8 % 33 26
Test Date: 2026-02-04 19:49:07 Functions: 32.9 % 76 25

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

Generated by: LCOV version 2.3