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