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 :44 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::queue_executor>::{lambda(void const*)#1}::operator()(void const*) const :44 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::queuing_executor>::{lambda(void const*)#1}::operator()(void const*) const :44 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 :44 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::sync_executor>::{lambda(void const*)#1}::operator()(void const*) const :44 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::test::blocking_executor>::{lambda(void const*)#1}::operator()(void const*) const :44 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::test_executor>::{lambda(void const*)#1}::operator()(void const*) const :44 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::thread_pool::executor_type>::{lambda(void const*)#1}::operator()(void const*) const :44 20x 100.0% 100.0% boost::capy::detail::vtable_for<boost::capy::tracking_executor>::{lambda(void const*)#1}::operator()(void const*) const :44 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::any_executor>::{lambda(void const*)#2}::operator()(void const*) const :48 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::queue_executor>::{lambda(void const*)#2}::operator()(void const*) const :48 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::queuing_executor>::{lambda(void const*)#2}::operator()(void const*) const :48 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 :48 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::sync_executor>::{lambda(void const*)#2}::operator()(void const*) const :48 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::test::blocking_executor>::{lambda(void const*)#2}::operator()(void const*) const :48 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::test_executor>::{lambda(void const*)#2}::operator()(void const*) const :48 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::thread_pool::executor_type>::{lambda(void const*)#2}::operator()(void const*) const :48 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::tracking_executor>::{lambda(void const*)#2}::operator()(void const*) const :48 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::any_executor>::{lambda(void const*)#3}::operator()(void const*) const :52 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::queue_executor>::{lambda(void const*)#3}::operator()(void const*) const :52 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::queuing_executor>::{lambda(void const*)#3}::operator()(void const*) const :52 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 :52 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::sync_executor>::{lambda(void const*)#3}::operator()(void const*) const :52 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::test::blocking_executor>::{lambda(void const*)#3}::operator()(void const*) const :52 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::test_executor>::{lambda(void const*)#3}::operator()(void const*) const :52 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::thread_pool::executor_type>::{lambda(void const*)#3}::operator()(void const*) const :52 0 0.0% 0.0% boost::capy::detail::vtable_for<boost::capy::tracking_executor>::{lambda(void const*)#3}::operator()(void const*) const :52 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 :56 1x 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 :56 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 :56 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 :56 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 :56 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 :56 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 :56 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 :56 327x 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 :56 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 :60 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 :60 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 :60 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 :60 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 :60 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 :60 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 :60 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 :60 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 :60 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 :64 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 :64 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 :64 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 :64 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 :64 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 :64 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 :64 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 :64 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 :64 0 0.0% 0.0% boost::capy::executor_ref::executor_ref() :122 4176x 100.0% 100.0% boost::capy::executor_ref::executor_ref<boost::capy::any_executor, 0>(boost::capy::any_executor const&) :154 20x 100.0% 100.0% boost::capy::executor_ref::executor_ref<boost::capy::queue_executor, 0>(boost::capy::queue_executor const&) :154 7x 100.0% 100.0% boost::capy::executor_ref::executor_ref<boost::capy::queuing_executor, 0>(boost::capy::queuing_executor const&) :154 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&) :154 4x 100.0% 100.0% boost::capy::executor_ref::executor_ref<boost::capy::sync_executor, 0>(boost::capy::sync_executor const&) :154 20x 100.0% 100.0% boost::capy::executor_ref::executor_ref<boost::capy::test::blocking_executor, 0>(boost::capy::test::blocking_executor const&) :154 2804x 100.0% 100.0% boost::capy::executor_ref::executor_ref<boost::capy::test_executor, 0>(boost::capy::test_executor const&) :154 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&) :154 665x 100.0% 100.0% boost::capy::executor_ref::executor_ref<boost::capy::tracking_executor, 0>(boost::capy::tracking_executor const&) :154 1x 100.0% 100.0% boost::capy::executor_ref::operator bool() const :165 6x 100.0% 100.0% boost::capy::executor_ref::context() const :176 20x 100.0% 100.0% boost::capy::executor_ref::dispatch(boost::capy::continuation&) const :216 117x 100.0% 100.0% boost::capy::executor_ref::post(boost::capy::continuation&) const :232 596x 100.0% 100.0% boost::capy::executor_ref::operator==(boost::capy::executor_ref const&) const :246 6x 83.3% 83.0% boost::capy::thread_pool::executor_type const* boost::capy::executor_ref::target<boost::capy::thread_pool::executor_type>() const :269 1x 75.0% 83.0% boost::capy::thread_pool::executor_type* boost::capy::executor_ref::target<boost::capy::thread_pool::executor_type>() :278 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/continuation.hpp>
16 #include <concepts>
17 #include <coroutine>
18 #include <type_traits>
19 #include <utility>
20
21 namespace boost {
22 namespace capy {
23
24 class execution_context;
25
26 namespace detail {
27
28 /** Virtual function table for type-erased executor operations. */
29 struct executor_vtable
30 {
31 execution_context& (*context)(void const*) noexcept;
32 void (*on_work_started)(void const*) noexcept;
33 void (*on_work_finished)(void const*) noexcept;
34 void (*post)(void const*, continuation&);
35 std::coroutine_handle<> (*dispatch)(void const*, continuation&);
36 bool (*equals)(void const*, void const*) noexcept;
37 detail::type_info const* type_id;
38 };
39
40 /** Vtable instance for a specific executor type. */
41 template<class Ex>
42 inline constexpr executor_vtable vtable_for = {
43 // context
44 20x [](void const* p) noexcept -> execution_context& {
45 20x return const_cast<Ex*>(static_cast<Ex const*>(p))->context();
46 },
47 // on_work_started
48 [](void const* p) noexcept {
49 const_cast<Ex*>(static_cast<Ex const*>(p))->on_work_started();
50 },
51 // on_work_finished
52 [](void const* p) noexcept {
53 const_cast<Ex*>(static_cast<Ex const*>(p))->on_work_finished();
54 },
55 // post
56 1192x [](void const* p, continuation& c) {
57 596x static_cast<Ex const*>(p)->post(c);
58 },
59 // dispatch
60 117x [](void const* p, continuation& c) -> std::coroutine_handle<> {
61 117x return static_cast<Ex const*>(p)->dispatch(c);
62 },
63 // equals
64 1x [](void const* a, void const* b) noexcept -> bool {
65 1x return *static_cast<Ex const*>(a) == *static_cast<Ex const*>(b);
66 },
67 // type_id
68 &detail::type_id<Ex>()
69 };
70
71 } // detail
72
73 /** A type-erased reference wrapper for executor objects.
74
75 This class provides type erasure for any executor type, enabling
76 runtime polymorphism without virtual functions or allocation.
77 It stores a pointer to the original executor and a pointer to a
78 static vtable, allowing executors of different types to be stored
79 uniformly while satisfying the full `Executor` concept.
80
81 @par Reference Semantics
82 This class has reference semantics: it does not allocate or own
83 the wrapped executor. Copy operations simply copy the internal
84 pointers. The caller must ensure the referenced executor outlives
85 all `executor_ref` instances that wrap it.
86
87 @par Thread Safety
88 The `executor_ref` itself is not thread-safe for concurrent
89 modification, but its executor operations are safe to call
90 concurrently if the underlying executor supports it.
91
92 @par Executor Concept
93 This class satisfies the `Executor` concept, making it usable
94 anywhere a concrete executor is expected.
95
96 @par Example
97 @code
98 void store_executor(executor_ref ex)
99 {
100 if(ex)
101 ex.post(my_continuation);
102 }
103
104 io_context ctx;
105 store_executor(ctx.get_executor());
106 @endcode
107
108 @see any_executor, Executor
109 */
110 class executor_ref
111 {
112 void const* ex_ = nullptr;
113 detail::executor_vtable const* vt_ = nullptr;
114
115 public:
116 /** Construct a default instance.
117
118 Constructs an empty `executor_ref`. Calling any executor
119 operations on a default-constructed instance results in
120 undefined behavior.
121 */
122 4176x executor_ref() = default;
123
124 /** Construct a copy.
125
126 Copies the internal pointers, preserving identity.
127 This enables the same-executor optimization when passing
128 executor_ref through coroutine chains.
129 */
130 executor_ref(executor_ref const&) = default;
131
132 /** Copy assignment operator. */
133 executor_ref& operator=(executor_ref const&) = default;
134
135 /** Constructs from any executor type.
136
137 Captures a reference to the given executor and stores a pointer
138 to the type-specific vtable. The executor must remain valid for
139 the lifetime of this `executor_ref` instance.
140
141 @param ex The executor to wrap. Must satisfy the `Executor`
142 concept. A pointer to this object is stored
143 internally; the executor must outlive this wrapper.
144 */
145 #if defined(__GNUC__) && !defined(__clang__)
146 // GCC constraint satisfaction caching bug workaround
147 template<class Ex,
148 std::enable_if_t<!std::is_same_v<
149 std::decay_t<Ex>, executor_ref>, int> = 0>
150 #else
151 template<class Ex>
152 requires (!std::same_as<std::decay_t<Ex>, executor_ref>)
153 #endif
154 3689x executor_ref(Ex const& ex) noexcept
155 3689x : ex_(&ex)
156 3689x , vt_(&detail::vtable_for<Ex>)
157 {
158 3689x }
159
160 /** Returns true if this instance holds a valid executor.
161
162 @return `true` if constructed with an executor, `false` if
163 default-constructed.
164 */
165 6x explicit operator bool() const noexcept
166 {
167 6x return ex_ != nullptr;
168 }
169
170 /** Returns a reference to the associated execution context.
171
172 @return A reference to the execution context.
173
174 @pre This instance was constructed with a valid executor.
175 */
176 20x execution_context& context() const noexcept
177 {
178 20x return vt_->context(ex_);
179 }
180
181 /** Informs the executor that work is beginning.
182
183 Must be paired with a subsequent call to `on_work_finished()`.
184
185 @pre This instance was constructed with a valid executor.
186 */
187 void on_work_started() const noexcept
188 {
189 vt_->on_work_started(ex_);
190 }
191
192 /** Informs the executor that work has completed.
193
194 @pre A preceding call to `on_work_started()` was made.
195 @pre This instance was constructed with a valid executor.
196 */
197 void on_work_finished() const noexcept
198 {
199 vt_->on_work_finished(ex_);
200 }
201
202 /** Dispatches a continuation through the wrapped executor.
203
204 Returns a handle for symmetric transfer. If running in the
205 executor's thread, returns `c.h`. Otherwise, posts the
206 continuation for later execution and returns
207 `std::noop_coroutine()`.
208
209 @param c The continuation to dispatch for resumption.
210 Must remain at a stable address until dequeued.
211
212 @return A handle for symmetric transfer or `std::noop_coroutine()`.
213
214 @pre This instance was constructed with a valid executor.
215 */
216 117x std::coroutine_handle<> dispatch(continuation& c) const
217 {
218 117x return vt_->dispatch(ex_, c);
219 }
220
221 /** Posts a continuation to the wrapped executor.
222
223 Posts the continuation to the executor for later execution
224 and returns. The caller should transfer to `std::noop_coroutine()`
225 after calling this.
226
227 @param c The continuation to post for resumption.
228 Must remain at a stable address until dequeued.
229
230 @pre This instance was constructed with a valid executor.
231 */
232 596x void post(continuation& c) const
233 {
234 596x vt_->post(ex_, c);
235 596x }
236
237 /** Compares two executor references for equality.
238
239 Two `executor_ref` instances are equal if they wrap
240 executors of the same type that compare equal.
241
242 @param other The executor reference to compare against.
243
244 @return `true` if both wrap equal executors of the same type.
245 */
246 6x bool operator==(executor_ref const& other) const noexcept
247 {
248 6x if (ex_ == other.ex_)
249 5x return true;
250 1x if (vt_ != other.vt_)
251 return false;
252 1x return vt_->equals(ex_, other.ex_);
253 }
254
255 /** Return a pointer to the wrapped executor if it matches
256 the requested type.
257
258 Performs a type check against the stored executor and
259 returns a typed pointer when the types match, or
260 `nullptr` otherwise. Analogous to
261 `std::any_cast< Executor >( &a )`.
262
263 @tparam Executor The executor type to retrieve.
264
265 @return A pointer to the underlying executor, or
266 `nullptr` if the type does not match.
267 */
268 template< typename Executor >
269 1x const Executor* target() const
270 {
271 1x if ( *vt_->type_id == detail::type_id< Executor >() )
272 1x return static_cast< Executor const* >( ex_ );
273 return nullptr;
274 }
275
276 /// @copydoc target() const
277 template< typename Executor>
278 2x Executor* target()
279 {
280 2x if ( *vt_->type_id == detail::type_id< Executor >() )
281 return const_cast< Executor* >(
282 1x static_cast< Executor const* >( ex_ ));
283 1x return nullptr;
284 }
285 };
286
287 } // capy
288 } // boost
289
290 #endif
291