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

15  

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*, std::coroutine_handle<>);
34  
    void (*post)(void const*, std::coroutine_handle<>);
35  
    void (*dispatch)(void const*, std::coroutine_handle<>);
35  
    void (*dispatch)(void const*, std::coroutine_handle<>);
36  
    bool (*equals)(void const*, void const*) noexcept;
36  
    bool (*equals)(void const*, void const*) noexcept;
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) {
59  
    [](void const* p, std::coroutine_handle<> h) {
60  
        static_cast<Ex const*>(p)->dispatch(h);
60  
        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  
};
66  
};
67  

67  

68  
} // detail
68  
} // detail
69  

69  

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

71  

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

77  

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

83  

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

88  

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

92  

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

100  

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

104  

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

111  

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

114  

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

120  

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

122  

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

128  

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

131  

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

133  

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

137  

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

156  

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

158  

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

166  

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

168  

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

170  

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

177  

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

179  

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

181  

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

188  

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

190  

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

198  

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

200  

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

205  

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

207  

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

214  

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

216  

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

220  

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

222  

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

229  

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

231  

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

234  

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

236  

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

248  

249  
} // capy
249  
} // capy
250  
} // boost
250  
} // boost
251  

251  

252  
#endif
252  
#endif