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