include/boost/capy/ex/execution_context.hpp

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