1  
//
1  
//
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.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/continuation.hpp>
14  
#include <boost/capy/continuation.hpp>
15  
#include <concepts>
15  
#include <concepts>
16  
#include <coroutine>
16  
#include <coroutine>
17  
#include <memory>
17  
#include <memory>
18  
#include <type_traits>
18  
#include <type_traits>
19  
#include <typeinfo>
19  
#include <typeinfo>
20  

20  

21  
namespace boost {
21  
namespace boost {
22  
namespace capy {
22  
namespace capy {
23  

23  

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

26  

27  
namespace detail {
27  
namespace detail {
28  

28  

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

31  

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

34  

35  
} // detail
35  
} // detail
36  

36  

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

38  

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

44  

45  
    @par Value Semantics
45  
    @par Value Semantics
46  

46  

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

52  

53  
    @par Default State
53  
    @par Default State
54  

54  

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

58  

59  
    @par Thread Safety
59  
    @par Thread Safety
60  

60  

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

65  

66  
    @par Executor Concept
66  
    @par Executor Concept
67  

67  

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

70  

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

80  

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

86  

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

88  

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

100  

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

105  

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

111  

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

116  

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

121  

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

126  

127  
        std::coroutine_handle<> dispatch(continuation& c) const override
127  
        std::coroutine_handle<> dispatch(continuation& c) const override
128  
        {
128  
        {
129  
            return ex_.dispatch(c);
129  
            return ex_.dispatch(c);
130  
        }
130  
        }
131  

131  

132  
        void post(continuation& c) const override
132  
        void post(continuation& c) const override
133  
        {
133  
        {
134  
            ex_.post(c);
134  
            ex_.post(c);
135  
        }
135  
        }
136  

136  

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

143  

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

149  

150  
public:
150  
public:
151  
    /** Construct a default instance.
151  
    /** Construct a default instance.
152  

152  

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

156  

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

161  

162  
    /** Construct a copy.
162  
    /** Construct a copy.
163  

163  

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

166  

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

171  

172  
    /** Copy assignment operator.
172  
    /** Copy assignment operator.
173  

173  

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

175  

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

180  

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

182  

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

186  

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

188  

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

201  

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

203  

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

211  

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

213  

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

215  

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

222  

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

224  

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

226  

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

233  

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

235  

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

243  

244  
    /** Dispatches a continuation through the wrapped executor.
244  
    /** Dispatches a continuation through the wrapped executor.
245  

245  

246  
        Returns a handle for symmetric transfer. If running in the
246  
        Returns a handle for symmetric transfer. If running in the
247  
        executor's thread, returns `c.h`. Otherwise, posts the
247  
        executor's thread, returns `c.h`. Otherwise, posts the
248  
        continuation for later execution and returns
248  
        continuation for later execution and returns
249  
        `std::noop_coroutine()`.
249  
        `std::noop_coroutine()`.
250  

250  

251  
        @param c The continuation to dispatch for resumption.
251  
        @param c The continuation to dispatch for resumption.
252  
                 Must remain at a stable address until dequeued.
252  
                 Must remain at a stable address until dequeued.
253  

253  

254  
        @return A handle for symmetric transfer or `std::noop_coroutine()`.
254  
        @return A handle for symmetric transfer or `std::noop_coroutine()`.
255  

255  

256  
        @pre This instance holds a valid executor.
256  
        @pre This instance holds a valid executor.
257  
    */
257  
    */
258  
    std::coroutine_handle<> dispatch(continuation& c) const
258  
    std::coroutine_handle<> dispatch(continuation& c) const
259  
    {
259  
    {
260  
        return p_->dispatch(c);
260  
        return p_->dispatch(c);
261  
    }
261  
    }
262  

262  

263  
    /** Posts a continuation to the wrapped executor.
263  
    /** Posts a continuation to the wrapped executor.
264  

264  

265  
        Posts the continuation to the executor for later execution
265  
        Posts the continuation to the executor for later execution
266  
        and returns. The caller should transfer to `std::noop_coroutine()`
266  
        and returns. The caller should transfer to `std::noop_coroutine()`
267  
        after calling this.
267  
        after calling this.
268  

268  

269  
        @param c The continuation to post for resumption.
269  
        @param c The continuation to post for resumption.
270  
                 Must remain at a stable address until dequeued.
270  
                 Must remain at a stable address until dequeued.
271  

271  

272  
        @pre This instance holds a valid executor.
272  
        @pre This instance holds a valid executor.
273  
    */
273  
    */
274  
    void post(continuation& c) const
274  
    void post(continuation& c) const
275  
    {
275  
    {
276  
        p_->post(c);
276  
        p_->post(c);
277  
    }
277  
    }
278  

278  

279  
    /** Compares two executor wrappers for equality.
279  
    /** Compares two executor wrappers for equality.
280  

280  

281  
        Two `any_executor` instances are equal if they both hold
281  
        Two `any_executor` instances are equal if they both hold
282  
        executors of the same type that compare equal, or if both
282  
        executors of the same type that compare equal, or if both
283  
        are empty.
283  
        are empty.
284  

284  

285  
        @param other The executor to compare against.
285  
        @param other The executor to compare against.
286  

286  

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

298  

299  
    /** Returns the type_info of the wrapped executor.
299  
    /** Returns the type_info of the wrapped executor.
300  

300  

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

311  

312  
} // capy
312  
} // capy
313  
} // boost
313  
} // boost
314  

314  

315  
#endif
315  
#endif