1  
//
1  
//
2  
// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
2  
// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
3  
//
3  
//
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
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)
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
//
6  
//
7  
// Official repository: https://github.com/cppalliance/capy
7  
// Official repository: https://github.com/cppalliance/capy
8  
//
8  
//
9  

9  

10  
#ifndef BOOST_CAPY_ANY_EXECUTOR_HPP
10  
#ifndef BOOST_CAPY_ANY_EXECUTOR_HPP
11  
#define BOOST_CAPY_ANY_EXECUTOR_HPP
11  
#define BOOST_CAPY_ANY_EXECUTOR_HPP
12  

12  

13  
#include <boost/capy/detail/config.hpp>
13  
#include <boost/capy/detail/config.hpp>
14  
#include <boost/capy/coro.hpp>
14  
#include <boost/capy/coro.hpp>
15  

15  

16  
#include <concepts>
16  
#include <concepts>
17  
#include <coroutine>
17  
#include <coroutine>
18  
#include <memory>
18  
#include <memory>
19  
#include <type_traits>
19  
#include <type_traits>
20  
#include <typeinfo>
20  
#include <typeinfo>
21  

21  

22  
namespace boost {
22  
namespace boost {
23  
namespace capy {
23  
namespace capy {
24  

24  

25  
class execution_context;
25  
class execution_context;
26  
template<typename> class strand;
26  
template<typename> class strand;
27  

27  

28  
namespace detail {
28  
namespace detail {
29  

29  

30  
template<typename T>
30  
template<typename T>
31  
struct is_strand_type : std::false_type {};
31  
struct is_strand_type : std::false_type {};
32  

32  

33  
template<typename E>
33  
template<typename E>
34  
struct is_strand_type<strand<E>> : std::true_type {};
34  
struct is_strand_type<strand<E>> : std::true_type {};
35  

35  

36  
} // detail
36  
} // detail
37  

37  

38  
/** A type-erased wrapper for executor objects.
38  
/** A type-erased wrapper for executor objects.
39  

39  

40  
    This class provides type erasure for any executor type, enabling
40  
    This class provides type erasure for any executor type, enabling
41  
    runtime polymorphism with automatic memory management via shared
41  
    runtime polymorphism with automatic memory management via shared
42  
    ownership. It stores a shared pointer to a polymorphic wrapper,
42  
    ownership. It stores a shared pointer to a polymorphic wrapper,
43  
    allowing executors of different types to be stored uniformly
43  
    allowing executors of different types to be stored uniformly
44  
    while satisfying the full `Executor` concept.
44  
    while satisfying the full `Executor` concept.
45  

45  

46  
    @par Value Semantics
46  
    @par Value Semantics
47  

47  

48  
    This class has value semantics with shared ownership. Copy and
48  
    This class has value semantics with shared ownership. Copy and
49  
    move operations are cheap, simply copying the internal shared
49  
    move operations are cheap, simply copying the internal shared
50  
    pointer. Multiple `any_executor` instances may share the same
50  
    pointer. Multiple `any_executor` instances may share the same
51  
    underlying executor. Move operations do not invalidate the
51  
    underlying executor. Move operations do not invalidate the
52  
    source; there is no moved-from state.
52  
    source; there is no moved-from state.
53  

53  

54  
    @par Default State
54  
    @par Default State
55  

55  

56  
    A default-constructed `any_executor` holds no executor. Calling
56  
    A default-constructed `any_executor` holds no executor. Calling
57  
    executor operations on a default-constructed instance results
57  
    executor operations on a default-constructed instance results
58  
    in undefined behavior. Use `operator bool()` to check validity.
58  
    in undefined behavior. Use `operator bool()` to check validity.
59  

59  

60  
    @par Thread Safety
60  
    @par Thread Safety
61  

61  

62  
    The `any_executor` itself is thread-safe for concurrent reads.
62  
    The `any_executor` itself is thread-safe for concurrent reads.
63  
    Concurrent modification requires external synchronization.
63  
    Concurrent modification requires external synchronization.
64  
    Executor operations are safe to call concurrently if the
64  
    Executor operations are safe to call concurrently if the
65  
    underlying executor supports it.
65  
    underlying executor supports it.
66  

66  

67  
    @par Executor Concept
67  
    @par Executor Concept
68  

68  

69  
    This class satisfies the `Executor` concept, making it usable
69  
    This class satisfies the `Executor` concept, making it usable
70  
    anywhere a concrete executor is expected.
70  
    anywhere a concrete executor is expected.
71  

71  

72  
    @par Example
72  
    @par Example
73  
    @code
73  
    @code
74  
    any_executor exec = ctx.get_executor();
74  
    any_executor exec = ctx.get_executor();
75  
    if(exec)
75  
    if(exec)
76  
    {
76  
    {
77  
        auto& context = exec.context();
77  
        auto& context = exec.context();
78  
        exec.post(my_coroutine);
78  
        exec.post(my_coroutine);
79  
    }
79  
    }
80  
    @endcode
80  
    @endcode
81  

81  

82  
    @see executor_ref, Executor
82  
    @see executor_ref, Executor
83  
*/
83  
*/
84  
class any_executor
84  
class any_executor
85  
{
85  
{
86  
    struct impl_base;
86  
    struct impl_base;
87  

87  

88  
    std::shared_ptr<impl_base> p_;
88  
    std::shared_ptr<impl_base> p_;
89  

89  

90  
    struct impl_base
90  
    struct impl_base
91  
    {
91  
    {
92  
        virtual ~impl_base() = default;
92  
        virtual ~impl_base() = default;
93  
        virtual execution_context& context() const noexcept = 0;
93  
        virtual execution_context& context() const noexcept = 0;
94  
        virtual void on_work_started() const noexcept = 0;
94  
        virtual void on_work_started() const noexcept = 0;
95  
        virtual void on_work_finished() const noexcept = 0;
95  
        virtual void on_work_finished() const noexcept = 0;
96  
        virtual void dispatch(std::coroutine_handle<>) const = 0;
96  
        virtual void dispatch(std::coroutine_handle<>) const = 0;
97  
        virtual void post(std::coroutine_handle<>) const = 0;
97  
        virtual void post(std::coroutine_handle<>) const = 0;
98  
        virtual bool equals(impl_base const*) const noexcept = 0;
98  
        virtual bool equals(impl_base const*) const noexcept = 0;
99  
        virtual std::type_info const& target_type() const noexcept = 0;
99  
        virtual std::type_info const& target_type() const noexcept = 0;
100  
    };
100  
    };
101  

101  

102  
    template<class Ex>
102  
    template<class Ex>
103  
    struct impl final : impl_base
103  
    struct impl final : impl_base
104  
    {
104  
    {
105  
        Ex ex_;
105  
        Ex ex_;
106  

106  

107  
        template<class Ex1>
107  
        template<class Ex1>
108  
        explicit impl(Ex1&& ex)
108  
        explicit impl(Ex1&& ex)
109  
            : ex_(std::forward<Ex1>(ex))
109  
            : ex_(std::forward<Ex1>(ex))
110  
        {
110  
        {
111  
        }
111  
        }
112  

112  

113  
        execution_context& context() const noexcept override
113  
        execution_context& context() const noexcept override
114  
        {
114  
        {
115  
            return const_cast<Ex&>(ex_).context();
115  
            return const_cast<Ex&>(ex_).context();
116  
        }
116  
        }
117  

117  

118  
        void on_work_started() const noexcept override
118  
        void on_work_started() const noexcept override
119  
        {
119  
        {
120  
            ex_.on_work_started();
120  
            ex_.on_work_started();
121  
        }
121  
        }
122  

122  

123  
        void on_work_finished() const noexcept override
123  
        void on_work_finished() const noexcept override
124  
        {
124  
        {
125  
            ex_.on_work_finished();
125  
            ex_.on_work_finished();
126  
        }
126  
        }
127  

127  

128  
        void dispatch(std::coroutine_handle<> h) const override
128  
        void dispatch(std::coroutine_handle<> h) const override
129  
        {
129  
        {
130  
            ex_.dispatch(h);
130  
            ex_.dispatch(h);
131  
        }
131  
        }
132  

132  

133  
        void post(std::coroutine_handle<> h) const override
133  
        void post(std::coroutine_handle<> h) const override
134  
        {
134  
        {
135  
            ex_.post(h);
135  
            ex_.post(h);
136  
        }
136  
        }
137  

137  

138  
        bool equals(impl_base const* other) const noexcept override
138  
        bool equals(impl_base const* other) const noexcept override
139  
        {
139  
        {
140  
            if(target_type() != other->target_type())
140  
            if(target_type() != other->target_type())
141  
                return false;
141  
                return false;
142  
            return ex_ == static_cast<impl const*>(other)->ex_;
142  
            return ex_ == static_cast<impl const*>(other)->ex_;
143  
        }
143  
        }
144  

144  

145  
        std::type_info const& target_type() const noexcept override
145  
        std::type_info const& target_type() const noexcept override
146  
        {
146  
        {
147  
            return typeid(Ex);
147  
            return typeid(Ex);
148  
        }
148  
        }
149  
    };
149  
    };
150  

150  

151  
public:
151  
public:
152  
    /** Default constructor.
152  
    /** Default constructor.
153  

153  

154  
        Constructs an empty `any_executor`. Calling any executor
154  
        Constructs an empty `any_executor`. Calling any executor
155  
        operations on a default-constructed instance results in
155  
        operations on a default-constructed instance results in
156  
        undefined behavior.
156  
        undefined behavior.
157  

157  

158  
        @par Postconditions
158  
        @par Postconditions
159  
        @li `!*this`
159  
        @li `!*this`
160  
    */
160  
    */
161  
    any_executor() = default;
161  
    any_executor() = default;
162  

162  

163  
    /** Copy constructor.
163  
    /** Copy constructor.
164  

164  

165  
        Creates a new `any_executor` sharing ownership of the
165  
        Creates a new `any_executor` sharing ownership of the
166  
        underlying executor with `other`.
166  
        underlying executor with `other`.
167  

167  

168  
        @par Postconditions
168  
        @par Postconditions
169  
        @li `*this == other`
169  
        @li `*this == other`
170  
    */
170  
    */
171  
    any_executor(any_executor const&) = default;
171  
    any_executor(any_executor const&) = default;
172  

172  

173  
    /** Copy assignment operator.
173  
    /** Copy assignment operator.
174  

174  

175  
        Shares ownership of the underlying executor with `other`.
175  
        Shares ownership of the underlying executor with `other`.
176  

176  

177  
        @par Postconditions
177  
        @par Postconditions
178  
        @li `*this == other`
178  
        @li `*this == other`
179  
    */
179  
    */
180  
    any_executor& operator=(any_executor const&) = default;
180  
    any_executor& operator=(any_executor const&) = default;
181  

181  

182  
    /** Constructs from any executor type.
182  
    /** Constructs from any executor type.
183  

183  

184  
        Allocates storage for a copy of the given executor and
184  
        Allocates storage for a copy of the given executor and
185  
        stores it internally. The executor must satisfy the
185  
        stores it internally. The executor must satisfy the
186  
        `Executor` concept.
186  
        `Executor` concept.
187  

187  

188  
        @param ex The executor to wrap. A copy is stored internally.
188  
        @param ex The executor to wrap. A copy is stored internally.
189  

189  

190  
        @par Postconditions
190  
        @par Postconditions
191  
        @li `*this` is valid
191  
        @li `*this` is valid
192  
    */
192  
    */
193  
    template<class Ex>
193  
    template<class Ex>
194  
        requires (
194  
        requires (
195  
            !std::same_as<std::decay_t<Ex>, any_executor> &&
195  
            !std::same_as<std::decay_t<Ex>, any_executor> &&
196  
            !detail::is_strand_type<std::decay_t<Ex>>::value &&
196  
            !detail::is_strand_type<std::decay_t<Ex>>::value &&
197  
            std::copy_constructible<std::decay_t<Ex>>)
197  
            std::copy_constructible<std::decay_t<Ex>>)
198  
    any_executor(Ex&& ex)
198  
    any_executor(Ex&& ex)
199  
        : p_(std::make_shared<impl<std::decay_t<Ex>>>(std::forward<Ex>(ex)))
199  
        : p_(std::make_shared<impl<std::decay_t<Ex>>>(std::forward<Ex>(ex)))
200  
    {
200  
    {
201  
    }
201  
    }
202  

202  

203  
    /** Returns true if this instance holds a valid executor.
203  
    /** Returns true if this instance holds a valid executor.
204  

204  

205  
        @return `true` if constructed with an executor, `false` if
205  
        @return `true` if constructed with an executor, `false` if
206  
                default-constructed.
206  
                default-constructed.
207  
    */
207  
    */
208  
    explicit operator bool() const noexcept
208  
    explicit operator bool() const noexcept
209  
    {
209  
    {
210  
        return p_ != nullptr;
210  
        return p_ != nullptr;
211  
    }
211  
    }
212  

212  

213  
    /** Returns a reference to the associated execution context.
213  
    /** Returns a reference to the associated execution context.
214  

214  

215  
        @return A reference to the execution context.
215  
        @return A reference to the execution context.
216  

216  

217  
        @pre This instance holds a valid executor.
217  
        @pre This instance holds a valid executor.
218  
    */
218  
    */
219  
    execution_context& context() const noexcept
219  
    execution_context& context() const noexcept
220  
    {
220  
    {
221  
        return p_->context();
221  
        return p_->context();
222  
    }
222  
    }
223  

223  

224  
    /** Informs the executor that work is beginning.
224  
    /** Informs the executor that work is beginning.
225  

225  

226  
        Must be paired with a subsequent call to `on_work_finished()`.
226  
        Must be paired with a subsequent call to `on_work_finished()`.
227  

227  

228  
        @pre This instance holds a valid executor.
228  
        @pre This instance holds a valid executor.
229  
    */
229  
    */
230  
    void on_work_started() const noexcept
230  
    void on_work_started() const noexcept
231  
    {
231  
    {
232  
        p_->on_work_started();
232  
        p_->on_work_started();
233  
    }
233  
    }
234  

234  

235  
    /** Informs the executor that work has completed.
235  
    /** Informs the executor that work has completed.
236  

236  

237  
        @pre A preceding call to `on_work_started()` was made.
237  
        @pre A preceding call to `on_work_started()` was made.
238  
        @pre This instance holds a valid executor.
238  
        @pre This instance holds a valid executor.
239  
    */
239  
    */
240  
    void on_work_finished() const noexcept
240  
    void on_work_finished() const noexcept
241  
    {
241  
    {
242  
        p_->on_work_finished();
242  
        p_->on_work_finished();
243  
    }
243  
    }
244  

244  

245  
    /** Dispatches a coroutine handle through the wrapped executor.
245  
    /** Dispatches a coroutine handle through the wrapped executor.
246  

246  

247  
        Invokes the executor's `dispatch()` operation with the given
247  
        Invokes the executor's `dispatch()` operation with the given
248  
        coroutine handle. If running in the executor's thread, resumes
248  
        coroutine handle. If running in the executor's thread, resumes
249  
        the coroutine inline via a normal function call. Otherwise,
249  
        the coroutine inline via a normal function call. Otherwise,
250  
        posts the coroutine for later execution.
250  
        posts the coroutine for later execution.
251  

251  

252  
        @param h The coroutine handle to dispatch for resumption.
252  
        @param h The coroutine handle to dispatch for resumption.
253  

253  

254  
        @pre This instance holds a valid executor.
254  
        @pre This instance holds a valid executor.
255  
    */
255  
    */
256  
    void dispatch(coro h) const
256  
    void dispatch(coro h) const
257  
    {
257  
    {
258  
        p_->dispatch(h);
258  
        p_->dispatch(h);
259  
    }
259  
    }
260  

260  

261  
    /** Posts a coroutine handle to the wrapped executor.
261  
    /** Posts a coroutine handle to the wrapped executor.
262  

262  

263  
        Posts the coroutine handle to the executor for later execution
263  
        Posts the coroutine handle to the executor for later execution
264  
        and returns. The caller should transfer to `std::noop_coroutine()`
264  
        and returns. The caller should transfer to `std::noop_coroutine()`
265  
        after calling this.
265  
        after calling this.
266  

266  

267  
        @param h The coroutine handle to post for resumption.
267  
        @param h The coroutine handle to post for resumption.
268  

268  

269  
        @pre This instance holds a valid executor.
269  
        @pre This instance holds a valid executor.
270  
    */
270  
    */
271  
    void post(coro h) const
271  
    void post(coro h) const
272  
    {
272  
    {
273  
        p_->post(h);
273  
        p_->post(h);
274  
    }
274  
    }
275  

275  

276  
    /** Compares two executor wrappers for equality.
276  
    /** Compares two executor wrappers for equality.
277  

277  

278  
        Two `any_executor` instances are equal if they both hold
278  
        Two `any_executor` instances are equal if they both hold
279  
        executors of the same type that compare equal, or if both
279  
        executors of the same type that compare equal, or if both
280  
        are empty.
280  
        are empty.
281  

281  

282  
        @param other The executor to compare against.
282  
        @param other The executor to compare against.
283  

283  

284  
        @return `true` if both wrap equal executors of the same type,
284  
        @return `true` if both wrap equal executors of the same type,
285  
                or both are empty.
285  
                or both are empty.
286  
    */
286  
    */
287  
    bool operator==(any_executor const& other) const noexcept
287  
    bool operator==(any_executor const& other) const noexcept
288  
    {
288  
    {
289  
        if(!p_ && !other.p_)
289  
        if(!p_ && !other.p_)
290  
            return true;
290  
            return true;
291  
        if(!p_ || !other.p_)
291  
        if(!p_ || !other.p_)
292  
            return false;
292  
            return false;
293  
        return p_->equals(other.p_.get());
293  
        return p_->equals(other.p_.get());
294  
    }
294  
    }
295  

295  

296  
    /** Returns the type_info of the wrapped executor.
296  
    /** Returns the type_info of the wrapped executor.
297  

297  

298  
        @return The `std::type_info` of the stored executor type,
298  
        @return The `std::type_info` of the stored executor type,
299  
                or `typeid(void)` if empty.
299  
                or `typeid(void)` if empty.
300  
    */
300  
    */
301  
    std::type_info const& target_type() const noexcept
301  
    std::type_info const& target_type() const noexcept
302  
    {
302  
    {
303  
        if(!p_)
303  
        if(!p_)
304  
            return typeid(void);
304  
            return typeid(void);
305  
        return p_->target_type();
305  
        return p_->target_type();
306  
    }
306  
    }
307  
};
307  
};
308  

308  

309  
} // capy
309  
} // capy
310  
} // boost
310  
} // boost
311  

311  

312  
#endif
312  
#endif