include/boost/capy/ex/execution_context.hpp

97.8% Lines (131/134) 100.0% List of functions (45/45) 68.9% Branches (31/45)
f(x) Functions (45)
Function Calls Lines Branches Blocks
boost::capy::execution_context::service::~service() :143 0 100.0% boost::capy::execution_context::service::service() :146 0 100.0% bool boost::capy::execution_context::has_service<boost::capy::(anonymous namespace)::base_service>() const :209 0 100.0% bool boost::capy::execution_context::has_service<boost::capy::(anonymous namespace)::derived_service>() const :209 0 100.0% bool boost::capy::execution_context::has_service<boost::capy::(anonymous namespace)::multi_arg_service>() const :209 0 100.0% bool boost::capy::execution_context::has_service<boost::capy::(anonymous namespace)::nested_service>() const :209 0 100.0% bool boost::capy::execution_context::has_service<boost::capy::(anonymous namespace)::simple_service>() const :209 0 100.0% bool boost::capy::execution_context::has_service<boost::capy::(anonymous namespace)::test_service>() const :209 0 100.0% boost::capy::(anonymous namespace)::base_service* boost::capy::execution_context::find_service<boost::capy::(anonymous namespace)::base_service>() const :224 0 100.0% boost::capy::(anonymous namespace)::derived_service* boost::capy::execution_context::find_service<boost::capy::(anonymous namespace)::derived_service>() const :224 0 100.0% boost::capy::(anonymous namespace)::multi_arg_service* boost::capy::execution_context::find_service<boost::capy::(anonymous namespace)::multi_arg_service>() const :224 0 100.0% boost::capy::(anonymous namespace)::nested_service* boost::capy::execution_context::find_service<boost::capy::(anonymous namespace)::nested_service>() const :224 0 100.0% boost::capy::(anonymous namespace)::simple_service* boost::capy::execution_context::find_service<boost::capy::(anonymous namespace)::simple_service>() const :224 0 100.0% boost::capy::(anonymous namespace)::test_service* boost::capy::execution_context::find_service<boost::capy::(anonymous namespace)::test_service>() const :224 0 100.0% boost::capy::(anonymous namespace)::derived_service& boost::capy::execution_context::use_service<boost::capy::(anonymous namespace)::derived_service>() :252 0 100.0% 100.0% boost::capy::(anonymous namespace)::nested_service& boost::capy::execution_context::use_service<boost::capy::(anonymous namespace)::nested_service>() :252 0 100.0% 100.0% boost::capy::(anonymous namespace)::simple_service& boost::capy::execution_context::use_service<boost::capy::(anonymous namespace)::simple_service>() :252 0 100.0% 100.0% boost::capy::(anonymous namespace)::test_service& boost::capy::execution_context::use_service<boost::capy::(anonymous namespace)::test_service>() :252 0 100.0% boost::capy::detail::strand_service_impl& boost::capy::execution_context::use_service<boost::capy::detail::strand_service_impl>() :252 0 100.0% boost::capy::detail::timer_service& boost::capy::execution_context::use_service<boost::capy::detail::timer_service>() :252 0 100.0% boost::capy::execution_context::use_service<boost::capy::(anonymous namespace)::test_service>()::impl::impl() :261 0 100.0% boost::capy::execution_context::use_service<boost::capy::detail::strand_service_impl>()::impl::impl() :261 0 100.0% boost::capy::execution_context::use_service<boost::capy::detail::timer_service>()::impl::impl() :261 0 100.0% boost::capy::execution_context::use_service<boost::capy::(anonymous namespace)::test_service>()::impl::create(boost::capy::execution_context&) :270 0 100.0% 100.0% boost::capy::execution_context::use_service<boost::capy::detail::strand_service_impl>()::impl::create(boost::capy::execution_context&) :270 0 100.0% 50.0% boost::capy::execution_context::use_service<boost::capy::detail::timer_service>()::impl::create(boost::capy::execution_context&) :270 0 100.0% 50.0% boost::capy::(anonymous namespace)::derived_service& boost::capy::execution_context::make_service<boost::capy::(anonymous namespace)::derived_service, int>(int&&) :308 0 100.0% 100.0% boost::capy::(anonymous namespace)::multi_arg_service& boost::capy::execution_context::make_service<boost::capy::(anonymous namespace)::multi_arg_service, int, char const (&) [5], double>(int&&, char const (&) [5], double&&) :308 0 100.0% 100.0% boost::capy::(anonymous namespace)::multi_arg_service& boost::capy::execution_context::make_service<boost::capy::(anonymous namespace)::multi_arg_service, int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, double>(int&&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&&, double&&) :308 0 100.0% 100.0% boost::capy::(anonymous namespace)::other_derived_service& boost::capy::execution_context::make_service<boost::capy::(anonymous namespace)::other_derived_service>() :308 0 100.0% 0.0% boost::capy::(anonymous namespace)::simple_service& boost::capy::execution_context::make_service<boost::capy::(anonymous namespace)::simple_service, int>(int&&) :308 0 100.0% 100.0% boost::capy::(anonymous namespace)::test_service& boost::capy::execution_context::make_service<boost::capy::(anonymous namespace)::test_service, int>(int&&) :308 0 100.0% boost::capy::(anonymous namespace)::tracking_service& boost::capy::execution_context::make_service<boost::capy::(anonymous namespace)::tracking_service, bool&>(bool&) :308 0 100.0% 100.0% boost::capy::execution_context::make_service<boost::capy::(anonymous namespace)::test_service, int>(int&&)::impl::impl(int&&) :323 0 100.0% boost::capy::execution_context::make_service<boost::capy::(anonymous namespace)::test_service, int>(int&&)::impl::create(boost::capy::execution_context&) :333 0 100.0% auto boost::capy::execution_context::make_service<boost::capy::(anonymous namespace)::test_service, int>(int&&)::impl::create(boost::capy::execution_context&)::{lambda((auto:1&&)...)#1}::operator()<int>(int&&) const :335 0 100.0% 100.0% boost::capy::execution_context::get_frame_allocator() const :358 0 100.0% boost::capy::execution_context::set_frame_allocator(std::pmr::memory_resource*) :378 0 100.0% void boost::capy::execution_context::set_frame_allocator<std::allocator<int> >(std::allocator<int> const&) :405 0 100.0% 100.0% void boost::capy::execution_context::set_frame_allocator<std::allocator<void> >(std::allocator<void> const&) :405 0 100.0% 100.0% boost::capy::test_io_context const* boost::capy::execution_context::target<boost::capy::test_io_context>() const :434 0 75.0% 50.0% boost::capy::execution_context_test::testTarget()::other_context* boost::capy::execution_context::target<boost::capy::execution_context_test::testTarget()::other_context>() :443 0 75.0% 50.0% boost::capy::test_io_context* boost::capy::execution_context::target<boost::capy::test_io_context>() :443 0 75.0% 50.0% boost::capy::execution_context::factory::factory(std::type_info const&, std::type_info const&) :509 0 100.0% boost::capy::execution_context::execution_context<boost::capy::test_io_context>(boost::capy::test_io_context*) :538 0 100.0%
Line Branch 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_EXECUTION_CONTEXT_HPP
11 #define BOOST_CAPY_EXECUTION_CONTEXT_HPP
12
13 #include <boost/capy/detail/config.hpp>
14 #include <boost/capy/detail/frame_memory_resource.hpp>
15 #include <boost/capy/detail/type_id.hpp>
16 #include <boost/capy/concept/executor.hpp>
17 #include <concepts>
18 #include <memory>
19 #include <memory_resource>
20 #include <mutex>
21 #include <tuple>
22 #include <type_traits>
23 #include <utility>
24
25 namespace boost {
26 namespace capy {
27
28 /** Base class for I/O object containers providing service management.
29
30 An execution context represents a place where function objects are
31 executed. It provides a service registry where polymorphic services
32 can be stored and retrieved by type. Each service type may be stored
33 at most once. Services may specify a nested `key_type` to enable
34 lookup by a base class type.
35
36 Derived classes such as `io_context` extend this to provide
37 execution facilities like event loops and thread pools. Derived
38 class destructors must call `shutdown()` and `destroy()` to ensure
39 proper service cleanup before member destruction.
40
41 @par Service Lifecycle
42 Services are created on first use via `use_service()` or explicitly
43 via `make_service()`. During destruction, `shutdown()` is called on
44 each service in reverse order of creation, then `destroy()` deletes
45 them. Both functions are idempotent.
46
47 @par Thread Safety
48 Service registration and lookup functions are thread-safe.
49 The `shutdown()` and `destroy()` functions are not thread-safe
50 and must only be called during destruction.
51
52 @par Example
53 @code
54 struct file_service : execution_context::service
55 {
56 protected:
57 void shutdown() override {}
58 };
59
60 struct posix_file_service : file_service
61 {
62 using key_type = file_service;
63
64 explicit posix_file_service(execution_context&) {}
65 };
66
67 class io_context : public execution_context
68 {
69 public:
70 ~io_context()
71 {
72 shutdown();
73 destroy();
74 }
75 };
76
77 io_context ctx;
78 ctx.make_service<posix_file_service>();
79 ctx.find_service<file_service>(); // returns posix_file_service*
80 ctx.find_service<posix_file_service>(); // also works
81 @endcode
82
83 @see service, is_execution_context
84 */
85 class BOOST_CAPY_DECL
86 execution_context
87 {
88 detail::type_info const* ti_ = nullptr;
89
90 template<class T, class = void>
91 struct get_key : std::false_type
92 {};
93
94 template<class T>
95 struct get_key<T, std::void_t<typename T::key_type>> : std::true_type
96 {
97 using type = typename T::key_type;
98 };
99 protected:
100 template< typename Derived >
101 explicit execution_context( Derived* ) noexcept;
102
103 public:
104 //------------------------------------------------
105
106 /** Abstract base class for services owned by an execution context.
107
108 Services provide extensible functionality to an execution context.
109 Each service type can be registered at most once. Services are
110 created via `use_service()` or `make_service()` and are owned by
111 the execution context for their lifetime.
112
113 Derived classes must implement the pure virtual `shutdown()` member
114 function, which is called when the owning execution context is
115 being destroyed. The `shutdown()` function should release resources
116 and cancel outstanding operations without blocking.
117
118 @par Deriving from service
119 @li Implement `shutdown()` to perform cleanup.
120 @li Accept `execution_context&` as the first constructor parameter.
121 @li Optionally define `key_type` to enable base-class lookup.
122
123 @par Example
124 @code
125 struct my_service : execution_context::service
126 {
127 explicit my_service(execution_context&) {}
128
129 protected:
130 void shutdown() override
131 {
132 // Cancel pending operations, release resources
133 }
134 };
135 @endcode
136
137 @see execution_context
138 */
139 class BOOST_CAPY_DECL
140 service
141 {
142 public:
143 57x virtual ~service() = default;
144
145 protected:
146 57x service() = default;
147
148 /** Called when the owning execution context shuts down.
149
150 Implementations should release resources and cancel any
151 outstanding asynchronous operations. This function must
152 not block and must not throw exceptions. Services are
153 shut down in reverse order of creation.
154
155 @par Exception Safety
156 No-throw guarantee.
157 */
158 virtual void shutdown() = 0;
159
160 private:
161 friend class execution_context;
162
163 service* next_ = nullptr;
164
165 // warning C4251: 'std::type_index' needs to have dll-interface
166 BOOST_CAPY_MSVC_WARNING_PUSH
167 BOOST_CAPY_MSVC_WARNING_DISABLE(4251)
168 detail::type_index t0_{detail::type_id<void>()};
169 detail::type_index t1_{detail::type_id<void>()};
170 BOOST_CAPY_MSVC_WARNING_POP
171 };
172
173 //------------------------------------------------
174
175 execution_context(execution_context const&) = delete;
176
177 execution_context& operator=(execution_context const&) = delete;
178
179 /** Destructor.
180
181 Calls `shutdown()` then `destroy()` to clean up all services.
182
183 @par Effects
184 All services are shut down and deleted in reverse order
185 of creation.
186
187 @par Exception Safety
188 No-throw guarantee.
189 */
190 ~execution_context();
191
192 /** Construct a default instance.
193
194 @par Exception Safety
195 Strong guarantee.
196 */
197 execution_context();
198
199 /** Return true if a service of type T exists.
200
201 @par Thread Safety
202 Thread-safe.
203
204 @tparam T The type of service to check.
205
206 @return `true` if the service exists.
207 */
208 template<class T>
209 14x bool has_service() const noexcept
210 {
211 14x return find_service<T>() != nullptr;
212 }
213
214 /** Return a pointer to the service of type T, or nullptr.
215
216 @par Thread Safety
217 Thread-safe.
218
219 @tparam T The type of service to find.
220
221 @return A pointer to the service, or `nullptr` if not present.
222 */
223 template<class T>
224 23x T* find_service() const noexcept
225 {
226 23x std::lock_guard<std::mutex> lock(mutex_);
227 23x return static_cast<T*>(find_impl(detail::type_id<T>()));
228 23x }
229
230 /** Return a reference to the service of type T, creating it if needed.
231
232 If no service of type T exists, one is created by calling
233 `T(execution_context&)`. If T has a nested `key_type`, the
234 service is also indexed under that type.
235
236 @par Constraints
237 @li `T` must derive from `service`.
238 @li `T` must be constructible from `execution_context&`.
239
240 @par Exception Safety
241 Strong guarantee. If service creation throws, the container
242 is unchanged.
243
244 @par Thread Safety
245 Thread-safe.
246
247 @tparam T The type of service to retrieve or create.
248
249 @return A reference to the service.
250 */
251 template<class T>
252 74x T& use_service()
253 {
254 static_assert(std::is_base_of<service, T>::value,
255 "T must derive from service");
256 static_assert(std::is_constructible<T, execution_context&>::value,
257 "T must be constructible from execution_context&");
258
259 struct impl : factory
260 {
261 59x impl()
262 : factory(
263 detail::type_id<T>(),
264 get_key<T>::value
265 ? detail::type_id<typename get_key<T>::type>()
266 59x : detail::type_id<T>())
267 {
268 59x }
269
270 43x service* create(execution_context& ctx) override
271 {
272
2/6
boost::capy::execution_context::use_service<boost::capy::detail::strand_service_impl>()::impl::create(boost::capy::execution_context&):
✓ Branch 2 taken 22 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
boost::capy::execution_context::use_service<boost::capy::detail::timer_service>()::impl::create(boost::capy::execution_context&):
✓ Branch 2 taken 19 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
43x return new T(ctx);
273 }
274 };
275
276 74x impl f;
277
6/6
boost::capy::(anonymous namespace)::derived_service& boost::capy::execution_context::use_service<boost::capy::(anonymous namespace)::derived_service>():
✓ Branch 1 taken 1 time.
boost::capy::(anonymous namespace)::nested_service& boost::capy::execution_context::use_service<boost::capy::(anonymous namespace)::nested_service>():
✓ Branch 1 taken 1 time.
boost::capy::(anonymous namespace)::simple_service& boost::capy::execution_context::use_service<boost::capy::(anonymous namespace)::simple_service>():
✓ Branch 1 taken 13 times.
boost::capy::execution_context::use_service<boost::capy::(anonymous namespace)::test_service>()::impl::create(boost::capy::execution_context&):
✓ Branch 1 taken 4 times.
boost::capy::execution_context::use_service<boost::capy::detail::strand_service_impl>()::impl::create(boost::capy::execution_context&):
✓ Branch 1 taken 26 times.
boost::capy::execution_context::use_service<boost::capy::detail::timer_service>()::impl::create(boost::capy::execution_context&):
✓ Branch 1 taken 29 times.
148x return static_cast<T&>(use_service_impl(f));
278 }
279
280 /** Construct and add a service.
281
282 A new service of type T is constructed using the provided
283 arguments and added to the container. If T has a nested
284 `key_type`, the service is also indexed under that type.
285
286 @par Constraints
287 @li `T` must derive from `service`.
288 @li `T` must be constructible from `execution_context&, Args...`.
289 @li If `T::key_type` exists, `T&` must be convertible to `key_type&`.
290
291 @par Exception Safety
292 Strong guarantee. If service creation throws, the container
293 is unchanged.
294
295 @par Thread Safety
296 Thread-safe.
297
298 @throws std::invalid_argument if a service of the same type
299 or `key_type` already exists.
300
301 @tparam T The type of service to create.
302
303 @param args Arguments forwarded to the constructor of T.
304
305 @return A reference to the created service.
306 */
307 template<class T, class... Args>
308 10x T& make_service(Args&&... args)
309 {
310 static_assert(std::is_base_of<service, T>::value,
311 "T must derive from service");
312 if constexpr(get_key<T>::value)
313 {
314 static_assert(
315 std::is_convertible<T&, typename get_key<T>::type&>::value,
316 "T& must be convertible to key_type&");
317 }
318
319 struct impl : factory
320 {
321 std::tuple<Args&&...> args_;
322
323 2x explicit impl(Args&&... a)
324 : factory(
325 detail::type_id<T>(),
326 get_key<T>::value
327 ? detail::type_id<typename get_key<T>::type>()
328 : detail::type_id<T>())
329 2x , args_(std::forward<Args>(a)...)
330 {
331 2x }
332
333 1x service* create(execution_context& ctx) override
334 {
335
1/1
✓ Branch 1 taken 1 time.
2x return std::apply([&ctx](auto&&... a) {
336 1x return new T(ctx, std::forward<decltype(a)>(a)...);
337 3x }, std::move(args_));
338 }
339 };
340
341
5/5
auto boost::capy::execution_context::make_service<boost::capy::(anonymous namespace)::test_service, int>(int&&)::impl::create(boost::capy::execution_context&)::{lambda((auto:1&&)...)#1}::operator()<int>(int&&) const:
✓ Branch 2 taken 2 times.
boost::capy::(anonymous namespace)::multi_arg_service& boost::capy::execution_context::make_service<boost::capy::(anonymous namespace)::multi_arg_service, int, char const (&) [5], double>(int&&, char const (&) [5], double&&):
✓ Branch 4 taken 1 time.
boost::capy::(anonymous namespace)::multi_arg_service& boost::capy::execution_context::make_service<boost::capy::(anonymous namespace)::multi_arg_service, int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, double>(int&&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&&, double&&):
✓ Branch 4 taken 1 time.
boost::capy::(anonymous namespace)::simple_service& boost::capy::execution_context::make_service<boost::capy::(anonymous namespace)::simple_service, int>(int&&):
✓ Branch 2 taken 3 times.
boost::capy::(anonymous namespace)::tracking_service& boost::capy::execution_context::make_service<boost::capy::(anonymous namespace)::tracking_service, bool&>(bool&):
✓ Branch 2 taken 1 time.
10x impl f(std::forward<Args>(args)...);
342
6/7
auto boost::capy::execution_context::make_service<boost::capy::(anonymous namespace)::test_service, int>(int&&)::impl::create(boost::capy::execution_context&)::{lambda((auto:1&&)...)#1}::operator()<int>(int&&) const:
✓ Branch 1 taken 1 time.
boost::capy::(anonymous namespace)::derived_service& boost::capy::execution_context::make_service<boost::capy::(anonymous namespace)::derived_service, int>(int&&):
✓ Branch 1 taken 1 time.
boost::capy::(anonymous namespace)::multi_arg_service& boost::capy::execution_context::make_service<boost::capy::(anonymous namespace)::multi_arg_service, int, char const (&) [5], double>(int&&, char const (&) [5], double&&):
✓ Branch 1 taken 1 time.
boost::capy::(anonymous namespace)::multi_arg_service& boost::capy::execution_context::make_service<boost::capy::(anonymous namespace)::multi_arg_service, int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, double>(int&&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&&, double&&):
✓ Branch 1 taken 1 time.
boost::capy::(anonymous namespace)::other_derived_service& boost::capy::execution_context::make_service<boost::capy::(anonymous namespace)::other_derived_service>():
✗ Branch 1 not taken.
boost::capy::(anonymous namespace)::simple_service& boost::capy::execution_context::make_service<boost::capy::(anonymous namespace)::simple_service, int>(int&&):
✓ Branch 1 taken 2 times.
boost::capy::(anonymous namespace)::tracking_service& boost::capy::execution_context::make_service<boost::capy::(anonymous namespace)::tracking_service, bool&>(bool&):
✓ Branch 1 taken 1 time.
17x return static_cast<T&>(make_service_impl(f));
343 }
344
345 //------------------------------------------------
346
347 /** Return the memory resource used for coroutine frame allocation.
348
349 The returned pointer is valid for the lifetime of this context.
350 By default, this returns a pointer to the recycling memory
351 resource which pools frame allocations for reuse.
352
353 @return Pointer to the frame allocator.
354
355 @see set_frame_allocator
356 */
357 std::pmr::memory_resource*
358 3276x get_frame_allocator() const noexcept
359 {
360 3276x return frame_alloc_;
361 }
362
363 /** Set the memory resource used for coroutine frame allocation.
364
365 The caller is responsible for ensuring the memory resource
366 remains valid for the lifetime of all coroutines launched
367 using this context's executor.
368
369 @par Thread Safety
370 Not thread-safe. Must not be called while any thread may
371 be referencing this execution context or its executor.
372
373 @param mr Pointer to the memory resource.
374
375 @see get_frame_allocator
376 */
377 void
378 1x set_frame_allocator(std::pmr::memory_resource* mr) noexcept
379 {
380 1x owned_.reset();
381 1x frame_alloc_ = mr;
382 1x }
383
384 /** Set the frame allocator from a standard Allocator.
385
386 The allocator is wrapped in an internal memory resource
387 adapter owned by this context. The wrapper remains valid
388 for the lifetime of this context or until a subsequent
389 call to set_frame_allocator.
390
391 @par Thread Safety
392 Not thread-safe. Must not be called while any thread may
393 be referencing this execution context or its executor.
394
395 @tparam Allocator The allocator type satisfying the
396 standard Allocator requirements.
397
398 @param a The allocator to use.
399
400 @see get_frame_allocator
401 */
402 template<class Allocator>
403 requires (!std::is_pointer_v<Allocator>)
404 void
405 152x set_frame_allocator(Allocator const& a)
406 {
407 static_assert(
408 requires { typename std::allocator_traits<Allocator>::value_type; },
409 "Allocator must satisfy allocator requirements");
410 static_assert(
411 std::is_copy_constructible_v<Allocator>,
412 "Allocator must be copy constructible");
413
414
2/2
void boost::capy::execution_context::set_frame_allocator<std::allocator<int> >(std::allocator<int> const&):
✓ Branch 1 taken 1 time.
void boost::capy::execution_context::set_frame_allocator<std::allocator<void> >(std::allocator<void> const&):
✓ Branch 1 taken 151 times.
152x auto p = std::make_shared<
415 detail::frame_memory_resource<Allocator>>(a);
416 152x frame_alloc_ = p.get();
417 152x owned_ = std::move(p);
418 152x }
419
420 /** Return a pointer to this context if it matches the
421 requested type.
422
423 Performs a type check and downcasts `this` when the
424 types match, or returns `nullptr` otherwise. Analogous
425 to `std::any_cast< ExecutionContext >( &a )`.
426
427 @tparam ExecutionContext The derived context type to
428 retrieve.
429
430 @return A pointer to this context as the requested
431 type, or `nullptr` if the type does not match.
432 */
433 template< typename ExecutionContext >
434 1x const ExecutionContext* target() const
435 {
436
3/6
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
✓ Branch 4 taken 1 time.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 time.
✗ Branch 7 not taken.
1x if ( ti_ && *ti_ == detail::type_id< ExecutionContext >() )
437 1x return static_cast< ExecutionContext const* >( this );
438 return nullptr;
439 }
440
441 /// @copydoc target() const
442 template< typename ExecutionContext >
443 2x ExecutionContext* target()
444 {
445
6/12
boost::capy::execution_context_test::testTarget()::other_context* boost::capy::execution_context::target<boost::capy::execution_context_test::testTarget()::other_context>():
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 time.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 time.
boost::capy::test_io_context* boost::capy::execution_context::target<boost::capy::test_io_context>():
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
✓ Branch 4 taken 1 time.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 time.
✗ Branch 7 not taken.
2x if ( ti_ && *ti_ == detail::type_id< ExecutionContext >() )
446 1x return static_cast< ExecutionContext* >( this );
447 1x return nullptr;
448 }
449
450 protected:
451 /** Shut down all services.
452
453 Calls `shutdown()` on each service in reverse order of creation.
454 After this call, services remain allocated but are in a stopped
455 state. Derived classes should call this in their destructor
456 before any members are destroyed. This function is idempotent;
457 subsequent calls have no effect.
458
459 @par Effects
460 Each service's `shutdown()` member function is invoked once.
461
462 @par Postconditions
463 @li All services are in a stopped state.
464
465 @par Exception Safety
466 No-throw guarantee.
467
468 @par Thread Safety
469 Not thread-safe. Must not be called concurrently with other
470 operations on this execution_context.
471 */
472 void shutdown() noexcept;
473
474 /** Destroy all services.
475
476 Deletes all services in reverse order of creation. Derived
477 classes should call this as the final step of destruction.
478 This function is idempotent; subsequent calls have no effect.
479
480 @par Preconditions
481 @li `shutdown()` has been called.
482
483 @par Effects
484 All services are deleted and removed from the container.
485
486 @par Postconditions
487 @li The service container is empty.
488
489 @par Exception Safety
490 No-throw guarantee.
491
492 @par Thread Safety
493 Not thread-safe. Must not be called concurrently with other
494 operations on this execution_context.
495 */
496 void destroy() noexcept;
497
498 private:
499 struct BOOST_CAPY_DECL
500 factory
501 {
502 // warning C4251: 'std::type_index' needs to have dll-interface
503 BOOST_CAPY_MSVC_WARNING_PUSH
504 BOOST_CAPY_MSVC_WARNING_DISABLE(4251)
505 detail::type_index t0;
506 detail::type_index t1;
507 BOOST_CAPY_MSVC_WARNING_POP
508
509 84x factory(
510 detail::type_info const& t0_,
511 detail::type_info const& t1_)
512 84x : t0(t0_), t1(t1_)
513 {
514 84x }
515
516 virtual service* create(execution_context&) = 0;
517
518 protected:
519 ~factory() = default;
520 };
521
522 service* find_impl(detail::type_index ti) const noexcept;
523 service& use_service_impl(factory& f);
524 service& make_service_impl(factory& f);
525
526 // warning C4251: std::mutex, std::shared_ptr need dll-interface
527 BOOST_CAPY_MSVC_WARNING_PUSH
528 BOOST_CAPY_MSVC_WARNING_DISABLE(4251)
529 mutable std::mutex mutex_;
530 std::shared_ptr<void> owned_;
531 BOOST_CAPY_MSVC_WARNING_POP
532 std::pmr::memory_resource* frame_alloc_ = nullptr;
533 service* head_ = nullptr;
534 bool shutdown_ = false;
535 };
536
537 template< typename Derived >
538 26x execution_context::
539 execution_context( Derived* ) noexcept
540 26x : execution_context()
541 {
542 26x ti_ = &detail::type_id< Derived >();
543 26x }
544
545 } // namespace capy
546 } // namespace boost
547
548 #endif
549