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