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