include/boost/capy/ex/any_executor.hpp

100.0% Lines (54/0/54) 79.1% List of functions (34/0/43)
any_executor.hpp
f(x) Functions (43)
Function Calls Lines Blocks
boost::capy::any_executor::impl_base::~impl_base() :94 20x 100.0% 100.0% boost::capy::any_executor::impl<boost::capy::(anonymous namespace)::counting_executor>::impl<boost::capy::(anonymous namespace)::counting_executor&>(boost::capy::(anonymous namespace)::counting_executor&) :110 1x 100.0% 100.0% boost::capy::any_executor::impl<boost::capy::(anonymous namespace)::counting_executor>::impl<boost::capy::(anonymous namespace)::counting_executor>(boost::capy::(anonymous namespace)::counting_executor&&) :110 1x 100.0% 100.0% boost::capy::any_executor::impl<boost::capy::test::blocking_executor>::impl<boost::capy::test::blocking_executor>(boost::capy::test::blocking_executor&&) :110 2x 100.0% 100.0% boost::capy::any_executor::impl<boost::capy::thread_pool::executor_type>::impl<boost::capy::thread_pool::executor_type&>(boost::capy::thread_pool::executor_type&) :110 11x 100.0% 100.0% boost::capy::any_executor::impl<boost::capy::thread_pool::executor_type>::impl<boost::capy::thread_pool::executor_type>(boost::capy::thread_pool::executor_type&&) :110 5x 100.0% 100.0% boost::capy::any_executor::impl<boost::capy::(anonymous namespace)::counting_executor>::context() const :115 0 0.0% 0.0% boost::capy::any_executor::impl<boost::capy::test::blocking_executor>::context() const :115 1x 100.0% 100.0% boost::capy::any_executor::impl<boost::capy::thread_pool::executor_type>::context() const :115 5x 100.0% 100.0% boost::capy::any_executor::impl<boost::capy::(anonymous namespace)::counting_executor>::on_work_started() const :120 1x 100.0% 100.0% boost::capy::any_executor::impl<boost::capy::test::blocking_executor>::on_work_started() const :120 4x 100.0% 100.0% boost::capy::any_executor::impl<boost::capy::thread_pool::executor_type>::on_work_started() const :120 0 0.0% 0.0% boost::capy::any_executor::impl<boost::capy::(anonymous namespace)::counting_executor>::on_work_finished() const :125 1x 100.0% 100.0% boost::capy::any_executor::impl<boost::capy::test::blocking_executor>::on_work_finished() const :125 4x 100.0% 100.0% boost::capy::any_executor::impl<boost::capy::thread_pool::executor_type>::on_work_finished() const :125 0 0.0% 0.0% boost::capy::any_executor::impl<boost::capy::(anonymous namespace)::counting_executor>::dispatch(boost::capy::continuation&) const :130 0 0.0% 0.0% boost::capy::any_executor::impl<boost::capy::test::blocking_executor>::dispatch(boost::capy::continuation&) const :130 4x 100.0% 100.0% boost::capy::any_executor::impl<boost::capy::thread_pool::executor_type>::dispatch(boost::capy::continuation&) const :130 1x 100.0% 100.0% boost::capy::any_executor::impl<boost::capy::(anonymous namespace)::counting_executor>::post(boost::capy::continuation&) const :135 0 0.0% 0.0% boost::capy::any_executor::impl<boost::capy::test::blocking_executor>::post(boost::capy::continuation&) const :135 0 0.0% 0.0% boost::capy::any_executor::impl<boost::capy::thread_pool::executor_type>::post(boost::capy::continuation&) const :135 16x 100.0% 100.0% boost::capy::any_executor::impl<boost::capy::(anonymous namespace)::counting_executor>::equals(boost::capy::any_executor::impl_base const*) const :140 0 0.0% 0.0% boost::capy::any_executor::impl<boost::capy::test::blocking_executor>::equals(boost::capy::any_executor::impl_base const*) const :140 0 0.0% 0.0% boost::capy::any_executor::impl<boost::capy::thread_pool::executor_type>::equals(boost::capy::any_executor::impl_base const*) const :140 9x 100.0% 100.0% boost::capy::any_executor::impl<boost::capy::(anonymous namespace)::counting_executor>::target_type() const :147 1x 100.0% 100.0% boost::capy::any_executor::impl<boost::capy::test::blocking_executor>::target_type() const :147 0 0.0% 0.0% boost::capy::any_executor::impl<boost::capy::thread_pool::executor_type>::target_type() const :147 18x 100.0% 100.0% boost::capy::any_executor::any_executor() :164 2x 100.0% 100.0% boost::capy::any_executor::any_executor(boost::capy::any_executor const&) :176 33x 100.0% 100.0% boost::capy::any_executor::operator=(boost::capy::any_executor const&) :189 6x 100.0% 100.0% boost::capy::any_executor::any_executor<boost::capy::(anonymous namespace)::counting_executor&>(boost::capy::(anonymous namespace)::counting_executor&) :207 1x 100.0% 100.0% boost::capy::any_executor::any_executor<boost::capy::(anonymous namespace)::counting_executor>(boost::capy::(anonymous namespace)::counting_executor&&) :207 1x 100.0% 100.0% boost::capy::any_executor::any_executor<boost::capy::test::blocking_executor>(boost::capy::test::blocking_executor&&) :207 2x 100.0% 100.0% boost::capy::any_executor::any_executor<boost::capy::thread_pool::executor_type&>(boost::capy::thread_pool::executor_type&) :207 11x 100.0% 100.0% boost::capy::any_executor::any_executor<boost::capy::thread_pool::executor_type>(boost::capy::thread_pool::executor_type&&) :207 5x 100.0% 100.0% boost::capy::any_executor::operator bool() const :217 6x 100.0% 100.0% boost::capy::any_executor::context() const :228 6x 100.0% 100.0% boost::capy::any_executor::on_work_started() const :239 5x 100.0% 100.0% boost::capy::any_executor::on_work_finished() const :249 5x 100.0% 100.0% boost::capy::any_executor::dispatch(boost::capy::continuation&) const :268 5x 100.0% 100.0% boost::capy::any_executor::post(boost::capy::continuation&) const :284 16x 100.0% 100.0% boost::capy::any_executor::operator==(boost::capy::any_executor const&) const :300 11x 100.0% 100.0% boost::capy::any_executor::target_type() const :314 2x 100.0% 100.0%
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3 // Copyright (c) 2026 Michael Vandeberg
4 //
5 // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 //
8 // Official repository: https://github.com/cppalliance/capy
9 //
10
11 #ifndef BOOST_CAPY_ANY_EXECUTOR_HPP
12 #define BOOST_CAPY_ANY_EXECUTOR_HPP
13
14 #include <boost/capy/detail/config.hpp>
15 #include <boost/capy/continuation.hpp>
16 #include <concepts>
17 #include <coroutine>
18 #include <memory>
19 #include <type_traits>
20 #include <typeinfo>
21
22 namespace boost {
23 namespace capy {
24
25 class execution_context;
26 template<typename> class strand;
27
28 namespace detail {
29
30 template<typename T>
31 struct is_strand_type : std::false_type {};
32
33 template<typename E>
34 struct is_strand_type<strand<E>> : std::true_type {};
35
36 } // detail
37
38 /** Forwards `dispatch`/`post`/`context` calls through a shared, type-erased executor pointer.
39
40 This class provides type erasure for any executor type, enabling
41 runtime polymorphism with automatic memory management via shared
42 ownership. It stores a shared pointer to a polymorphic wrapper,
43 allowing executors of different types to be stored uniformly
44 while satisfying the full `Executor` concept.
45
46 @par Value Semantics
47
48 This class has value semantics with shared ownership. Copy and
49 move operations are cheap, copying the internal shared
50 pointer. Multiple `any_executor` instances may share the same
51 underlying executor. Move operations do not invalidate the
52 source; there is no moved-from state.
53
54 @par Default State
55
56 A default-constructed `any_executor` holds no executor.
57 `operator bool()`, `operator==`, and `target_type()` report the
58 empty state. `context()`, `on_work_started()`, `on_work_finished()`,
59 `dispatch()`, and `post()` are undefined behavior until an
60 executor is assigned.
61
62 @par Thread Safety
63
64 The `any_executor` itself is thread-safe for concurrent reads.
65 Concurrent modification requires external synchronization.
66 Executor operations are safe to call concurrently if the
67 underlying executor supports it.
68
69 @par Executor Concept
70
71 This class satisfies the `Executor` concept, making it usable
72 anywhere a concrete executor is expected.
73
74 @par Example
75 @code
76 any_executor exec = ctx.get_executor();
77 if(exec)
78 {
79 auto& context = exec.context();
80 exec.post(my_coroutine);
81 }
82 @endcode
83
84 @see executor_ref, Executor
85 */
86 class any_executor
87 {
88 struct impl_base;
89
90 std::shared_ptr<impl_base> p_;
91
92 struct impl_base
93 {
94 20x virtual ~impl_base() = default;
95 virtual execution_context& context() const noexcept = 0;
96 virtual void on_work_started() const noexcept = 0;
97 virtual void on_work_finished() const noexcept = 0;
98 virtual std::coroutine_handle<> dispatch(continuation&) const = 0;
99 virtual void post(continuation&) const = 0;
100 virtual bool equals(impl_base const*) const noexcept = 0;
101 virtual std::type_info const& target_type() const noexcept = 0;
102 };
103
104 template<class Ex>
105 struct impl final : impl_base
106 {
107 Ex ex_;
108
109 template<class Ex1>
110 20x explicit impl(Ex1&& ex)
111 20x : ex_(std::forward<Ex1>(ex))
112 {
113 20x }
114
115 6x execution_context& context() const noexcept override
116 {
117 6x return const_cast<Ex&>(ex_).context();
118 }
119
120 5x void on_work_started() const noexcept override
121 {
122 5x ex_.on_work_started();
123 5x }
124
125 5x void on_work_finished() const noexcept override
126 {
127 5x ex_.on_work_finished();
128 5x }
129
130 5x std::coroutine_handle<> dispatch(continuation& c) const override
131 {
132 5x return ex_.dispatch(c);
133 }
134
135 16x void post(continuation& c) const override
136 {
137 16x ex_.post(c);
138 16x }
139
140 9x bool equals(impl_base const* other) const noexcept override
141 {
142 9x if(target_type() != other->target_type())
143 1x return false;
144 8x return ex_ == static_cast<impl const*>(other)->ex_;
145 }
146
147 19x std::type_info const& target_type() const noexcept override
148 {
149 19x return typeid(Ex);
150 }
151 };
152
153 public:
154 /** Construct a default instance.
155
156 Constructs an empty `any_executor`. `operator bool()` reports
157 the empty state; `context()`, `on_work_started()`,
158 `on_work_finished()`, `dispatch()`, and `post()` are undefined
159 behavior until an executor is assigned.
160
161 @par Postconditions
162 @li `!*this`
163 */
164 2x any_executor() = default;
165
166 /** Construct a copy.
167
168 Creates a new `any_executor` sharing ownership of the
169 underlying executor with `other`.
170
171 @param other The executor to copy.
172
173 @par Postconditions
174 @li `*this == other`
175 */
176 33x any_executor(any_executor const& other) = default;
177
178 /** Copy assignment operator.
179
180 Shares ownership of the underlying executor with `other`.
181
182 @param other The executor to copy.
183
184 @return A reference to `*this`.
185
186 @par Postconditions
187 @li `*this == other`
188 */
189 6x any_executor& operator=(any_executor const& other) = default;
190
191 /** Constructs from any executor type.
192
193 Allocates storage for a copy of the given executor and
194 stores it internally. The executor must satisfy the
195 `Executor` concept.
196
197 @param ex The executor to wrap. A copy is stored internally.
198
199 @par Postconditions
200 @li `*this` is valid
201 */
202 template<class Ex>
203 requires (
204 !std::same_as<std::decay_t<Ex>, any_executor> &&
205 !detail::is_strand_type<std::decay_t<Ex>>::value &&
206 std::copy_constructible<std::decay_t<Ex>>)
207 20x any_executor(Ex&& ex)
208 20x : p_(std::make_shared<impl<std::decay_t<Ex>>>(std::forward<Ex>(ex)))
209 {
210 20x }
211
212 /** Returns true if this instance holds a valid executor.
213
214 @return `true` if constructed with an executor, `false` if
215 default-constructed.
216 */
217 6x explicit operator bool() const noexcept
218 {
219 6x return p_ != nullptr;
220 }
221
222 /** Returns a reference to the associated execution context.
223
224 @return A reference to the execution context.
225
226 @pre This instance holds a valid executor.
227 */
228 6x execution_context& context() const noexcept
229 {
230 6x return p_->context();
231 }
232
233 /** Informs the executor that work is beginning.
234
235 Must be paired with a subsequent call to `on_work_finished()`.
236
237 @pre This instance holds a valid executor.
238 */
239 5x void on_work_started() const noexcept
240 {
241 5x p_->on_work_started();
242 5x }
243
244 /** Informs the executor that work has completed.
245
246 @pre A preceding call to `on_work_started()` was made.
247 @pre This instance holds a valid executor.
248 */
249 5x void on_work_finished() const noexcept
250 {
251 5x p_->on_work_finished();
252 5x }
253
254 /** Dispatches a continuation through the wrapped executor.
255
256 Returns a handle for symmetric transfer. If running in the
257 executor's thread, returns `c.h`. Otherwise, posts the
258 continuation for later execution and returns
259 `std::noop_coroutine()`.
260
261 @param c The continuation to dispatch for resumption.
262 Must remain at a stable address until dequeued.
263
264 @return A handle for symmetric transfer or `std::noop_coroutine()`.
265
266 @pre This instance holds a valid executor.
267 */
268 5x std::coroutine_handle<> dispatch(continuation& c) const
269 {
270 5x return p_->dispatch(c);
271 }
272
273 /** Posts a continuation to the wrapped executor.
274
275 Posts the continuation to the executor for later execution
276 and returns. The caller should transfer to `std::noop_coroutine()`
277 after calling this.
278
279 @param c The continuation to post for resumption.
280 Must remain at a stable address until dequeued.
281
282 @pre This instance holds a valid executor.
283 */
284 16x void post(continuation& c) const
285 {
286 16x p_->post(c);
287 16x }
288
289 /** Compares two executor wrappers for equality.
290
291 Two `any_executor` instances are equal if they both hold
292 executors of the same type that compare equal, or if both
293 are empty.
294
295 @param other The executor to compare against.
296
297 @return `true` if both wrap equal executors of the same type,
298 or both are empty.
299 */
300 11x bool operator==(any_executor const& other) const noexcept
301 {
302 11x if(!p_ && !other.p_)
303 1x return true;
304 10x if(!p_ || !other.p_)
305 1x return false;
306 9x return p_->equals(other.p_.get());
307 }
308
309 /** Returns the type_info of the wrapped executor.
310
311 @return The `std::type_info` of the stored executor type,
312 or `typeid(void)` if empty.
313 */
314 2x std::type_info const& target_type() const noexcept
315 {
316 2x if(!p_)
317 1x return typeid(void);
318 1x return p_->target_type();
319 }
320 };
321
322 } // capy
323 } // boost
324
325 #endif
326