LCOV - code coverage report
Current view: top level - capy/ex - executor_ref.hpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 100.0 % 46 46
Test Date: 2026-08-14 20:51:18 Functions: 39.6 % 101 40 61

           TLA  Line data    Source code
       1                 : //
       2                 : // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
       3                 : // Copyright (c) 2026 Michael Vandeberg
       4                 : //
       5                 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
       6                 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
       7                 : //
       8                 : // Official repository: https://github.com/cppalliance/capy
       9                 : //
      10                 : 
      11                 : #ifndef BOOST_CAPY_EXECUTOR_REF_HPP
      12                 : #define BOOST_CAPY_EXECUTOR_REF_HPP
      13                 : 
      14                 : #include <boost/capy/detail/config.hpp>
      15                 : #include <boost/capy/detail/type_id.hpp>
      16                 : #include <boost/capy/continuation.hpp>
      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*, continuation&);
      36                 :     std::coroutine_handle<> (*dispatch)(void const*, continuation&);
      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 HIT           1 :     [](void const* p) noexcept -> execution_context& {
      46               1 :         return const_cast<Ex*>(static_cast<Ex const*>(p))->context();
      47                 :     },
      48                 :     // on_work_started
      49               2 :     [](void const* p) noexcept {
      50               1 :         const_cast<Ex*>(static_cast<Ex const*>(p))->on_work_started();
      51                 :     },
      52                 :     // on_work_finished
      53               2 :     [](void const* p) noexcept {
      54               1 :         const_cast<Ex*>(static_cast<Ex const*>(p))->on_work_finished();
      55                 :     },
      56                 :     // post
      57           34610 :     [](void const* p, continuation& c) {
      58           17305 :         static_cast<Ex const*>(p)->post(c);
      59                 :     },
      60                 :     // dispatch
      61             122 :     [](void const* p, continuation& c) -> std::coroutine_handle<> {
      62             122 :         return static_cast<Ex const*>(p)->dispatch(c);
      63                 :     },
      64                 :     // equals
      65               1 :     [](void const* a, void const* b) noexcept -> bool {
      66               1 :         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                 : /** Forwards `dispatch`/`post`/`context` calls through a non-owning, type-erased executor pointer.
      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. Executors of different types are therefore 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 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_continuation);
     103                 :     }
     104                 : 
     105                 :     thread_pool 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                 :     /** Construct a default instance.
     118                 : 
     119                 :         Constructs an empty `executor_ref`. `operator bool()` and
     120                 :         `operator==()` report the empty state; `context()`,
     121                 :         `on_work_started()`, `on_work_finished()`, `dispatch()`,
     122                 :         `post()`, and `target()` are undefined behavior until an
     123                 :         executor is assigned.
     124                 :     */
     125            3528 :     executor_ref() = default;
     126                 : 
     127                 :     /** Construct a copy.
     128                 : 
     129                 :         Copies the internal pointers, preserving identity.
     130                 :         This enables the same-executor optimization when passing
     131                 :         executor_ref through coroutine chains.
     132                 : 
     133                 :         @param other The reference to copy.
     134                 :     */
     135                 :     executor_ref(executor_ref const& other) = default;
     136                 : 
     137                 :     /** Copy assignment operator.
     138                 : 
     139                 :         @param other The reference to copy.
     140                 : 
     141                 :         @return A reference to `*this`.
     142                 :     */
     143                 :     executor_ref& operator=(executor_ref const& other) = default;
     144                 : 
     145                 :     /** Constructs from any executor type.
     146                 : 
     147                 :         Captures a reference to the given executor and stores a pointer
     148                 :         to the type-specific vtable. The executor must remain valid for
     149                 :         the lifetime of this `executor_ref` instance.
     150                 : 
     151                 :         @param ex The executor to wrap. Must satisfy the `Executor`
     152                 :                   concept. A pointer to this object is stored
     153                 :                   internally; the executor must outlive this wrapper.
     154                 :     */
     155                 : #if defined(__GNUC__) && !defined(__clang__)
     156                 :     // GCC constraint satisfaction caching bug workaround
     157                 :     template<class Ex,
     158                 :         std::enable_if_t<!std::is_same_v<
     159                 :             std::decay_t<Ex>, executor_ref>, int> = 0>
     160                 : #else
     161                 :     template<class Ex>
     162                 :         requires (!std::same_as<std::decay_t<Ex>, executor_ref>)
     163                 : #endif
     164           32430 :     executor_ref(Ex const& ex) noexcept
     165           32430 :         : ex_(&ex)
     166           32430 :         , vt_(&detail::vtable_for<Ex>)
     167                 :     {
     168           32430 :     }
     169                 : 
     170                 :     /** Returns true if this instance holds a valid executor.
     171                 : 
     172                 :         @return `true` if constructed with an executor, `false` if
     173                 :                 default-constructed.
     174                 :     */
     175               6 :     explicit operator bool() const noexcept
     176                 :     {
     177               6 :         return ex_ != nullptr;
     178                 :     }
     179                 : 
     180                 :     /** Returns a reference to the associated execution context.
     181                 : 
     182                 :         @return A reference to the execution context.
     183                 : 
     184                 :         @pre This instance was constructed with a valid executor.
     185                 :     */
     186               1 :     execution_context& context() const noexcept
     187                 :     {
     188               1 :         return vt_->context(ex_);
     189                 :     }
     190                 : 
     191                 :     /** Informs the executor that work is beginning.
     192                 : 
     193                 :         Must be paired with a subsequent call to `on_work_finished()`.
     194                 : 
     195                 :         @pre This instance was constructed with a valid executor.
     196                 :     */
     197               1 :     void on_work_started() const noexcept
     198                 :     {
     199               1 :         vt_->on_work_started(ex_);
     200               1 :     }
     201                 : 
     202                 :     /** Informs the executor that work has completed.
     203                 : 
     204                 :         @pre A preceding call to `on_work_started()` was made.
     205                 :         @pre This instance was constructed with a valid executor.
     206                 :     */
     207               1 :     void on_work_finished() const noexcept
     208                 :     {
     209               1 :         vt_->on_work_finished(ex_);
     210               1 :     }
     211                 : 
     212                 :     /** Dispatches a continuation through the wrapped executor.
     213                 : 
     214                 :         Returns a handle for symmetric transfer. If running in the
     215                 :         executor's thread, returns `c.h`. Otherwise, posts the
     216                 :         continuation for later execution and returns
     217                 :         `std::noop_coroutine()`.
     218                 : 
     219                 :         @param c The continuation to dispatch for resumption.
     220                 :                  Must remain at a stable address until dequeued.
     221                 : 
     222                 :         @return A handle for symmetric transfer or `std::noop_coroutine()`.
     223                 : 
     224                 :         @pre This instance was constructed with a valid executor.
     225                 :     */
     226             122 :     std::coroutine_handle<> dispatch(continuation& c) const
     227                 :     {
     228             122 :         return vt_->dispatch(ex_, c);
     229                 :     }
     230                 : 
     231                 :     /** Posts a continuation to the wrapped executor.
     232                 : 
     233                 :         Posts the continuation to the executor for later execution
     234                 :         and returns. The caller should transfer to `std::noop_coroutine()`
     235                 :         after calling this.
     236                 : 
     237                 :         @param c The continuation to post for resumption.
     238                 :                  Must remain at a stable address until dequeued.
     239                 : 
     240                 :         @pre This instance was constructed with a valid executor.
     241                 :     */
     242           17305 :     void post(continuation& c) const
     243                 :     {
     244           17305 :         vt_->post(ex_, c);
     245           17305 :     }
     246                 : 
     247                 :     /** Compares two executor references for equality.
     248                 : 
     249                 :         Two `executor_ref` instances are equal if they wrap
     250                 :         executors of the same type that compare equal.
     251                 : 
     252                 :         @param other The executor reference to compare against.
     253                 : 
     254                 :         @return `true` if both wrap equal executors of the same type.
     255                 :     */
     256               7 :     bool operator==(executor_ref const& other) const noexcept
     257                 :     {
     258               7 :         if (ex_ == other.ex_)
     259               5 :             return true;
     260               2 :         if (vt_ != other.vt_)
     261               1 :             return false;
     262               1 :         return vt_->equals(ex_, other.ex_);
     263                 :     }
     264                 : 
     265                 :     /** Return a pointer to the wrapped executor if it matches
     266                 :         the requested type.
     267                 : 
     268                 :         Performs a type check against the stored executor and
     269                 :         returns a typed pointer when the types match, or
     270                 :         `nullptr` otherwise. Analogous to
     271                 :         `std::any_cast< Executor >( &a )`.
     272                 : 
     273                 :         @tparam Executor The executor type to retrieve.
     274                 : 
     275                 :         @return A pointer to the underlying executor, or
     276                 :             `nullptr` if the type does not match.
     277                 :     */
     278                 :     template< typename Executor >
     279               2 :     const Executor* target() const
     280                 :     {
     281               2 :         if ( *vt_->type_id == detail::type_id< Executor >() )
     282               1 :            return static_cast< Executor const* >( ex_ );
     283               1 :         return nullptr;
     284                 :     }
     285                 : 
     286                 :     /// @copydoc target() const
     287                 :     template< typename Executor>
     288               2 :     Executor* target()
     289                 :     {
     290               2 :         if ( *vt_->type_id == detail::type_id< Executor >() )
     291                 :            return const_cast< Executor* >(
     292               1 :                static_cast< Executor const* >( ex_ ));
     293               1 :         return nullptr;
     294                 :     }
     295                 : };
     296                 : 
     297                 : } // capy
     298                 : } // boost
     299                 : 
     300                 : #endif
        

Generated by: LCOV version 2.3