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 <concepts>
15  
#include <concepts>
16  
#include <coroutine>
16  
#include <coroutine>
17  
#include <type_traits>
17  
#include <type_traits>
18  
#include <utility>
18  
#include <utility>
19  

19  

20  
namespace boost {
20  
namespace boost {
21  
namespace capy {
21  
namespace capy {
22  

22  

23  
class execution_context;
23  
class execution_context;
24  

24  

25  
namespace detail {
25  
namespace detail {
26  

26  

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

38  

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

69  

70  
} // detail
70  
} // detail
71  

71  

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

73  

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

79  

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

85  

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

90  

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

94  

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

102  

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

106  

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

113  

114  
public:
114  
public:
115  
    /** Default constructor.
115  
    /** Default constructor.
116  

116  

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

122  

123  
    /** Copy constructor.
123  
    /** Copy constructor.
124  

124  

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

130  

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

133  

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

135  

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

139  

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

158  

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

160  

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

168  

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

170  

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

172  

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

179  

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

181  

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

183  

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

190  

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

192  

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

200  

201  
    /** Dispatches a coroutine handle through the wrapped executor.
201  
    /** Dispatches a coroutine handle through the wrapped executor.
202  

202  

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

206  

207  
        @param h The coroutine handle to dispatch for resumption.
207  
        @param h The coroutine handle to dispatch for resumption.
208  

208  

209  
        @return A handle for symmetric transfer or `std::noop_coroutine()`.
209  
        @return A handle for symmetric transfer or `std::noop_coroutine()`.
210  

210  

211  
        @pre This instance was constructed with a valid executor.
211  
        @pre This instance was constructed with a valid executor.
212  
    */
212  
    */
213  
    std::coroutine_handle<> dispatch(std::coroutine_handle<> h) const
213  
    std::coroutine_handle<> dispatch(std::coroutine_handle<> h) const
214  
    {
214  
    {
215  
        return vt_->dispatch(ex_, h);
215  
        return vt_->dispatch(ex_, h);
216  
    }
216  
    }
217  

217  

218  
    /** Posts a coroutine handle to the wrapped executor.
218  
    /** Posts a coroutine handle to the wrapped executor.
219  

219  

220  
        Posts the coroutine handle to the executor for later execution
220  
        Posts the coroutine handle to the executor for later execution
221  
        and returns. The caller should transfer to `std::noop_coroutine()`
221  
        and returns. The caller should transfer to `std::noop_coroutine()`
222  
        after calling this.
222  
        after calling this.
223  

223  

224  
        @param h The coroutine handle to post for resumption.
224  
        @param h The coroutine handle to post for resumption.
225  

225  

226  
        @pre This instance was constructed with a valid executor.
226  
        @pre This instance was constructed with a valid executor.
227  
    */
227  
    */
228  
    void post(std::coroutine_handle<> h) const
228  
    void post(std::coroutine_handle<> h) const
229  
    {
229  
    {
230  
        vt_->post(ex_, h);
230  
        vt_->post(ex_, h);
231  
    }
231  
    }
232  

232  

233  
    /** Compares two executor references for equality.
233  
    /** Compares two executor references for equality.
234  

234  

235  
        Two `executor_ref` instances are equal if they wrap
235  
        Two `executor_ref` instances are equal if they wrap
236  
        executors of the same type that compare equal.
236  
        executors of the same type that compare equal.
237  

237  

238  
        @param other The executor reference to compare against.
238  
        @param other The executor reference to compare against.
239  

239  

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

250  

251  
    /** Return a pointer to the wrapped executor if it matches
251  
    /** Return a pointer to the wrapped executor if it matches
252  
        the requested type.
252  
        the requested type.
253  

253  

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

258  

259  
        @tparam Executor The executor type to retrieve.
259  
        @tparam Executor The executor type to retrieve.
260  

260  

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

271  

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

282  

283  
} // capy
283  
} // capy
284  
} // boost
284  
} // boost
285  

285  

286  
#endif
286  
#endif