Line data Source code
1 : //
2 : // Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
3 : //
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)
6 : //
7 : // Official repository: https://github.com/cppalliance/capy
8 : //
9 :
10 : #ifndef BOOST_CAPY_EXECUTOR_REF_HPP
11 : #define BOOST_CAPY_EXECUTOR_REF_HPP
12 :
13 : #include <boost/capy/detail/config.hpp>
14 : #include <boost/capy/detail/type_id.hpp>
15 : #include <boost/capy/coro.hpp>
16 :
17 : #include <concepts>
18 : #include <coroutine>
19 : #include <type_traits>
20 : #include <utility>
21 :
22 : namespace boost {
23 : namespace capy {
24 :
25 : class execution_context;
26 :
27 : namespace detail {
28 :
29 : /** Virtual function table for type-erased executor operations. */
30 : struct executor_vtable
31 : {
32 : execution_context& (*context)(void const*) noexcept;
33 : void (*on_work_started)(void const*) noexcept;
34 : void (*on_work_finished)(void const*) noexcept;
35 : void (*post)(void const*, std::coroutine_handle<>);
36 : void (*dispatch)(void const*, std::coroutine_handle<>);
37 : bool (*equals)(void const*, void const*) noexcept;
38 : detail::type_info const* type_id;
39 : };
40 :
41 : /** Vtable instance for a specific executor type. */
42 : template<class Ex>
43 : inline constexpr executor_vtable vtable_for = {
44 : // context
45 0 : [](void const* p) noexcept -> execution_context& {
46 0 : return const_cast<Ex*>(static_cast<Ex const*>(p))->context();
47 : },
48 : // on_work_started
49 0 : [](void const* p) noexcept {
50 0 : const_cast<Ex*>(static_cast<Ex const*>(p))->on_work_started();
51 : },
52 : // on_work_finished
53 0 : [](void const* p) noexcept {
54 0 : const_cast<Ex*>(static_cast<Ex const*>(p))->on_work_finished();
55 : },
56 : // post
57 94 : [](void const* p, std::coroutine_handle<> h) {
58 47 : static_cast<Ex const*>(p)->post(h);
59 : },
60 : // dispatch
61 596 : [](void const* p, std::coroutine_handle<> h) {
62 298 : static_cast<Ex const*>(p)->dispatch(h);
63 : },
64 : // equals
65 9 : [](void const* a, void const* b) noexcept -> bool {
66 9 : return *static_cast<Ex const*>(a) == *static_cast<Ex const*>(b);
67 : },
68 : // type_id
69 : &detail::type_id<Ex>()
70 : };
71 :
72 : } // detail
73 :
74 : /** A type-erased reference wrapper for executor objects.
75 :
76 : This class provides type erasure for any executor type, enabling
77 : runtime polymorphism without virtual functions or allocation.
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
80 : uniformly while satisfying the full `Executor` concept.
81 :
82 : @par Reference Semantics
83 : This class has reference semantics: it does not allocate or own
84 : the wrapped executor. Copy operations simply copy the internal
85 : pointers. The caller must ensure the referenced executor outlives
86 : all `executor_ref` instances that wrap it.
87 :
88 : @par Thread Safety
89 : The `executor_ref` itself is not thread-safe for concurrent
90 : modification, but its executor operations are safe to call
91 : concurrently if the underlying executor supports it.
92 :
93 : @par Executor Concept
94 : This class satisfies the `Executor` concept, making it usable
95 : anywhere a concrete executor is expected.
96 :
97 : @par Example
98 : @code
99 : void store_executor(executor_ref ex)
100 : {
101 : if(ex)
102 : ex.post(my_coroutine);
103 : }
104 :
105 : io_context ctx;
106 : store_executor(ctx.get_executor());
107 : @endcode
108 :
109 : @see any_executor, Executor
110 : */
111 : class executor_ref
112 : {
113 : void const* ex_ = nullptr;
114 : detail::executor_vtable const* vt_ = nullptr;
115 :
116 : public:
117 : /** Default constructor.
118 :
119 : Constructs an empty `executor_ref`. Calling any executor
120 : operations on a default-constructed instance results in
121 : undefined behavior.
122 : */
123 6683 : executor_ref() = default;
124 :
125 : /** Copy constructor.
126 :
127 : Copies the internal pointers, preserving identity.
128 : This enables the same-executor optimization when passing
129 : executor_ref through coroutine chains.
130 : */
131 : executor_ref(executor_ref const&) = default;
132 :
133 : /** Copy assignment operator. */
134 : executor_ref& operator=(executor_ref const&) = default;
135 :
136 : /** Constructs from any executor type.
137 :
138 : Captures a reference to the given executor and stores a pointer
139 : to the type-specific vtable. The executor must remain valid for
140 : the lifetime of this `executor_ref` instance.
141 :
142 : @param ex The executor to wrap. Must satisfy the `Executor`
143 : concept. A pointer to this object is stored
144 : internally; the executor must outlive this wrapper.
145 : */
146 : #if defined(__GNUC__) && !defined(__clang__)
147 : // GCC constraint satisfaction caching bug workaround
148 : template<class Ex,
149 : std::enable_if_t<!std::is_same_v<
150 : std::decay_t<Ex>, executor_ref>, int> = 0>
151 : #else
152 : template<class Ex>
153 : requires (!std::same_as<std::decay_t<Ex>, executor_ref>)
154 : #endif
155 3674 : executor_ref(Ex const& ex) noexcept
156 3674 : : ex_(&ex)
157 3674 : , vt_(&detail::vtable_for<Ex>)
158 : {
159 3674 : }
160 :
161 : /** Returns true if this instance holds a valid executor.
162 :
163 : @return `true` if constructed with an executor, `false` if
164 : default-constructed.
165 : */
166 22 : explicit operator bool() const noexcept
167 : {
168 22 : return ex_ != nullptr;
169 : }
170 :
171 : /** Returns a reference to the associated execution context.
172 :
173 : @return A reference to the execution context.
174 :
175 : @pre This instance was constructed with a valid executor.
176 : */
177 : execution_context& context() const noexcept
178 : {
179 : return vt_->context(ex_);
180 : }
181 :
182 : /** Informs the executor that work is beginning.
183 :
184 : Must be paired with a subsequent call to `on_work_finished()`.
185 :
186 : @pre This instance was constructed with a valid executor.
187 : */
188 : void on_work_started() const noexcept
189 : {
190 : vt_->on_work_started(ex_);
191 : }
192 :
193 : /** Informs the executor that work has completed.
194 :
195 : @pre A preceding call to `on_work_started()` was made.
196 : @pre This instance was constructed with a valid executor.
197 : */
198 : void on_work_finished() const noexcept
199 : {
200 : vt_->on_work_finished(ex_);
201 : }
202 :
203 : /** Dispatches a coroutine handle through the wrapped executor.
204 :
205 : Invokes the executor's `dispatch()` operation with the given
206 : coroutine handle. If running in the executor's thread, resumes
207 : the coroutine inline via a normal function call. Otherwise,
208 : posts the coroutine for later execution.
209 :
210 : @param h The coroutine handle to dispatch for resumption.
211 :
212 : @pre This instance was constructed with a valid executor.
213 : */
214 298 : void dispatch(coro h) const
215 : {
216 298 : vt_->dispatch(ex_, h);
217 298 : }
218 :
219 : /** Posts a coroutine handle to the wrapped executor.
220 :
221 : Posts the coroutine handle to the executor for later execution
222 : and returns. The caller should transfer to `std::noop_coroutine()`
223 : after calling this.
224 :
225 : @param h The coroutine handle to post for resumption.
226 :
227 : @pre This instance was constructed with a valid executor.
228 : */
229 47 : void post(coro h) const
230 : {
231 47 : vt_->post(ex_, h);
232 47 : }
233 :
234 : /** Compares two executor references for equality.
235 :
236 : Two `executor_ref` instances are equal if they wrap
237 : executors of the same type that compare equal.
238 :
239 : @param other The executor reference to compare against.
240 :
241 : @return `true` if both wrap equal executors of the same type.
242 : */
243 3134 : bool operator==(executor_ref const& other) const noexcept
244 : {
245 3134 : if (ex_ == other.ex_)
246 3125 : return true;
247 9 : if (vt_ != other.vt_)
248 0 : return false;
249 9 : 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 4 : detail::type_info const& type_id() const noexcept
259 : {
260 4 : return *vt_->type_id;
261 : }
262 : };
263 :
264 : } // capy
265 : } // boost
266 :
267 : #endif
|