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() :88 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&) :104 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&&) :104 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&&) :104 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&) :104 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&&) :104 5x 100.0% 100.0% boost::capy::any_executor::impl<boost::capy::(anonymous namespace)::counting_executor>::context() const :109 0 0.0% 0.0% boost::capy::any_executor::impl<boost::capy::test::blocking_executor>::context() const :109 1x 100.0% 100.0% boost::capy::any_executor::impl<boost::capy::thread_pool::executor_type>::context() const :109 5x 100.0% 100.0% boost::capy::any_executor::impl<boost::capy::(anonymous namespace)::counting_executor>::on_work_started() const :114 1x 100.0% 100.0% boost::capy::any_executor::impl<boost::capy::test::blocking_executor>::on_work_started() const :114 4x 100.0% 100.0% boost::capy::any_executor::impl<boost::capy::thread_pool::executor_type>::on_work_started() const :114 0 0.0% 0.0% boost::capy::any_executor::impl<boost::capy::(anonymous namespace)::counting_executor>::on_work_finished() const :119 1x 100.0% 100.0% boost::capy::any_executor::impl<boost::capy::test::blocking_executor>::on_work_finished() const :119 4x 100.0% 100.0% boost::capy::any_executor::impl<boost::capy::thread_pool::executor_type>::on_work_finished() const :119 0 0.0% 0.0% boost::capy::any_executor::impl<boost::capy::(anonymous namespace)::counting_executor>::dispatch(boost::capy::continuation&) const :124 0 0.0% 0.0% boost::capy::any_executor::impl<boost::capy::test::blocking_executor>::dispatch(boost::capy::continuation&) const :124 4x 100.0% 100.0% boost::capy::any_executor::impl<boost::capy::thread_pool::executor_type>::dispatch(boost::capy::continuation&) const :124 1x 100.0% 100.0% boost::capy::any_executor::impl<boost::capy::(anonymous namespace)::counting_executor>::post(boost::capy::continuation&) const :129 0 0.0% 0.0% boost::capy::any_executor::impl<boost::capy::test::blocking_executor>::post(boost::capy::continuation&) const :129 0 0.0% 0.0% boost::capy::any_executor::impl<boost::capy::thread_pool::executor_type>::post(boost::capy::continuation&) const :129 15x 100.0% 100.0% boost::capy::any_executor::impl<boost::capy::(anonymous namespace)::counting_executor>::equals(boost::capy::any_executor::impl_base const*) const :134 0 0.0% 0.0% boost::capy::any_executor::impl<boost::capy::test::blocking_executor>::equals(boost::capy::any_executor::impl_base const*) const :134 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 :134 9x 100.0% 100.0% boost::capy::any_executor::impl<boost::capy::(anonymous namespace)::counting_executor>::target_type() const :141 1x 100.0% 100.0% boost::capy::any_executor::impl<boost::capy::test::blocking_executor>::target_type() const :141 0 0.0% 0.0% boost::capy::any_executor::impl<boost::capy::thread_pool::executor_type>::target_type() const :141 18x 100.0% 100.0% boost::capy::any_executor::any_executor() :158 2x 100.0% 100.0% boost::capy::any_executor::any_executor(boost::capy::any_executor const&) :170 33x 100.0% 100.0% boost::capy::any_executor::operator=(boost::capy::any_executor const&) :183 6x 100.0% 100.0% boost::capy::any_executor::any_executor<boost::capy::(anonymous namespace)::counting_executor&>(boost::capy::(anonymous namespace)::counting_executor&) :201 1x 100.0% 100.0% boost::capy::any_executor::any_executor<boost::capy::(anonymous namespace)::counting_executor>(boost::capy::(anonymous namespace)::counting_executor&&) :201 1x 100.0% 100.0% boost::capy::any_executor::any_executor<boost::capy::test::blocking_executor>(boost::capy::test::blocking_executor&&) :201 2x 100.0% 100.0% boost::capy::any_executor::any_executor<boost::capy::thread_pool::executor_type&>(boost::capy::thread_pool::executor_type&) :201 11x 100.0% 100.0% boost::capy::any_executor::any_executor<boost::capy::thread_pool::executor_type>(boost::capy::thread_pool::executor_type&&) :201 5x 100.0% 100.0% boost::capy::any_executor::operator bool() const :211 6x 100.0% 100.0% boost::capy::any_executor::context() const :222 6x 100.0% 100.0% boost::capy::any_executor::on_work_started() const :233 5x 100.0% 100.0% boost::capy::any_executor::on_work_finished() const :243 5x 100.0% 100.0% boost::capy::any_executor::dispatch(boost::capy::continuation&) const :262 5x 100.0% 100.0% boost::capy::any_executor::post(boost::capy::continuation&) const :278 15x 100.0% 100.0% boost::capy::any_executor::operator==(boost::capy::any_executor const&) const :294 11x 100.0% 100.0% boost::capy::any_executor::target_type() const :308 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 @par !example example
76
77
78 @see executor_ref, Executor
79 */
80 class any_executor
81 {
82 struct impl_base;
83
84 std::shared_ptr<impl_base> p_;
85
86 struct impl_base
87 {
88 20x virtual ~impl_base() = default;
89 virtual execution_context& context() const noexcept = 0;
90 virtual void on_work_started() const noexcept = 0;
91 virtual void on_work_finished() const noexcept = 0;
92 virtual std::coroutine_handle<> dispatch(continuation&) const = 0;
93 virtual void post(continuation&) const = 0;
94 virtual bool equals(impl_base const*) const noexcept = 0;
95 virtual std::type_info const& target_type() const noexcept = 0;
96 };
97
98 template<class Ex>
99 struct impl final : impl_base
100 {
101 Ex ex_;
102
103 template<class Ex1>
104 20x explicit impl(Ex1&& ex)
105 20x : ex_(std::forward<Ex1>(ex))
106 {
107 20x }
108
109 6x execution_context& context() const noexcept override
110 {
111 6x return const_cast<Ex&>(ex_).context();
112 }
113
114 5x void on_work_started() const noexcept override
115 {
116 5x ex_.on_work_started();
117 5x }
118
119 5x void on_work_finished() const noexcept override
120 {
121 5x ex_.on_work_finished();
122 5x }
123
124 5x std::coroutine_handle<> dispatch(continuation& c) const override
125 {
126 5x return ex_.dispatch(c);
127 }
128
129 15x void post(continuation& c) const override
130 {
131 15x ex_.post(c);
132 15x }
133
134 9x bool equals(impl_base const* other) const noexcept override
135 {
136 9x if(target_type() != other->target_type())
137 1x return false;
138 8x return ex_ == static_cast<impl const*>(other)->ex_;
139 }
140
141 19x std::type_info const& target_type() const noexcept override
142 {
143 19x return typeid(Ex);
144 }
145 };
146
147 public:
148 /** Construct a default instance.
149
150 Constructs an empty `any_executor`. `operator bool()` reports
151 the empty state; `context()`, `on_work_started()`,
152 `on_work_finished()`, `dispatch()`, and `post()` are undefined
153 behavior until an executor is assigned.
154
155 @par Postconditions
156 @li `!*this`
157 */
158 2x any_executor() = default;
159
160 /** Construct a copy.
161
162 Creates a new `any_executor` sharing ownership of the
163 underlying executor with `other`.
164
165 @param other The executor to copy.
166
167 @par Postconditions
168 @li `*this == other`
169 */
170 33x any_executor(any_executor const& other) = default;
171
172 /** Copy assignment operator.
173
174 Shares ownership of the underlying executor with `other`.
175
176 @param other The executor to copy.
177
178 @return A reference to `*this`.
179
180 @par Postconditions
181 @li `*this == other`
182 */
183 6x any_executor& operator=(any_executor const& other) = default;
184
185 /** Constructs from any executor type.
186
187 Allocates storage for a copy of the given executor and
188 stores it internally. The executor must satisfy the
189 `Executor` concept.
190
191 @param ex The executor to wrap. A copy is stored internally.
192
193 @par Postconditions
194 @li `*this` is valid
195 */
196 template<class Ex>
197 requires (
198 !std::same_as<std::decay_t<Ex>, any_executor> &&
199 !detail::is_strand_type<std::decay_t<Ex>>::value &&
200 std::copy_constructible<std::decay_t<Ex>>)
201 20x any_executor(Ex&& ex)
202 20x : p_(std::make_shared<impl<std::decay_t<Ex>>>(std::forward<Ex>(ex)))
203 {
204 20x }
205
206 /** Returns true if this instance holds a valid executor.
207
208 @return `true` if constructed with an executor, `false` if
209 default-constructed.
210 */
211 6x explicit operator bool() const noexcept
212 {
213 6x return p_ != nullptr;
214 }
215
216 /** Returns a reference to the associated execution context.
217
218 @return A reference to the execution context.
219
220 @pre This instance holds a valid executor.
221 */
222 6x execution_context& context() const noexcept
223 {
224 6x return p_->context();
225 }
226
227 /** Informs the executor that work is beginning.
228
229 Must be paired with a subsequent call to `on_work_finished()`.
230
231 @pre This instance holds a valid executor.
232 */
233 5x void on_work_started() const noexcept
234 {
235 5x p_->on_work_started();
236 5x }
237
238 /** Informs the executor that work has completed.
239
240 @pre A preceding call to `on_work_started()` was made.
241 @pre This instance holds a valid executor.
242 */
243 5x void on_work_finished() const noexcept
244 {
245 5x p_->on_work_finished();
246 5x }
247
248 /** Dispatches a continuation through the wrapped executor.
249
250 Returns a handle for symmetric transfer. If running in the
251 executor's thread, returns `c.h`. Otherwise, posts the
252 continuation for later execution and returns
253 `std::noop_coroutine()`.
254
255 @param c The continuation to dispatch for resumption.
256 Must remain at a stable address until dequeued.
257
258 @return A handle for symmetric transfer or `std::noop_coroutine()`.
259
260 @pre This instance holds a valid executor.
261 */
262 5x std::coroutine_handle<> dispatch(continuation& c) const
263 {
264 5x return p_->dispatch(c);
265 }
266
267 /** Posts a continuation to the wrapped executor.
268
269 Posts the continuation to the executor for later execution
270 and returns. The caller should transfer to `std::noop_coroutine()`
271 after calling this.
272
273 @param c The continuation to post for resumption.
274 Must remain at a stable address until dequeued.
275
276 @pre This instance holds a valid executor.
277 */
278 15x void post(continuation& c) const
279 {
280 15x p_->post(c);
281 15x }
282
283 /** Compares two executor wrappers for equality.
284
285 Two `any_executor` instances are equal if they both hold
286 executors of the same type that compare equal, or if both
287 are empty.
288
289 @param other The executor to compare against.
290
291 @return `true` if both wrap equal executors of the same type,
292 or both are empty.
293 */
294 11x bool operator==(any_executor const& other) const noexcept
295 {
296 11x if(!p_ && !other.p_)
297 1x return true;
298 10x if(!p_ || !other.p_)
299 1x return false;
300 9x return p_->equals(other.p_.get());
301 }
302
303 /** Returns the type_info of the wrapped executor.
304
305 @return The `std::type_info` of the stored executor type,
306 or `typeid(void)` if empty.
307 */
308 2x std::type_info const& target_type() const noexcept
309 {
310 2x if(!p_)
311 1x return typeid(void);
312 1x return p_->target_type();
313 }
314 };
315
316 } // capy
317 } // boost
318
319 #endif
320