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

Generated by: LCOV version 2.3