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