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