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