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_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/coro.hpp>
15  
#include <boost/capy/coro.hpp>
15  

16  

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*, std::coroutine_handle<>);
35  
    void (*post)(void const*, std::coroutine_handle<>);
35  
    void (*dispatch)(void const*, std::coroutine_handle<>);
36  
    void (*dispatch)(void const*, std::coroutine_handle<>);
36  
    bool (*equals)(void const*, void const*) noexcept;
37  
    bool (*equals)(void const*, void const*) noexcept;
 
38 +
    detail::type_info const* type_id;
37  
};
39  
};
38  

40  

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

71  

68  
} // detail
72  
} // detail
69  

73  

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

75  

72  
    This class provides type erasure for any executor type, enabling
76  
    This class provides type erasure for any executor type, enabling
73  
    runtime polymorphism without virtual functions or allocation.
77  
    runtime polymorphism without virtual functions or allocation.
74  
    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
75  
    static vtable, allowing executors of different types to be stored
79  
    static vtable, allowing executors of different types to be stored
76  
    uniformly while satisfying the full `Executor` concept.
80  
    uniformly while satisfying the full `Executor` concept.
77  

81  

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

87  

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

92  

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

96  

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

104  

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

108  

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

115  

112  
public:
116  
public:
113  
    /** Default constructor.
117  
    /** Default constructor.
114  

118  

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

124  

121  
    /** Copy constructor.
125  
    /** Copy constructor.
122  

126  

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

132  

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

135  

132  
    /** Constructs from any executor type.
136  
    /** Constructs from any executor type.
133  

137  

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

141  

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

160  

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

162  

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

170  

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

172  

169  
        @return A reference to the execution context.
173  
        @return A reference to the execution context.
170  

174  

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

181  

178  
    /** Informs the executor that work is beginning.
182  
    /** Informs the executor that work is beginning.
179  

183  

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

185  

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

192  

189  
    /** Informs the executor that work has completed.
193  
    /** Informs the executor that work has completed.
190  

194  

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

202  

199  
    /** Dispatches a coroutine handle through the wrapped executor.
203  
    /** Dispatches a coroutine handle through the wrapped executor.
200  

204  

201  
        Invokes the executor's `dispatch()` operation with the given
205  
        Invokes the executor's `dispatch()` operation with the given
202  
        coroutine handle. If running in the executor's thread, resumes
206  
        coroutine handle. If running in the executor's thread, resumes
203  
        the coroutine inline via a normal function call. Otherwise,
207  
        the coroutine inline via a normal function call. Otherwise,
204  
        posts the coroutine for later execution.
208  
        posts the coroutine for later execution.
205  

209  

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

211  

208  
        @pre This instance was constructed with a valid executor.
212  
        @pre This instance was constructed with a valid executor.
209  
    */
213  
    */
210  
    void dispatch(coro h) const
214  
    void dispatch(coro h) const
211  
    {
215  
    {
212  
        vt_->dispatch(ex_, h);
216  
        vt_->dispatch(ex_, h);
213  
    }
217  
    }
214  

218  

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

220  

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

224  

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

226  

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

233  

230  
    /** Compares two executor references for equality.
234  
    /** Compares two executor references for equality.
231  

235  

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

238  

235  
        @param other The executor reference to compare against.
239  
        @param other The executor reference to compare against.
236  

240  

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

 
252 +
    /** Returns the type info of the underlying executor type.
 
253 +

 
254 +
        @return A reference to the type_info for the wrapped executor.
 
255 +

 
256 +
        @pre This instance was constructed with a valid executor.
 
257 +
    */
 
258 +
    detail::type_info const& type_id() const noexcept
 
259 +
    {
 
260 +
        return *vt_->type_id;
246  
    }
261  
    }
247  
};
262  
};
248  

263  

249  
} // capy
264  
} // capy
250  
} // boost
265  
} // boost
251  

266  

252  
#endif
267  
#endif