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/concept/executor.hpp>
15  
#include <boost/capy/continuation.hpp>
16  
#include <boost/capy/continuation.hpp>
16  
#include <concepts>
17  
#include <concepts>
17  
#include <coroutine>
18  
#include <coroutine>
18  
#include <type_traits>
19  
#include <type_traits>
19  
#include <utility>
20  
#include <utility>
20  

21  

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

24  

24  
class execution_context;
25  
class execution_context;
25  

26  

26  
namespace detail {
27  
namespace detail {
27  

28  

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

40  

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

71  

71  
} // detail
72  
} // detail
72  

73  

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

75  

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

81  

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

87  

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

92  

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

96  

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

104  

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

108  

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

115  

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

118  

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

124  

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

126  

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

132  

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

135  

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

137  

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

141  

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

161  

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

163  

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

171  

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

173  

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

175  

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

182  

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

184  

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

186  

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

193  

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

195  

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

203  

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

205  

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

210  

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

213  

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

215  

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

222  

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

224  

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

228  

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

231  

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

238  

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

240  

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

243  

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

245  

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

256  

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

259  

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

264  

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

266  

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

277  

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

288  

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

291  

290  
#endif
292  
#endif