include/boost/capy/ex/execution_context.hpp

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