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

16  

17  
#include <concepts>
17  
#include <concepts>
18  
#include <coroutine>
18  
#include <coroutine>
19  
#include <type_traits>
19  
#include <type_traits>
20  
#include <utility>
20  
#include <utility>
21  

21  

22  
namespace boost {
22  
namespace boost {
23  
namespace capy {
23  
namespace capy {
24  

24  

25  
class execution_context;
25  
class execution_context;
26  

26  

27  
namespace detail {
27  
namespace detail {
28  

28  

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

40  

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

71  

72  
} // detail
72  
} // detail
73  

73  

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

75  

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

81  

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

87  

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

92  

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

96  

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

104  

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

108  

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

115  

116  
public:
116  
public:
117  
    /** Default constructor.
117  
    /** Default constructor.
118  

118  

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

124  

125  
    /** Copy constructor.
125  
    /** Copy constructor.
126  

126  

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

132  

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

135  

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

137  

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

141  

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

160  

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

162  

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

170  

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

172  

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

174  

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

181  

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

183  

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

185  

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

192  

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

194  

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

202  

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

204  

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

209  

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

211  

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

218  

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

220  

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

224  

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

226  

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

233  

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

235  

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

238  

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

240  

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

251  

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

253  

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

255  

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

263  

264  
} // capy
264  
} // capy
265  
} // boost
265  
} // boost
266  

266  

267  
#endif
267  
#endif