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