LCOV - code coverage report
Current view: top level - capy/ex - any_executor.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 75.5 % 53 40
Test Date: 2026-02-02 05:00:52 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              :     @par Example
      73              :     @code
      74              :     any_executor exec = ctx.get_executor();
      75              :     if(exec)
      76              :     {
      77              :         auto& context = exec.context();
      78              :         exec.post(my_coroutine);
      79              :     }
      80              :     @endcode
      81              : 
      82              :     @see executor_ref, Executor
      83              : */
      84              : class any_executor
      85              : {
      86              :     struct impl_base;
      87              : 
      88              :     std::shared_ptr<impl_base> p_;
      89              : 
      90              :     struct impl_base
      91              :     {
      92           16 :         virtual ~impl_base() = default;
      93              :         virtual execution_context& context() const noexcept = 0;
      94              :         virtual void on_work_started() const noexcept = 0;
      95              :         virtual void on_work_finished() const noexcept = 0;
      96              :         virtual std::coroutine_handle<> dispatch(std::coroutine_handle<>) const = 0;
      97              :         virtual void post(std::coroutine_handle<>) const = 0;
      98              :         virtual bool equals(impl_base const*) const noexcept = 0;
      99              :         virtual std::type_info const& target_type() const noexcept = 0;
     100              :     };
     101              : 
     102              :     template<class Ex>
     103              :     struct impl final : impl_base
     104              :     {
     105              :         Ex ex_;
     106              : 
     107              :         template<class Ex1>
     108           16 :         explicit impl(Ex1&& ex)
     109           16 :             : ex_(std::forward<Ex1>(ex))
     110              :         {
     111           16 :         }
     112              : 
     113            5 :         execution_context& context() const noexcept override
     114              :         {
     115            5 :             return const_cast<Ex&>(ex_).context();
     116              :         }
     117              : 
     118            0 :         void on_work_started() const noexcept override
     119              :         {
     120            0 :             ex_.on_work_started();
     121            0 :         }
     122              : 
     123            0 :         void on_work_finished() const noexcept override
     124              :         {
     125            0 :             ex_.on_work_finished();
     126            0 :         }
     127              : 
     128            1 :         std::coroutine_handle<> dispatch(std::coroutine_handle<> h) const override
     129              :         {
     130            1 :             return ex_.dispatch(h);
     131              :         }
     132              : 
     133           15 :         void post(std::coroutine_handle<> h) const override
     134              :         {
     135           15 :             ex_.post(h);
     136           15 :         }
     137              : 
     138            8 :         bool equals(impl_base const* other) const noexcept override
     139              :         {
     140            8 :             if(target_type() != other->target_type())
     141            0 :                 return false;
     142            8 :             return ex_ == static_cast<impl const*>(other)->ex_;
     143              :         }
     144              : 
     145           17 :         std::type_info const& target_type() const noexcept override
     146              :         {
     147           17 :             return typeid(Ex);
     148              :         }
     149              :     };
     150              : 
     151              : public:
     152              :     /** Default constructor.
     153              : 
     154              :         Constructs an empty `any_executor`. Calling any executor
     155              :         operations on a default-constructed instance results in
     156              :         undefined behavior.
     157              : 
     158              :         @par Postconditions
     159              :         @li `!*this`
     160              :     */
     161              :     any_executor() = default;
     162              : 
     163              :     /** Copy constructor.
     164              : 
     165              :         Creates a new `any_executor` sharing ownership of the
     166              :         underlying executor with `other`.
     167              : 
     168              :         @par Postconditions
     169              :         @li `*this == other`
     170              :     */
     171            9 :     any_executor(any_executor const&) = default;
     172              : 
     173              :     /** Copy assignment operator.
     174              : 
     175              :         Shares ownership of the underlying executor with `other`.
     176              : 
     177              :         @par Postconditions
     178              :         @li `*this == other`
     179              :     */
     180            2 :     any_executor& operator=(any_executor const&) = default;
     181              : 
     182              :     /** Constructs from any executor type.
     183              : 
     184              :         Allocates storage for a copy of the given executor and
     185              :         stores it internally. The executor must satisfy the
     186              :         `Executor` concept.
     187              : 
     188              :         @param ex The executor to wrap. A copy is stored internally.
     189              : 
     190              :         @par Postconditions
     191              :         @li `*this` is valid
     192              :     */
     193              :     template<class Ex>
     194              :         requires (
     195              :             !std::same_as<std::decay_t<Ex>, any_executor> &&
     196              :             !detail::is_strand_type<std::decay_t<Ex>>::value &&
     197              :             std::copy_constructible<std::decay_t<Ex>>)
     198           16 :     any_executor(Ex&& ex)
     199           16 :         : p_(std::make_shared<impl<std::decay_t<Ex>>>(std::forward<Ex>(ex)))
     200              :     {
     201           16 :     }
     202              : 
     203              :     /** Returns true if this instance holds a valid executor.
     204              : 
     205              :         @return `true` if constructed with an executor, `false` if
     206              :                 default-constructed.
     207              :     */
     208            6 :     explicit operator bool() const noexcept
     209              :     {
     210            6 :         return p_ != nullptr;
     211              :     }
     212              : 
     213              :     /** Returns a reference to the associated execution context.
     214              : 
     215              :         @return A reference to the execution context.
     216              : 
     217              :         @pre This instance holds a valid executor.
     218              :     */
     219            5 :     execution_context& context() const noexcept
     220              :     {
     221            5 :         return p_->context();
     222              :     }
     223              : 
     224              :     /** Informs the executor that work is beginning.
     225              : 
     226              :         Must be paired with a subsequent call to `on_work_finished()`.
     227              : 
     228              :         @pre This instance holds a valid executor.
     229              :     */
     230            0 :     void on_work_started() const noexcept
     231              :     {
     232            0 :         p_->on_work_started();
     233            0 :     }
     234              : 
     235              :     /** Informs the executor that work has completed.
     236              : 
     237              :         @pre A preceding call to `on_work_started()` was made.
     238              :         @pre This instance holds a valid executor.
     239              :     */
     240            0 :     void on_work_finished() const noexcept
     241              :     {
     242            0 :         p_->on_work_finished();
     243            0 :     }
     244              : 
     245              :     /** Dispatches a coroutine handle through the wrapped executor.
     246              : 
     247              :         Invokes the executor's `dispatch()` operation with the given
     248              :         coroutine handle, returning a handle suitable for symmetric
     249              :         transfer.
     250              : 
     251              :         @param h The coroutine handle to dispatch for resumption.
     252              : 
     253              :         @return A coroutine handle that the caller may use for symmetric
     254              :                 transfer, or `std::noop_coroutine()` if the executor
     255              :                 posted the work for later execution.
     256              : 
     257              :         @pre This instance holds a valid executor.
     258              :     */
     259            1 :     coro dispatch(coro h) const
     260              :     {
     261            1 :         return p_->dispatch(h);
     262              :     }
     263              : 
     264              :     /** Posts a coroutine handle to the wrapped executor.
     265              : 
     266              :         Posts the coroutine handle to the executor for later execution
     267              :         and returns. The caller should transfer to `std::noop_coroutine()`
     268              :         after calling this.
     269              : 
     270              :         @param h The coroutine handle to post for resumption.
     271              : 
     272              :         @pre This instance holds a valid executor.
     273              :     */
     274           15 :     void post(coro h) const
     275              :     {
     276           15 :         p_->post(h);
     277           15 :     }
     278              : 
     279              :     /** Compares two executor wrappers for equality.
     280              : 
     281              :         Two `any_executor` instances are equal if they both hold
     282              :         executors of the same type that compare equal, or if both
     283              :         are empty.
     284              : 
     285              :         @param other The executor to compare against.
     286              : 
     287              :         @return `true` if both wrap equal executors of the same type,
     288              :                 or both are empty.
     289              :     */
     290           10 :     bool operator==(any_executor const& other) const noexcept
     291              :     {
     292           10 :         if(!p_ && !other.p_)
     293            1 :             return true;
     294            9 :         if(!p_ || !other.p_)
     295            1 :             return false;
     296            8 :         return p_->equals(other.p_.get());
     297              :     }
     298              : 
     299              :     /** Returns the type_info of the wrapped executor.
     300              : 
     301              :         @return The `std::type_info` of the stored executor type,
     302              :                 or `typeid(void)` if empty.
     303              :     */
     304            2 :     std::type_info const& target_type() const noexcept
     305              :     {
     306            2 :         if(!p_)
     307            1 :             return typeid(void);
     308            1 :         return p_->target_type();
     309              :     }
     310              : };
     311              : 
     312              : } // capy
     313              : } // boost
     314              : 
     315              : #endif
        

Generated by: LCOV version 2.3