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