include/boost/capy/ex/any_executor.hpp

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