LCOV - code coverage report
Current view: top level - capy/ex - any_executor.hpp (source / functions) Coverage Total Hit
Test: coverage_remapped.info Lines: 75.5 % 53 40
Test Date: 2026-02-12 22:58:59 Functions: 78.3 % 23 18

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

Generated by: LCOV version 2.3