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_EXECUTOR_REF_HPP
10  
#ifndef BOOST_CAPY_EXECUTOR_REF_HPP
11  
#define BOOST_CAPY_EXECUTOR_REF_HPP
11  
#define BOOST_CAPY_EXECUTOR_REF_HPP
12  

12  

13  
#include <boost/capy/detail/config.hpp>
13  
#include <boost/capy/detail/config.hpp>
14  
#include <boost/capy/detail/type_id.hpp>
14  
#include <boost/capy/detail/type_id.hpp>
15  
#include <boost/capy/continuation.hpp>
15  
#include <boost/capy/continuation.hpp>
16  
#include <concepts>
16  
#include <concepts>
17  
#include <coroutine>
17  
#include <coroutine>
18  
#include <type_traits>
18  
#include <type_traits>
19  
#include <utility>
19  
#include <utility>
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  

25  

26  
namespace detail {
26  
namespace detail {
27  

27  

28  
/** Virtual function table for type-erased executor operations. */
28  
/** Virtual function table for type-erased executor operations. */
29  
struct executor_vtable
29  
struct executor_vtable
30  
{
30  
{
31  
    execution_context& (*context)(void const*) noexcept;
31  
    execution_context& (*context)(void const*) noexcept;
32  
    void (*on_work_started)(void const*) noexcept;
32  
    void (*on_work_started)(void const*) noexcept;
33  
    void (*on_work_finished)(void const*) noexcept;
33  
    void (*on_work_finished)(void const*) noexcept;
34  
    void (*post)(void const*, continuation&);
34  
    void (*post)(void const*, continuation&);
35  
    std::coroutine_handle<> (*dispatch)(void const*, continuation&);
35  
    std::coroutine_handle<> (*dispatch)(void const*, continuation&);
36  
    bool (*equals)(void const*, void const*) noexcept;
36  
    bool (*equals)(void const*, void const*) noexcept;
37  
    detail::type_info const* type_id;
37  
    detail::type_info const* type_id;
38  
};
38  
};
39  

39  

40  
/** Vtable instance for a specific executor type. */
40  
/** Vtable instance for a specific executor type. */
41  
template<class Ex>
41  
template<class Ex>
42  
inline constexpr executor_vtable vtable_for = {
42  
inline constexpr executor_vtable vtable_for = {
43  
    // context
43  
    // context
44  
    [](void const* p) noexcept -> execution_context& {
44  
    [](void const* p) noexcept -> execution_context& {
45  
        return const_cast<Ex*>(static_cast<Ex const*>(p))->context();
45  
        return const_cast<Ex*>(static_cast<Ex const*>(p))->context();
46  
    },
46  
    },
47  
    // on_work_started
47  
    // on_work_started
48  
    [](void const* p) noexcept {
48  
    [](void const* p) noexcept {
49  
        const_cast<Ex*>(static_cast<Ex const*>(p))->on_work_started();
49  
        const_cast<Ex*>(static_cast<Ex const*>(p))->on_work_started();
50  
    },
50  
    },
51  
    // on_work_finished
51  
    // on_work_finished
52  
    [](void const* p) noexcept {
52  
    [](void const* p) noexcept {
53  
        const_cast<Ex*>(static_cast<Ex const*>(p))->on_work_finished();
53  
        const_cast<Ex*>(static_cast<Ex const*>(p))->on_work_finished();
54  
    },
54  
    },
55  
    // post
55  
    // post
56  
    [](void const* p, continuation& c) {
56  
    [](void const* p, continuation& c) {
57  
        static_cast<Ex const*>(p)->post(c);
57  
        static_cast<Ex const*>(p)->post(c);
58  
    },
58  
    },
59  
    // dispatch
59  
    // dispatch
60  
    [](void const* p, continuation& c) -> std::coroutine_handle<> {
60  
    [](void const* p, continuation& c) -> std::coroutine_handle<> {
61  
        return static_cast<Ex const*>(p)->dispatch(c);
61  
        return static_cast<Ex const*>(p)->dispatch(c);
62  
    },
62  
    },
63  
    // equals
63  
    // equals
64  
    [](void const* a, void const* b) noexcept -> bool {
64  
    [](void const* a, void const* b) noexcept -> bool {
65  
        return *static_cast<Ex const*>(a) == *static_cast<Ex const*>(b);
65  
        return *static_cast<Ex const*>(a) == *static_cast<Ex const*>(b);
66  
    },
66  
    },
67  
    // type_id
67  
    // type_id
68  
    &detail::type_id<Ex>()
68  
    &detail::type_id<Ex>()
69  
};
69  
};
70  

70  

71  
} // detail
71  
} // detail
72  

