LCOV - code coverage report
Current view: top level - boost/capy/ex - any_executor_ref.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 73.3 % 30 22
Test Date: 2026-01-18 18:26:31 Functions: 32.8 % 61 20

            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_EXECUTOR_REF_HPP
      11              : #define BOOST_CAPY_ANY_EXECUTOR_REF_HPP
      12              : 
      13              : #include <boost/capy/detail/config.hpp>
      14              : #include <boost/capy/ex/any_coro.hpp>
      15              : 
      16              : #include <concepts>
      17              : #include <coroutine>
      18              : #include <type_traits>
      19              : #include <utility>
      20              : 
      21              : namespace boost {
      22              : namespace capy {
      23              : 
      24              : class execution_context;
      25              : 
      26              : namespace detail {
      27              : 
      28              : /** Virtual function table for type-erased executor operations. */
      29              : struct executor_vtable
      30              : {
      31              :     execution_context& (*context)(void const*) noexcept;
      32              :     void (*on_work_started)(void const*) noexcept;
      33              :     void (*on_work_finished)(void const*) noexcept;
      34              :     void (*post)(void const*, std::coroutine_handle<>);
      35              :     std::coroutine_handle<> (*dispatch)(void const*, std::coroutine_handle<>);
      36              :     bool (*equals)(void const*, void const*) noexcept;
      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           70 :     [](void const* p, std::coroutine_handle<> h) {
      56           35 :         static_cast<Ex const*>(p)->post(h);
      57              :     },
      58              :     // dispatch
      59          110 :     [](void const* p, std::coroutine_handle<> h) -> std::coroutine_handle<> {
      60          110 :         return static_cast<Ex const*>(p)->dispatch(h);
      61              :     },
      62              :     // equals
      63            4 :     [](void const* a, void const* b) noexcept -> bool {
      64            4 :         return *static_cast<Ex const*>(a) == *static_cast<Ex const*>(b);
      65              :     }
      66              : };
      67              : 
      68              : } // detail
      69              : 
      70              : /** A type-erased reference wrapper for executor objects.
      71              : 
      72              :     This class provides type erasure for any executor type, enabling
      73              :     runtime polymorphism without virtual functions or allocation.
      74              :     It stores a pointer to the original executor and a pointer to a
      75              :     static vtable, allowing executors of different types to be stored
      76              :     uniformly while satisfying the full `Executor` concept.
      77              : 
      78              :     @par Reference Semantics
      79              :     This class has reference semantics: it does not allocate or own
      80              :     the wrapped executor. Copy operations simply copy the internal
      81              :     pointers. The caller must ensure the referenced executor outlives
      82              :     all `any_executor_ref` instances that wrap it.
      83              : 
      84              :     @par Thread Safety
      85              :     The `any_executor_ref` itself is not thread-safe for concurrent
      86              :     modification, but its executor operations are safe to call
      87              :     concurrently if the underlying executor supports it.
      88              : 
      89              :     @par Executor Concept
      90              :     This class satisfies the `Executor` concept, making it usable
      91              :     anywhere a concrete executor is expected.
      92              : */
      93              : class any_executor_ref
      94              : {
      95              :     void const* ex_ = nullptr;
      96              :     detail::executor_vtable const* vt_ = nullptr;
      97              : 
      98              : public:
      99              :     /** Default constructor.
     100              : 
     101              :         Constructs an empty `any_executor_ref`. Calling any executor
     102              :         operations on a default-constructed instance results in
     103              :         undefined behavior.
     104              :     */
     105          533 :     any_executor_ref() = default;
     106              : 
     107              :     /** Copy constructor.
     108              : 
     109              :         Copies the internal pointers, preserving identity.
     110              :         This enables the same-executor optimization when passing
     111              :         any_executor_ref through coroutine chains.
     112              :     */
     113              :     any_executor_ref(any_executor_ref const&) = default;
     114              : 
     115              :     /** Copy assignment operator. */
     116              :     any_executor_ref& operator=(any_executor_ref const&) = default;
     117              : 
     118              :     /** Constructs from any executor type.
     119              : 
     120              :         Captures a reference to the given executor and stores a pointer
     121              :         to the type-specific vtable. The executor must remain valid for
     122              :         the lifetime of this `any_executor_ref` instance.
     123              : 
     124              :         @param ex The executor to wrap. Must satisfy the `Executor`
     125              :                   concept. A pointer to this object is stored
     126              :                   internally; the executor must outlive this wrapper.
     127              :     */
     128              :     template<class Ex>
     129              :         requires (!std::same_as<std::decay_t<Ex>, any_executor_ref>)
     130          461 :     any_executor_ref(Ex const& ex) noexcept
     131          461 :         : ex_(&ex)
     132          461 :         , vt_(&detail::vtable_for<Ex>)
     133              :     {
     134          461 :     }
     135              : 
     136              :     /** Returns true if this instance holds a valid executor.
     137              : 
     138              :         @return `true` if constructed with an executor, `false` if
     139              :                 default-constructed.
     140              :     */
     141            2 :     explicit operator bool() const noexcept
     142              :     {
     143            2 :         return ex_ != nullptr;
     144              :     }
     145              : 
     146              :     /** Returns a reference to the associated execution context.
     147              : 
     148              :         @return A reference to the execution context.
     149              : 
     150              :         @pre This instance was constructed with a valid executor.
     151              :     */
     152              :     execution_context& context() const noexcept
     153              :     {
     154              :         return vt_->context(ex_);
     155              :     }
     156              : 
     157              :     /** Informs the executor that work is beginning.
     158              : 
     159              :         Must be paired with a subsequent call to `on_work_finished()`.
     160              : 
     161              :         @pre This instance was constructed with a valid executor.
     162              :     */
     163              :     void on_work_started() const noexcept
     164              :     {
     165              :         vt_->on_work_started(ex_);
     166              :     }
     167              : 
     168              :     /** Informs the executor that work has completed.
     169              : 
     170              :         @pre A preceding call to `on_work_started()` was made.
     171              :         @pre This instance was constructed with a valid executor.
     172              :     */
     173              :     void on_work_finished() const noexcept
     174              :     {
     175              :         vt_->on_work_finished(ex_);
     176              :     }
     177              : 
     178              :     /** Dispatches a coroutine handle through the wrapped executor.
     179              : 
     180              :         Invokes the executor's `dispatch()` operation with the given
     181              :         coroutine handle, returning a handle suitable for symmetric
     182              :         transfer.
     183              : 
     184              :         @param h The coroutine handle to dispatch for resumption.
     185              : 
     186              :         @return A coroutine handle that the caller may use for symmetric
     187              :                 transfer, or `std::noop_coroutine()` if the executor
     188              :                 posted the work for later execution.
     189              : 
     190              :         @pre This instance was constructed with a valid executor.
     191              :     */
     192          110 :     any_coro dispatch(any_coro h) const
     193              :     {
     194          110 :         return vt_->dispatch(ex_, h);
     195              :     }
     196              : 
     197              :     /** Posts a coroutine handle to the wrapped executor.
     198              : 
     199              :         Posts the coroutine handle to the executor for later execution
     200              :         and returns. The caller should transfer to `std::noop_coroutine()`
     201              :         after calling this.
     202              : 
     203              :         @param h The coroutine handle to post for resumption.
     204              : 
     205              :         @pre This instance was constructed with a valid executor.
     206              :     */
     207           35 :     void post(any_coro h) const
     208              :     {
     209           35 :         vt_->post(ex_, h);
     210           35 :     }
     211              : 
     212              :     /** Compares two executor references for equality.
     213              : 
     214              :         Two `any_executor_ref` instances are equal if they wrap
     215              :         executors of the same type that compare equal.
     216              : 
     217              :         @param other The executor reference to compare against.
     218              : 
     219              :         @return `true` if both wrap equal executors of the same type.
     220              :     */
     221            4 :     bool operator==(any_executor_ref const& other) const noexcept
     222              :     {
     223            4 :         if (vt_ != other.vt_)
     224            0 :             return false;
     225            4 :         if (!vt_)
     226            0 :             return true;
     227            4 :         return vt_->equals(ex_, other.ex_);
     228              :     }
     229              : };
     230              : 
     231              : } // capy
     232              : } // boost
     233              : 
     234              : #endif
        

Generated by: LCOV version 2.3