LCOV - code coverage report
Current view: top level - boost/capy/ex - any_executor.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 75.5 % 53 40
Test Date: 2026-01-30 23:43:15 Functions: 78.3 % 23 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_EXECUTOR_HPP
      11              : #define BOOST_CAPY_ANY_EXECUTOR_HPP
      12              : 
      13              : #include <boost/capy/detail/config.hpp>
      14              : #include <boost/capy/coro.hpp>
      15              : 
      16              : #include <concepts>
      17              : #include <coroutine>
      18              : #include <memory>
      19              : #include <type_traits>
      20              : #include <typeinfo>
      21              : 
      22              : namespace boost {
      23              : namespace capy {
      24              : 
      25              : class execution_context;
      26              : template<typename> class strand;
      27              : 
      28              : namespace detail {
      29              : 
      30              : template<typename T>
      31              : struct is_strand_type : std::false_type {};
      32              : 
      33              : template<typename E>
      34              : struct is_strand_type<strand<E>> : std::true_type {};
      35              : 
      36              : } // detail
      37              : 
      38              : /** A type-erased wrapper for executor objects.
      39              : 
      40              :     This class provides type erasure for any executor type, enabling
      41              :     runtime polymorphism with automatic memory management via shared
      42              :     ownership. It stores a shared pointer to a polymorphic wrapper,
      43              :     allowing executors of different types to be stored uniformly
      44              :     while satisfying the full `Executor` concept.
      45              : 
      46              :     @par Value Semantics
      47              : 
      48              :     This class has value semantics with shared ownership. Copy and
      49              :     move operations are cheap, simply copying the internal shared
      50              :     pointer. Multiple `any_executor` instances may share the same
      51              :     underlying executor. Move operations do not invalidate the
      52              :     source; there is no moved-from state.
      53              : 
      54              :     @par Default State
      55              : 
      56              :     A default-constructed `any_executor` holds no executor. Calling
      57              :     executor operations on a default-constructed instance results
      58              :     in undefined behavior. Use `operator bool()` to check validity.
      59              : 
      60              :     @par Thread Safety
      61              : 
      62              :     The `any_executor` itself is thread-safe for concurrent reads.
      63              :     Concurrent modification requires external synchronization.
      64              :     Executor operations are safe to call concurrently if the
      65              :     underlying executor supports it.
      66              : 
      67              :     @par Executor Concept
      68              : 
      69              :     This class satisfies the `Executor` concept, making it usable
      70              :     anywhere a concrete executor is expected.
      71              : 
      72              :     @see executor_ref, Executor
      73              : */
      74              : class any_executor
      75              : {
      76              :     struct impl_base;
      77              : 
      78              :     std::shared_ptr<impl_base> p_;
      79              : 
      80              :     struct impl_base
      81              :     {
      82           16 :         virtual ~impl_base() = default;
      83              :         virtual execution_context& context() const noexcept = 0;
      84              :         virtual void on_work_started() const noexcept = 0;
      85              :         virtual void on_work_finished() const noexcept = 0;
      86              :         virtual std::coroutine_handle<> dispatch(std::coroutine_handle<>) const = 0;
      87              :         virtual void post(std::coroutine_handle<>) const = 0;
      88              :         virtual bool equals(impl_base const*) const noexcept = 0;
      89              :         virtual std::type_info const& target_type() const noexcept = 0;
      90              :     };
      91              : 
      92              :     template<class Ex>
      93              :     struct impl final : impl_base
      94              :     {
      95              :         Ex ex_;
      96              : 
      97              :         template<class Ex1>
      98           16 :         explicit impl(Ex1&& ex)
      99           16 :             : ex_(std::forward<Ex1>(ex))
     100              :         {
     101           16 :         }
     102              : 
     103            5 :         execution_context& context() const noexcept override
     104              :         {
     105            5 :             return const_cast<Ex&>(ex_).context();
     106              :         }
     107              : 
     108            0 :         void on_work_started() const noexcept override
     109              :         {
     110            0 :             ex_.on_work_started();
     111            0 :         }
     112              : 
     113            0 :         void on_work_finished() const noexcept override
     114              :         {
     115            0 :             ex_.on_work_finished();
     116            0 :         }
     117              : 
     118            1 :         std::coroutine_handle<> dispatch(std::coroutine_handle<> h) const override
     119              :         {
     120            1 :             return ex_.dispatch(h);
     121              :         }
     122              : 
     123           15 :         void post(std::coroutine_handle<> h) const override
     124              :         {
     125           15 :             ex_.post(h);
     126           15 :         }
     127              : 
     128            8 :         bool equals(impl_base const* other) const noexcept override
     129              :         {
     130            8 :             if(target_type() != other->target_type())
     131            0 :                 return false;
     132            8 :             return ex_ == static_cast<impl const*>(other)->ex_;
     133              :         }
     134              : 
     135           17 :         std::type_info const& target_type() const noexcept override
     136              :         {
     137           17 :             return typeid(Ex);
     138              :         }
     139              :     };
     140              : 
     141              : public:
     142              :     /** Default constructor.
     143              : 
     144              :         Constructs an empty `any_executor`. Calling any executor
     145              :         operations on a default-constructed instance results in
     146              :         undefined behavior.
     147              : 
     148              :         @par Postconditions
     149              :         @li `!*this`
     150              :     */
     151              :     any_executor() = default;
     152              : 
     153              :     /** Copy constructor.
     154              : 
     155              :         Creates a new `any_executor` sharing ownership of the
     156              :         underlying executor with `other`.
     157              : 
     158              :         @par Postconditions
     159              :         @li `*this == other`
     160              :     */
     161            9 :     any_executor(any_executor const&) = default;
     162              : 
     163              :     /** Copy assignment operator.
     164              : 
     165              :         Shares ownership of the underlying executor with `other`.
     166              : 
     167              :         @par Postconditions
     168              :         @li `*this == other`
     169              :     */
     170            2 :     any_executor& operator=(any_executor const&) = default;
     171              : 
     172              :     /** Constructs from any executor type.
     173              : 
     174              :         Allocates storage for a copy of the given executor and
     175              :         stores it internally. The executor must satisfy the
     176              :         `Executor` concept.
     177              : 
     178              :         @param ex The executor to wrap. A copy is stored internally.
     179              : 
     180              :         @par Postconditions
     181              :         @li `*this` is valid
     182              :     */
     183              :     template<class Ex>
     184              :         requires (
     185              :             !std::same_as<std::decay_t<Ex>, any_executor> &&
     186              :             !detail::is_strand_type<std::decay_t<Ex>>::value &&
     187              :             std::copy_constructible<std::decay_t<Ex>>)
     188           16 :     any_executor(Ex&& ex)
     189           16 :         : p_(std::make_shared<impl<std::decay_t<Ex>>>(std::forward<Ex>(ex)))
     190              :     {
     191           16 :     }
     192              : 
     193              :     /** Returns true if this instance holds a valid executor.
     194              : 
     195              :         @return `true` if constructed with an executor, `false` if
     196              :                 default-constructed.
     197              :     */
     198            6 :     explicit operator bool() const noexcept
     199              :     {
     200            6 :         return p_ != nullptr;
     201              :     }
     202              : 
     203              :     /** Returns a reference to the associated execution context.
     204              : 
     205              :         @return A reference to the execution context.
     206              : 
     207              :         @pre This instance holds a valid executor.
     208              :     */
     209            5 :     execution_context& context() const noexcept
     210              :     {
     211            5 :         return p_->context();
     212              :     }
     213              : 
     214              :     /** Informs the executor that work is beginning.
     215              : 
     216              :         Must be paired with a subsequent call to `on_work_finished()`.
     217              : 
     218              :         @pre This instance holds a valid executor.
     219              :     */
     220            0 :     void on_work_started() const noexcept
     221              :     {
     222            0 :         p_->on_work_started();
     223            0 :     }
     224              : 
     225              :     /** Informs the executor that work has completed.
     226              : 
     227              :         @pre A preceding call to `on_work_started()` was made.
     228              :         @pre This instance holds a valid executor.
     229              :     */
     230            0 :     void on_work_finished() const noexcept
     231              :     {
     232            0 :         p_->on_work_finished();
     233            0 :     }
     234              : 
     235              :     /** Dispatches a coroutine handle through the wrapped executor.
     236              : 
     237              :         Invokes the executor's `dispatch()` operation with the given
     238              :         coroutine handle, returning a handle suitable for symmetric
     239              :         transfer.
     240              : 
     241              :         @param h The coroutine handle to dispatch for resumption.
     242              : 
     243              :         @return A coroutine handle that the caller may use for symmetric
     244              :                 transfer, or `std::noop_coroutine()` if the executor
     245              :                 posted the work for later execution.
     246              : 
     247              :         @pre This instance holds a valid executor.
     248              :     */
     249            1 :     coro dispatch(coro h) const
     250              :     {
     251            1 :         return p_->dispatch(h);
     252              :     }
     253              : 
     254              :     /** Posts a coroutine handle to the wrapped executor.
     255              : 
     256              :         Posts the coroutine handle to the executor for later execution
     257              :         and returns. The caller should transfer to `std::noop_coroutine()`
     258              :         after calling this.
     259              : 
     260              :         @param h The coroutine handle to post for resumption.
     261              : 
     262              :         @pre This instance holds a valid executor.
     263              :     */
     264           15 :     void post(coro h) const
     265              :     {
     266           15 :         p_->post(h);
     267           15 :     }
     268              : 
     269              :     /** Compares two executor wrappers for equality.
     270              : 
     271              :         Two `any_executor` instances are equal if they both hold
     272              :         executors of the same type that compare equal, or if both
     273              :         are empty.
     274              : 
     275              :         @param other The executor to compare against.
     276              : 
     277              :         @return `true` if both wrap equal executors of the same type,
     278              :                 or both are empty.
     279              :     */
     280           10 :     bool operator==(any_executor const& other) const noexcept
     281              :     {
     282           10 :         if(!p_ && !other.p_)
     283            1 :             return true;
     284            9 :         if(!p_ || !other.p_)
     285            1 :             return false;
     286            8 :         return p_->equals(other.p_.get());
     287              :     }
     288              : 
     289              :     /** Returns the type_info of the wrapped executor.
     290              : 
     291              :         @return The `std::type_info` of the stored executor type,
     292              :                 or `typeid(void)` if empty.
     293              :     */
     294            2 :     std::type_info const& target_type() const noexcept
     295              :     {
     296            2 :         if(!p_)
     297            1 :             return typeid(void);
     298            1 :         return p_->target_type();
     299              :     }
     300              : };
     301              : 
     302              : } // capy
     303              : } // boost
     304              : 
     305              : #endif
        

Generated by: LCOV version 2.3