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