include/boost/capy/ex/executor_ref.hpp

60.6% Lines (80/132) 42.3% List of functions (30/71)
executor_ref.hpp
f(x) Functions (71)
Function Calls Lines Blocks
boost::capy::detail::vtable_for<boost::capy::any_executor>::{lambda(void const*)#1}::operator()(void const*) const :45 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::queue_executor>::{lambda(void const*)#1}::operator()(void const*) const :45 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::queuing_executor>::{lambda(void const*)#1}::operator()(void const*) const :45 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::strand<boost::capy::thread_pool::executor_type> >::{lambda(void const*)#1}::operator()(void const*) const :45 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::sync_executor>::{lambda(void const*)#1}::operator()(void const*) const :45 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::test::blocking_executor>::{lambda(void const*)#1}::operator()(void const*) const :45 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::test_executor>::{lambda(void const*)#1}::operator()(void const*) const :45 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::thread_pool::executor_type>::{lambda(void const*)#1}::operator()(void const*) const :45 20x 100.0% 100.0% boost::capy::detail::vtable_for<boost::capy::tracking_executor>::{lambda(void const*)#1}::operator()(void const*) const :45 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::any_executor>::{lambda(void const*)#2}::operator()(void const*) const :49 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::queue_executor>::{lambda(void const*)#2}::operator()(void const*) const :49 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::queuing_executor>::{lambda(void const*)#2}::operator()(void const*) const :49 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::strand<boost::capy::thread_pool::executor_type> >::{lambda(void const*)#2}::operator()(void const*) const :49 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::sync_executor>::{lambda(void const*)#2}::operator()(void const*) const :49 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::test::blocking_executor>::{lambda(void const*)#2}::operator()(void const*) const :49 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::test_executor>::{lambda(void const*)#2}::operator()(void const*) const :49 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::thread_pool::executor_type>::{lambda(void const*)#2}::operator()(void const*) const :49 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::tracking_executor>::{lambda(void const*)#2}::operator()(void const*) const :49 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::any_executor>::{lambda(void const*)#3}::operator()(void const*) const :53 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::queue_executor>::{lambda(void const*)#3}::operator()(void const*) const :53 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::queuing_executor>::{lambda(void const*)#3}::operator()(void const*) const :53 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::strand<boost::capy::thread_pool::executor_type> >::{lambda(void const*)#3}::operator()(void const*) const :53 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::sync_executor>::{lambda(void const*)#3}::operator()(void const*) const :53 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::test::blocking_executor>::{lambda(void const*)#3}::operator()(void const*) const :53 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::test_executor>::{lambda(void const*)#3}::operator()(void const*) const :53 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::thread_pool::executor_type>::{lambda(void const*)#3}::operator()(void const*) const :53 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::tracking_executor>::{lambda(void const*)#3}::operator()(void const*) const :53 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::any_executor>::{lambda(void const*, boost::capy::continuation&)#1}::operator()(void const*, boost::capy::continuation&) const :57 4x 100.0% 100.0% boost::capy::detail::vtable_for<boost::capy::queue_executor>::{lambda(void const*, boost::capy::continuation&)#1}::operator()(void const*, boost::capy::continuation&) const :57 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::queuing_executor>::{lambda(void const*, boost::capy::continuation&)#1}::operator()(void const*, boost::capy::continuation&) const :57 28x 100.0% 100.0% boost::capy::detail::vtable_for<boost::capy::strand<boost::capy::thread_pool::executor_type> >::{lambda(void const*, boost::capy::continuation&)#1}::operator()(void const*, boost::capy::continuation&) const :57 6x 100.0% 100.0% boost::capy::detail::vtable_for<boost::capy::sync_executor>::{lambda(void const*, boost::capy::continuation&)#1}::operator()(void const*, boost::capy::continuation&) const :57 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::test::blocking_executor>::{lambda(void const*, boost::capy::continuation&)#1}::operator()(void const*, boost::capy::continuation&) const :57 75x 100.0% 100.0% boost::capy::detail::vtable_for<boost::capy::test_executor>::{lambda(void const*, boost::capy::continuation&)#1}::operator()(void const*, boost::capy::continuation&) const :57 159x 100.0% 100.0% boost::capy::detail::vtable_for<boost::capy::thread_pool::executor_type>::{lambda(void const*, boost::capy::continuation&)#1}::operator()(void const*, boost::capy::continuation&) const :57 328x 100.0% 100.0% boost::capy::detail::vtable_for<boost::capy::tracking_executor>::{lambda(void const*, boost::capy::continuation&)#1}::operator()(void const*, boost::capy::continuation&) const :57 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::any_executor>::{lambda(void const*, boost::capy::continuation&)#2}::operator()(void const*, boost::capy::continuation&) const :61 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::queue_executor>::{lambda(void const*, boost::capy::continuation&)#2}::operator()(void const*, boost::capy::continuation&) const :61 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::queuing_executor>::{lambda(void const*, boost::capy::continuation&)#2}::operator()(void const*, boost::capy::continuation&) const :61 1x 100.0% 100.0% boost::capy::detail::vtable_for<boost::capy::strand<boost::capy::thread_pool::executor_type> >::{lambda(void const*, boost::capy::continuation&)#2}::operator()(void const*, boost::capy::continuation&) const :61 3x 100.0% 100.0% boost::capy::detail::vtable_for<boost::capy::sync_executor>::{lambda(void const*, boost::capy::continuation&)#2}::operator()(void const*, boost::capy::continuation&) const :61 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::test::blocking_executor>::{lambda(void const*, boost::capy::continuation&)#2}::operator()(void const*, boost::capy::continuation&) const :61 25x 100.0% 100.0% boost::capy::detail::vtable_for<boost::capy::test_executor>::{lambda(void const*, boost::capy::continuation&)#2}::operator()(void const*, boost::capy::continuation&) const :61 74x 100.0% 100.0% boost::capy::detail::vtable_for<boost::capy::thread_pool::executor_type>::{lambda(void const*, boost::capy::continuation&)#2}::operator()(void const*, boost::capy::continuation&) const :61 14x 100.0% 100.0% boost::capy::detail::vtable_for<boost::capy::tracking_executor>::{lambda(void const*, boost::capy::continuation&)#2}::operator()(void const*, boost::capy::continuation&) const :61 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::any_executor>::{lambda(void const*, void const*)#1}::operator()(void const*, void const*) const :65 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::queue_executor>::{lambda(void const*, void const*)#1}::operator()(void const*, void const*) const :65 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::queuing_executor>::{lambda(void const*, void const*)#1}::operator()(void const*, void const*) const :65 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::strand<boost::capy::thread_pool::executor_type> >::{lambda(void const*, void const*)#1}::operator()(void const*, void const*) const :65 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::sync_executor>::{lambda(void const*, void const*)#1}::operator()(void const*, void const*) const :65 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::test::blocking_executor>::{lambda(void const*, void const*)#1}::operator()(void const*, void const*) const :65 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::test_executor>::{lambda(void const*, void const*)#1}::operator()(void const*, void const*) const :65 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::thread_pool::executor_type>::{lambda(void const*, void const*)#1}::operator()(void const*, void const*) const :65 1x 50.0% 100.0% boost::capy::detail::vtable_for<boost::capy::tracking_executor>::{lambda(void const*, void const*)#1}::operator()(void const*, void const*) const :65 0 0.0% 0.0% boost::capy::executor_ref::executor_ref() :123 4176x 100.0% 100.0% boost::capy::executor_ref::executor_ref<boost::capy::any_executor, 0>(boost::capy::any_executor const&) :156 20x 100.0% 100.0% boost::capy::executor_ref::executor_ref<boost::capy::queue_executor, 0>(boost::capy::queue_executor const&) :156 7x 100.0% 100.0% boost::capy::executor_ref::executor_ref<boost::capy::queuing_executor, 0>(boost::capy::queuing_executor const&) :156 31x 100.0% 100.0% boost::capy::executor_ref::executor_ref<boost::capy::strand<boost::capy::thread_pool::executor_type>, 0>(boost::capy::strand<boost::capy::thread_pool::executor_type> const&) :156 4x 100.0% 100.0% boost::capy::executor_ref::executor_ref<boost::capy::sync_executor, 0>(boost::capy::sync_executor const&) :156 20x 100.0% 100.0% boost::capy::executor_ref::executor_ref<boost::capy::test::blocking_executor, 0>(boost::capy::test::blocking_executor const&) :156 2804x 100.0% 100.0% boost::capy::executor_ref::executor_ref<boost::capy::test_executor, 0>(boost::capy::test_executor const&) :156 137x 100.0% 100.0% boost::capy::executor_ref::executor_ref<boost::capy::thread_pool::executor_type, 0>(boost::capy::thread_pool::executor_type const&) :156 665x 100.0% 100.0% boost::capy::executor_ref::executor_ref<boost::capy::tracking_executor, 0>(boost::capy::tracking_executor const&) :156 1x 100.0% 100.0% boost::capy::executor_ref::operator bool() const :167 6x 100.0% 100.0% boost::capy::executor_ref::context() const :178 20x 100.0% 100.0% boost::capy::executor_ref::dispatch(boost::capy::continuation&) const :218 117x 100.0% 100.0% boost::capy::executor_ref::post(boost::capy::continuation&) const :234 600x 100.0% 100.0% boost::capy::executor_ref::operator==(boost::capy::executor_ref const&) const :248 6x 83.3% 83.0% boost::capy::thread_pool::executor_type const* boost::capy::executor_ref::target<boost::capy::thread_pool::executor_type>() const :271 1x 75.0% 83.0% boost::capy::thread_pool::executor_type* boost::capy::executor_ref::target<boost::capy::thread_pool::executor_type>() :280 2x 100.0% 100.0%
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.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/concept/executor.hpp>
16 #include <boost/capy/continuation.hpp>
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*, continuation&);
36 std::coroutine_handle<> (*dispatch)(void const*, continuation&);
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 20x [](void const* p) noexcept -> execution_context& {
46 20x return const_cast<Ex*>(static_cast<Ex const*>(p))->context();
47 },
48 // on_work_started
49 [](void const* p) noexcept {
50 const_cast<Ex*>(static_cast<Ex const*>(p))->on_work_started();
51 },
52 // on_work_finished
53 [](void const* p) noexcept {
54 const_cast<Ex*>(static_cast<Ex const*>(p))->on_work_finished();
55 },
56 // post
57 1200x [](void const* p, continuation& c) {
58 600x static_cast<Ex const*>(p)->post(c);
59 },
60 // dispatch
61 117x [](void const* p, continuation& c) -> std::coroutine_handle<> {
62 117x return static_cast<Ex const*>(p)->dispatch(c);
63 },
64 // equals
65 1x [](void const* a, void const* b) noexcept -> bool {
66 1x 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_continuation);
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 /** Construct a default instance.
118
119 Constructs an empty `executor_ref`. Calling any executor
120 operations on a default-constructed instance results in
121 undefined behavior.
122 */
123 4176x executor_ref() = default;
124
125 /** Construct a copy.
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 && Executor<Ex>)
155 #endif
156 3689x executor_ref(Ex const& ex) noexcept
157 3689x : ex_(&ex)
158 3689x , vt_(&detail::vtable_for<Ex>)
159 {
160 3689x }
161
162 /** Returns true if this instance holds a valid executor.
163
164 @return `true` if constructed with an executor, `false` if
165 default-constructed.
166 */
167 6x explicit operator bool() const noexcept
168 {
169 6x return ex_ != nullptr;
170 }
171
172 /** Returns a reference to the associated execution context.
173
174 @return A reference to the execution context.
175
176 @pre This instance was constructed with a valid executor.
177 */
178 20x execution_context& context() const noexcept
179 {
180 20x return vt_->context(ex_);
181 }
182
183 /** Informs the executor that work is beginning.
184
185 Must be paired with a subsequent call to `on_work_finished()`.
186
187 @pre This instance was constructed with a valid executor.
188 */
189 void on_work_started() const noexcept
190 {
191 vt_->on_work_started(ex_);
192 }
193
194 /** Informs the executor that work has completed.
195
196 @pre A preceding call to `on_work_started()` was made.
197 @pre This instance was constructed with a valid executor.
198 */
199 void on_work_finished() const noexcept
200 {
201 vt_->on_work_finished(ex_);
202 }
203
204 /** Dispatches a continuation through the wrapped executor.
205
206 Returns a handle for symmetric transfer. If running in the
207 executor's thread, returns `c.h`. Otherwise, posts the
208 continuation for later execution and returns
209 `std::noop_coroutine()`.
210
211 @param c The continuation to dispatch for resumption.
212 Must remain at a stable address until dequeued.
213
214 @return A handle for symmetric transfer or `std::noop_coroutine()`.
215
216 @pre This instance was constructed with a valid executor.
217 */
218 117x std::coroutine_handle<> dispatch(continuation& c) const
219 {
220 117x return vt_->dispatch(ex_, c);
221 }
222
223 /** Posts a continuation to the wrapped executor.
224
225 Posts the continuation to the executor for later execution
226 and returns. The caller should transfer to `std::noop_coroutine()`
227 after calling this.
228
229 @param c The continuation to post for resumption.
230 Must remain at a stable address until dequeued.
231
232 @pre This instance was constructed with a valid executor.
233 */
234 600x void post(continuation& c) const
235 {
236 600x vt_->post(ex_, c);
237 600x }
238
239 /** Compares two executor references for equality.
240
241 Two `executor_ref` instances are equal if they wrap
242 executors of the same type that compare equal.
243
244 @param other The executor reference to compare against.
245
246 @return `true` if both wrap equal executors of the same type.
247 */
248 6x bool operator==(executor_ref const& other) const noexcept
249 {
250 6x if (ex_ == other.ex_)
251 5x return true;
252 1x if (vt_ != other.vt_)
253 return false;
254 1x return vt_->equals(ex_, other.ex_);
255 }
256
257 /** Return a pointer to the wrapped executor if it matches
258 the requested type.
259
260 Performs a type check against the stored executor and
261 returns a typed pointer when the types match, or
262 `nullptr` otherwise. Analogous to
263 `std::any_cast< Executor >( &a )`.
264
265 @tparam Executor The executor type to retrieve.
266
267 @return A pointer to the underlying executor, or
268 `nullptr` if the type does not match.
269 */
270 template< typename Executor >
271 1x const Executor* target() const noexcept
272 {
273 1x if ( *vt_->type_id == detail::type_id< Executor >() )
274 1x return static_cast< Executor const* >( ex_ );
275 return nullptr;
276 }
277
278 /// @copydoc target() const
279 template< typename Executor>
280 2x Executor* target() noexcept
281 {
282 2x if ( *vt_->type_id == detail::type_id< Executor >() )
283 return const_cast< Executor* >(
284 1x static_cast< Executor const* >( ex_ ));
285 1x return nullptr;
286 }
287 };
288
289 } // capy
290 } // boost
291
292 #endif
293