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