72  

73  
/** A type-erased reference wrapper for executor objects.
73  
/** A type-erased reference wrapper for executor objects.
74  

74  

75  
    This class provides type erasure for any executor type, enabling
75  
    This class provides type erasure for any executor type, enabling
76  
    runtime polymorphism without virtual functions or allocation.
76  
    runtime polymorphism without virtual functions or allocation.
77  
    It stores a pointer to the original executor and a pointer to a
77  
    It stores a pointer to the original executor and a pointer to a
78  
    static vtable, allowing executors of different types to be stored
78  
    static vtable, allowing executors of different types to be stored
79  
    uniformly while satisfying the full `Executor` concept.
79  
    uniformly while satisfying the full `Executor` concept.
80  

80  

81  
    @par Reference Semantics
81  
    @par Reference Semantics
82  
    This class has reference semantics: it does not allocate or own
82  
    This class has reference semantics: it does not allocate or own
83  
    the wrapped executor. Copy operations simply copy the internal
83  
    the wrapped executor. Copy operations simply copy the internal
84  
    pointers. The caller must ensure the referenced executor outlives
84  
    pointers. The caller must ensure the referenced executor outlives
85  
    all `executor_ref` instances that wrap it.
85  
    all `executor_ref` instances that wrap it.
86  

86  

87  
    @par Thread Safety
87  
    @par Thread Safety
88  
    The `executor_ref` itself is not thread-safe for concurrent
88  
    The `executor_ref` itself is not thread-safe for concurrent
89  
    modification, but its executor operations are safe to call
89  
    modification, but its executor operations are safe to call
90  
    concurrently if the underlying executor supports it.
90  
    concurrently if the underlying executor supports it.
91  

91  

92  
    @par Executor Concept
92  
    @par Executor Concept
93  
    This class satisfies the `Executor` concept, making it usable
93  
    This class satisfies the `Executor` concept, making it usable
94  
    anywhere a concrete executor is expected.
94  
    anywhere a concrete executor is expected.
95  

95  

96  
    @par Example
96  
    @par Example
97  
    @code
97  
    @code
98  
    void store_executor(executor_ref ex)
98  
    void store_executor(executor_ref ex)
99  
    {
99  
    {
100  
        if(ex)
100  
        if(ex)
101  
            ex.post(my_continuation);
101  
            ex.post(my_continuation);
102  
    }
102  
    }
103  

103  

104  
    io_context ctx;
104  
    io_context ctx;
105  
    store_executor(ctx.get_executor());
105  
    store_executor(ctx.get_executor());
106  
    @endcode
106  
    @endcode
107  

107  

108  
    @see any_executor, Executor
108  
    @see any_executor, Executor
109  
*/
109  
*/
110  
class executor_ref
110  
class executor_ref
111  
{
111  
{
112  
    void const* ex_ = nullptr;
112  
    void const* ex_ = nullptr;
113  
    detail::executor_vtable const* vt_ = nullptr;
113  
    detail::executor_vtable const* vt_ = nullptr;
114  

114  

115  
public:
115  
public:
116  
    /** Construct a default instance.
116  
    /** Construct a default instance.
117  

117  

118  
        Constructs an empty `executor_ref`. Calling any executor
118  
        Constructs an empty `executor_ref`. Calling any executor
119  
        operations on a default-constructed instance results in
119  
        operations on a default-constructed instance results in
120  
        undefined behavior.
120  
        undefined behavior.
121  
    */
121  
    */
122  
    executor_ref() = default;
122  
    executor_ref() = default;
123  

123  

124  
    /** Construct a copy.
124  
    /** Construct a copy.
125  

125  

126  
        Copies the internal pointers, preserving identity.
126  
        Copies the internal pointers, preserving identity.
127  
        This enables the same-executor optimization when passing
127  
        This enables the same-executor optimization when passing
128  
        executor_ref through coroutine chains.
128  
        executor_ref through coroutine chains.
129  
    */
129  
    */
130  
    executor_ref(executor_ref const&) = default;
130  
    executor_ref(executor_ref const&) = default;
131  

131  

132  
    /** Copy assignment operator. */
132  
    /** Copy assignment operator. */
133  
    executor_ref& operator=(executor_ref const&) = default;
133  
    executor_ref& operator=(executor_ref const&) = default;
134  

134  

135  
    /** Constructs from any executor type.
135  
    /** Constructs from any executor type.
136  

136  

137  
        Captures a reference to the given executor and stores a pointer
137  
        Captures a reference to the given executor and stores a pointer
138  
        to the type-specific vtable. The executor must remain valid for
138  
        to the type-specific vtable. The executor must remain valid for
139  
        the lifetime of this `executor_ref` instance.
139  
        the lifetime of this `executor_ref` instance.
140  

140  

141  
        @param ex The executor to wrap. Must satisfy the `Executor`
141  
        @param ex The executor to wrap. Must satisfy the `Executor`
142  
                  concept. A pointer to this object is stored
142  
                  concept. A pointer to this object is stored
143  
                  internally; the executor must outlive this wrapper.
143  
                  internally; the executor must outlive this wrapper.
144  
    */
144  
    */
145  
#if defined(__GNUC__) && !defined(__clang__)
145  
#if defined(__GNUC__) && !defined(__clang__)
146  
    // GCC constraint satisfaction caching bug workaround
146  
    // GCC constraint satisfaction caching bug workaround
147  
    template<class Ex,
147  
    template<class Ex,
148  
        std::enable_if_t<!std::is_same_v<
148  
        std::enable_if_t<!std::is_same_v<
149  
            std::decay_t<Ex>, executor_ref>, int> = 0>
149  
            std::decay_t<Ex>, executor_ref>, int> = 0>
150  
#else
150  
#else
151  
    template<class Ex>
151  
    template<class Ex>
152  
        requires (!std::same_as<std::decay_t<Ex>, executor_ref>)
152  
        requires (!std::same_as<std::decay_t<Ex>, executor_ref>)
153  
#endif
153  
#endif
154  
    executor_ref(Ex const& ex) noexcept
154  
    executor_ref(Ex const& ex) noexcept
155  
        : ex_(&ex)
155  
        : ex_(&ex)
156  
        , vt_(&detail::vtable_for<Ex>)
156  
        , vt_(&detail::vtable_for<Ex>)
157  
    {
157  
    {
158  
    }
158  
    }
159  

159  

160  
    /** Returns true if this instance holds a valid executor.
160  
    /** Returns true if this instance holds a valid executor.
161  

161  

162  
        @return `true` if constructed with an executor, `false` if
162  
        @return `true` if constructed with an executor, `false` if
163  
                default-constructed.
163  
                default-constructed.
164  
    */
164  
    */
165  
    explicit operator bool() const noexcept
165  
    explicit operator bool() const noexcept
166  
    {
166  
    {
167  
        return ex_ != nullptr;
167  
        return ex_ != nullptr;
168  
    }
168  
    }
169  

169  

170  
    /** Returns a reference to the associated execution context.
170  
    /** Returns a reference to the associated execution context.
171  

171  

172  
        @return A reference to the execution context.
172  
        @return A reference to the execution context.
173  

173  

174  
        @pre This instance was constructed with a valid executor.
174  
        @pre This instance was constructed with a valid executor.
175  
    */
175  
    */
176  
    execution_context& context() const noexcept
176  
    execution_context& context() const noexcept
177  
    {
177  
    {
178  
        return vt_->context(ex_);
178  
        return vt_->context(ex_);
179  
    }
179  
    }
180  

180  

181  
    /** Informs the executor that work is beginning.
181  
    /** Informs the executor that work is beginning.
182  

182  

183  
        Must be paired with a subsequent call to `on_work_finished()`.
183  
        Must be paired with a subsequent call to `on_work_finished()`.
184  

184  

185  
        @pre This instance was constructed with a valid executor.
185  
        @pre This instance was constructed with a valid executor.
186  
    */
186  
    */
187  
    void on_work_started() const noexcept
187  
    void on_work_started() const noexcept
188  
    {
188  
    {
189  
        vt_->on_work_started(ex_);
189  
        vt_->on_work_started(ex_);
190  
    }
190  
    }
191  

191  

192  
    /** Informs the executor that work has completed.
192  
    /** Informs the executor that work has completed.
193  

193  

194  
        @pre A preceding call to `on_work_started()` was made.
194  
        @pre A preceding call to `on_work_started()` was made.
195  
        @pre This instance was constructed with a valid executor.
195  
        @pre This instance was constructed with a valid executor.
196  
    */
196  
    */
197  
    void on_work_finished() const noexcept
197  
    void on_work_finished() const noexcept
198  
    {
198  
    {
199  
        vt_->on_work_finished(ex_);
199  
        vt_->on_work_finished(ex_);
200  
    }
200  
    }
201  

201  

202  
    /** Dispatches a continuation through the wrapped executor.
202  
    /** Dispatches a continuation through the wrapped executor.
203  

203  

204  
        Returns a handle for symmetric transfer. If running in the
204  
        Returns a handle for symmetric transfer. If running in the
205  
        executor's thread, returns `c.h`. Otherwise, posts the
205  
        executor's thread, returns `c.h`. Otherwise, posts the
206  
        continuation for later execution and returns
206  
        continuation for later execution and returns
207  
        `std::noop_coroutine()`.
207  
        `std::noop_coroutine()`.
208  

208  

209  
        @param c The continuation to dispatch for resumption.
209  
        @param c The continuation to dispatch for resumption.
210  
                 Must remain at a stable address until dequeued.
210  
                 Must remain at a stable address until dequeued.
211  

211  

212  
        @return A handle for symmetric transfer or `std::noop_coroutine()`.
212  
        @return A handle for symmetric transfer or `std::noop_coroutine()`.
213  

213  

214  
        @pre This instance was constructed with a valid executor.
214  
        @pre This instance was constructed with a valid executor.
215  
    */
215  
    */
216  
    std::coroutine_handle<> dispatch(continuation& c) const
216  
    std::coroutine_handle<> dispatch(continuation& c) const
217  
    {
217  
    {
218  
        return vt_->dispatch(ex_, c);
218  
        return vt_->dispatch(ex_, c);
219  
    }
219  
    }
220  

220  

221  
    /** Posts a continuation to the wrapped executor.
221  
    /** Posts a continuation to the wrapped executor.
222  

222  

223  
        Posts the continuation to the executor for later execution
223  
        Posts the continuation to the executor for later execution
224  
        and returns. The caller should transfer to `std::noop_coroutine()`
224  
        and returns. The caller should transfer to `std::noop_coroutine()`
225  
        after calling this.
225  
        after calling this.
226  

226  

227  
        @param c The continuation to post for resumption.
227  
        @param c The continuation to post for resumption.
228  
                 Must remain at a stable address until dequeued.
228  
                 Must remain at a stable address until dequeued.
229  

229  

230  
        @pre This instance was constructed with a valid executor.
230  
        @pre This instance was constructed with a valid executor.
231  
    */
231  
    */
232  
    void post(continuation& c) const
232  
    void post(continuation& c) const
233  
    {
233  
    {
234  
        vt_->post(ex_, c);
234  
        vt_->post(ex_, c);
235  
    }
235  
    }
236  

236  

237  
    /** Compares two executor references for equality.
237  
    /** Compares two executor references for equality.
238  

238  

239  
        Two `executor_ref` instances are equal if they wrap
239  
        Two `executor_ref` instances are equal if they wrap
240  
        executors of the same type that compare equal.
240  
        executors of the same type that compare equal.
241  

241  

242  
        @param other The executor reference to compare against.
242  
        @param other The executor reference to compare against.
243  

243  

244  
        @return `true` if both wrap equal executors of the same type.
244  
        @return `true` if both wrap equal executors of the same type.
245  
    */
245  
    */
246  
    bool operator==(executor_ref const& other) const noexcept
246  
    bool operator==(executor_ref const& other) const noexcept
247  
    {
247  
    {
248  
        if (ex_ == other.ex_)
248  
        if (ex_ == other.ex_)
249  
            return true;
249  
            return true;
250  
        if (vt_ != other.vt_)
250  
        if (vt_ != other.vt_)
251  
            return false;
251  
            return false;
252  
        return vt_->equals(ex_, other.ex_);
252  
        return vt_->equals(ex_, other.ex_);
253  
    }
253  
    }
254  

254  

255  
    /** Return a pointer to the wrapped executor if it matches
255  
    /** Return a pointer to the wrapped executor if it matches
256  
        the requested type.
256  
        the requested type.
257  

257  

258  
        Performs a type check against the stored executor and
258  
        Performs a type check against the stored executor and
259  
        returns a typed pointer when the types match, or
259  
        returns a typed pointer when the types match, or
260  
        `nullptr` otherwise. Analogous to
260  
        `nullptr` otherwise. Analogous to
261  
        `std::any_cast< Executor >( &a )`.
261  
        `std::any_cast< Executor >( &a )`.
262  

262  

263  
        @tparam Executor The executor type to retrieve.
263  
        @tparam Executor The executor type to retrieve.
264  

264  

265  
        @return A pointer to the underlying executor, or
265  
        @return A pointer to the underlying executor, or
266  
            `nullptr` if the type does not match.
266  
            `nullptr` if the type does not match.
267  
    */
267  
    */
268  
    template< typename Executor >
268  
    template< typename Executor >
269  
    const Executor* target() const
269  
    const Executor* target() const
270  
    {
270  
    {
271  
        if ( *vt_->type_id == detail::type_id< Executor >() )
271  
        if ( *vt_->type_id == detail::type_id< Executor >() )
272  
           return static_cast< Executor const* >( ex_ );
272  
           return static_cast< Executor const* >( ex_ );
273  
        return nullptr;
273  
        return nullptr;
274  
    }
274  
    }
275  

275  

276  
    /// @copydoc target() const
276  
    /// @copydoc target() const
277  
    template< typename Executor>
277  
    template< typename Executor>
278  
    Executor* target()
278  
    Executor* target()
279  
    {
279  
    {
280  
        if ( *vt_->type_id == detail::type_id< Executor >() )
280  
        if ( *vt_->type_id == detail::type_id< Executor >() )
281  
           return const_cast< Executor* >(
281  
           return const_cast< Executor* >(
282  
               static_cast< Executor const* >( ex_ ));
282  
               static_cast< Executor const* >( ex_ ));
283  
        return nullptr;
283  
        return nullptr;
284  
    }
284  
    }
285  
};
285  
};
286  

286  

287  
} // capy
287  
} // capy
288  
} // boost
288  
} // boost
289  

289  

290  
#endif
290  
#endif