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