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:
143  
        virtual ~service() = default;
143  
        virtual ~service() = default;
144  

144  

145  
    protected:
145  
    protected:
146  
        service() = default;
146  
        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 -
        BOOST_CAPY_MSVC_WARNING_PUSH
166 +
#ifdef _MSC_VER
167 -
        BOOST_CAPY_MSVC_WARNING_DISABLE(4251)
167 +
# pragma warning(push)
 
168 +
# pragma warning(disable: 4251)
 
169 +
#endif
168  
        detail::type_index t0_{detail::type_id<void>()};
170  
        detail::type_index t0_{detail::type_id<void>()};
169  
        detail::type_index t1_{detail::type_id<void>()};
171  
        detail::type_index t1_{detail::type_id<void>()};
170 -
        BOOST_CAPY_MSVC_WARNING_POP
172 +
#ifdef _MSC_VER
 
173 +
# pragma warning(pop)
 
174 +
#endif
171  
    };
175  
    };
172  

176  

173  
    //------------------------------------------------
177  
    //------------------------------------------------
174  

178  

175  
    execution_context(execution_context const&) = delete;
179  
    execution_context(execution_context const&) = delete;
176  

180  

177  
    execution_context& operator=(execution_context const&) = delete;
181  
    execution_context& operator=(execution_context const&) = delete;
178  

182  

179  
    /** Destructor.
183  
    /** Destructor.
180  

184  

181  
        Calls `shutdown()` then `destroy()` to clean up all services.
185  
        Calls `shutdown()` then `destroy()` to clean up all services.
182  

186  

183  
        @par Effects
187  
        @par Effects
184  
        All services are shut down and deleted in reverse order
188  
        All services are shut down and deleted in reverse order
185  
        of creation.
189  
        of creation.
186  

190  

187  
        @par Exception Safety
191  
        @par Exception Safety
188  
        No-throw guarantee.
192  
        No-throw guarantee.
189  
    */
193  
    */
190  
    ~execution_context();
194  
    ~execution_context();
191  

195  

192  
    /** Construct a default instance.
196  
    /** Construct a default instance.
193  

197  

194  
        @par Exception Safety
198  
        @par Exception Safety
195  
        Strong guarantee.
199  
        Strong guarantee.
196  
    */
200  
    */
197  
    execution_context();
201  
    execution_context();
198  

202  

199  
    /** Return true if a service of type T exists.
203  
    /** Return true if a service of type T exists.
200  

204  

201  
        @par Thread Safety
205  
        @par Thread Safety
202  
        Thread-safe.
206  
        Thread-safe.
203  

207  

204  
        @tparam T The type of service to check.
208  
        @tparam T The type of service to check.
205  

209  

206  
        @return `true` if the service exists.
210  
        @return `true` if the service exists.
207  
    */
211  
    */
208  
    template<class T>
212  
    template<class T>
209  
    bool has_service() const noexcept
213  
    bool has_service() const noexcept
210  
    {
214  
    {
211  
        return find_service<T>() != nullptr;
215  
        return find_service<T>() != nullptr;
212  
    }
216  
    }
213  

217  

214  
    /** Return a pointer to the service of type T, or nullptr.
218  
    /** Return a pointer to the service of type T, or nullptr.
215  

219  

216  
        @par Thread Safety
220  
        @par Thread Safety
217  
        Thread-safe.
221  
        Thread-safe.
218  

222  

219  
        @tparam T The type of service to find.
223  
        @tparam T The type of service to find.
220  

224  

221  
        @return A pointer to the service, or `nullptr` if not present.
225  
        @return A pointer to the service, or `nullptr` if not present.
222  
    */
226  
    */
223  
    template<class T>
227  
    template<class T>
224  
    T* find_service() const noexcept
228  
    T* find_service() const noexcept
225  
    {
229  
    {
226  
        std::lock_guard<std::mutex> lock(mutex_);
230  
        std::lock_guard<std::mutex> lock(mutex_);
227  
        return static_cast<T*>(find_impl(detail::type_id<T>()));
231  
        return static_cast<T*>(find_impl(detail::type_id<T>()));
228  
    }
232  
    }
229  

233  

230  
    /** Return a reference to the service of type T, creating it if needed.
234  
    /** Return a reference to the service of type T, creating it if needed.
231  

235  

232  
        If no service of type T exists, one is created by calling
236  
        If no service of type T exists, one is created by calling
233  
        `T(execution_context&)`. If T has a nested `key_type`, the
237  
        `T(execution_context&)`. If T has a nested `key_type`, the
234  
        service is also indexed under that type.
238  
        service is also indexed under that type.
235  

239  

236  
        @par Constraints
240  
        @par Constraints
237  
        @li `T` must derive from `service`.
241  
        @li `T` must derive from `service`.
238  
        @li `T` must be constructible from `execution_context&`.
242  
        @li `T` must be constructible from `execution_context&`.
239  

243  

240  
        @par Exception Safety
244  
        @par Exception Safety
241  
        Strong guarantee. If service creation throws, the container
245  
        Strong guarantee. If service creation throws, the container
242  
        is unchanged.
246  
        is unchanged.
243  

247  

244  
        @par Thread Safety
248  
        @par Thread Safety
245  
        Thread-safe.
249  
        Thread-safe.
246  

250  

247  
        @tparam T The type of service to retrieve or create.
251  
        @tparam T The type of service to retrieve or create.
248  

252  

249  
        @return A reference to the service.
253  
        @return A reference to the service.
250  
    */
254  
    */
251  
    template<class T>
255  
    template<class T>
252  
    T& use_service()
256  
    T& use_service()
253  
    {
257  
    {
254  
        static_assert(std::is_base_of<service, T>::value,
258  
        static_assert(std::is_base_of<service, T>::value,
255  
            "T must derive from service");
259  
            "T must derive from service");
256  
        static_assert(std::is_constructible<T, execution_context&>::value,
260  
        static_assert(std::is_constructible<T, execution_context&>::value,
257  
            "T must be constructible from execution_context&");
261  
            "T must be constructible from execution_context&");
258  

262  

259  
        struct impl : factory
263  
        struct impl : factory
260  
        {
264  
        {
261  
            impl()
265  
            impl()
262  
                : factory(
266  
                : factory(
263  
                    detail::type_id<T>(),
267  
                    detail::type_id<T>(),
264  
                    get_key<T>::value
268  
                    get_key<T>::value
265  
                        ? detail::type_id<typename get_key<T>::type>()
269  
                        ? detail::type_id<typename get_key<T>::type>()
266  
                        : detail::type_id<T>())
270  
                        : detail::type_id<T>())
267  
            {
271  
            {
268  
            }
272  
            }
269  

273  

270  
            service* create(execution_context& ctx) override
274  
            service* create(execution_context& ctx) override
271  
            {
275  
            {
272  
                return new T(ctx);
276  
                return new T(ctx);
273  
            }
277  
            }
274  
        };
278  
        };
275  

279  

276  
        impl f;
280  
        impl f;
277  
        return static_cast<T&>(use_service_impl(f));
281  
        return static_cast<T&>(use_service_impl(f));
278  
    }
282  
    }
279  

283  

280  
    /** Construct and add a service.
284  
    /** Construct and add a service.
281  

285  

282  
        A new service of type T is constructed using the provided
286  
        A new service of type T is constructed using the provided
283  
        arguments and added to the container. If T has a nested
287  
        arguments and added to the container. If T has a nested
284  
        `key_type`, the service is also indexed under that type.
288  
        `key_type`, the service is also indexed under that type.
285  

289  

286  
        @par Constraints
290  
        @par Constraints
287  
        @li `T` must derive from `service`.
291  
        @li `T` must derive from `service`.
288  
        @li `T` must be constructible from `execution_context&, Args...`.
292  
        @li `T` must be constructible from `execution_context&, Args...`.
289  
        @li If `T::key_type` exists, `T&` must be convertible to `key_type&`.
293  
        @li If `T::key_type` exists, `T&` must be convertible to `key_type&`.
290  

294  

291  
        @par Exception Safety
295  
        @par Exception Safety
292  
        Strong guarantee. If service creation throws, the container
296  
        Strong guarantee. If service creation throws, the container
293  
        is unchanged.
297  
        is unchanged.
294  

298  

295  
        @par Thread Safety
299  
        @par Thread Safety
296  
        Thread-safe.
300  
        Thread-safe.
297  

301  

298  
        @throws std::invalid_argument if a service of the same type
302  
        @throws std::invalid_argument if a service of the same type
299  
            or `key_type` already exists.
303  
            or `key_type` already exists.
300  

304  

301  
        @tparam T The type of service to create.
305  
        @tparam T The type of service to create.
302  

306  

303  
        @param args Arguments forwarded to the constructor of T.
307  
        @param args Arguments forwarded to the constructor of T.
304  

308  

305  
        @return A reference to the created service.
309  
        @return A reference to the created service.
306  
    */
310  
    */
307  
    template<class T, class... Args>
311  
    template<class T, class... Args>
308  
    T& make_service(Args&&... args)
312  
    T& make_service(Args&&... args)
309  
    {
313  
    {
310  
        static_assert(std::is_base_of<service, T>::value,
314  
        static_assert(std::is_base_of<service, T>::value,
311  
            "T must derive from service");
315  
            "T must derive from service");
312  
        if constexpr(get_key<T>::value)
316  
        if constexpr(get_key<T>::value)
313  
        {
317  
        {
314  
            static_assert(
318  
            static_assert(
315  
                std::is_convertible<T&, typename get_key<T>::type&>::value,
319  
                std::is_convertible<T&, typename get_key<T>::type&>::value,
316  
                "T& must be convertible to key_type&");
320  
                "T& must be convertible to key_type&");
317  
        }
321  
        }
318  

322  

319  
        struct impl : factory
323  
        struct impl : factory
320  
        {
324  
        {
321  
            std::tuple<Args&&...> args_;
325  
            std::tuple<Args&&...> args_;
322  

326  

323  
            explicit impl(Args&&... a)
327  
            explicit impl(Args&&... a)
324  
                : factory(
328  
                : factory(
325  
                    detail::type_id<T>(),
329  
                    detail::type_id<T>(),
326  
                    get_key<T>::value
330  
                    get_key<T>::value
327  
                        ? detail::type_id<typename get_key<T>::type>()
331  
                        ? detail::type_id<typename get_key<T>::type>()
328  
                        : detail::type_id<T>())
332  
                        : detail::type_id<T>())
329  
                , args_(std::forward<Args>(a)...)
333  
                , args_(std::forward<Args>(a)...)
330  
            {
334  
            {
331  
            }
335  
            }
332  

336  

333  
            service* create(execution_context& ctx) override
337  
            service* create(execution_context& ctx) override
334  
            {
338  
            {
335  
                return std::apply([&ctx](auto&&... a) {
339  
                return std::apply([&ctx](auto&&... a) {
336  
                    return new T(ctx, std::forward<decltype(a)>(a)...);
340  
                    return new T(ctx, std::forward<decltype(a)>(a)...);
337  
                }, std::move(args_));
341  
                }, std::move(args_));
338  
            }
342  
            }
339  
        };
343  
        };
340  

344  

341  
        impl f(std::forward<Args>(args)...);
345  
        impl f(std::forward<Args>(args)...);
342  
        return static_cast<T&>(make_service_impl(f));
346  
        return static_cast<T&>(make_service_impl(f));
343  
    }
347  
    }
344  

348  

345  
    //------------------------------------------------
349  
    //------------------------------------------------
346  

350  

347  
    /** Return the memory resource used for coroutine frame allocation.
351  
    /** Return the memory resource used for coroutine frame allocation.
348  

352  

349  
        The returned pointer is valid for the lifetime of this context.
353  
        The returned pointer is valid for the lifetime of this context.
350  
        By default, this returns a pointer to the recycling memory
354  
        By default, this returns a pointer to the recycling memory
351  
        resource which pools frame allocations for reuse.
355  
        resource which pools frame allocations for reuse.
352  

356  

353  
        @return Pointer to the frame allocator.
357  
        @return Pointer to the frame allocator.
354  

358  

355  
        @see set_frame_allocator
359  
        @see set_frame_allocator
356  
    */
360  
    */
357  
    std::pmr::memory_resource*
361  
    std::pmr::memory_resource*
358  
    get_frame_allocator() const noexcept
362  
    get_frame_allocator() const noexcept
359  
    {
363  
    {
360  
        return frame_alloc_;
364  
        return frame_alloc_;
361  
    }
365  
    }
362  

366  

363  
    /** Set the memory resource used for coroutine frame allocation.
367  
    /** Set the memory resource used for coroutine frame allocation.
364  

368  

365  
        The caller is responsible for ensuring the memory resource
369  
        The caller is responsible for ensuring the memory resource
366  
        remains valid for the lifetime of all coroutines launched
370  
        remains valid for the lifetime of all coroutines launched
367  
        using this context's executor.
371  
        using this context's executor.
368  

372  

369  
        @par Thread Safety
373  
        @par Thread Safety
370  
        Not thread-safe. Must not be called while any thread may
374  
        Not thread-safe. Must not be called while any thread may
371  
        be referencing this execution context or its executor.
375  
        be referencing this execution context or its executor.
372  

376  

373  
        @param mr Pointer to the memory resource.
377  
        @param mr Pointer to the memory resource.
374  

378  

375  
        @see get_frame_allocator
379  
        @see get_frame_allocator
376  
    */
380  
    */
377  
    void
381  
    void
378  
    set_frame_allocator(std::pmr::memory_resource* mr) noexcept
382  
    set_frame_allocator(std::pmr::memory_resource* mr) noexcept
379  
    {
383  
    {
380  
        owned_.reset();
384  
        owned_.reset();
381  
        frame_alloc_ = mr;
385  
        frame_alloc_ = mr;
382  
    }
386  
    }
383  

387  

384  
    /** Set the frame allocator from a standard Allocator.
388  
    /** Set the frame allocator from a standard Allocator.
385  

389  

386  
        The allocator is wrapped in an internal memory resource
390  
        The allocator is wrapped in an internal memory resource
387  
        adapter owned by this context. The wrapper remains valid
391  
        adapter owned by this context. The wrapper remains valid
388  
        for the lifetime of this context or until a subsequent
392  
        for the lifetime of this context or until a subsequent
389  
        call to set_frame_allocator.
393  
        call to set_frame_allocator.
390  

394  

391  
        @par Thread Safety
395  
        @par Thread Safety
392  
        Not thread-safe. Must not be called while any thread may
396  
        Not thread-safe. Must not be called while any thread may
393  
        be referencing this execution context or its executor.
397  
        be referencing this execution context or its executor.
394  

398  

395  
        @tparam Allocator The allocator type satisfying the
399  
        @tparam Allocator The allocator type satisfying the
396  
            standard Allocator requirements.
400  
            standard Allocator requirements.
397  

401  

398  
        @param a The allocator to use.
402  
        @param a The allocator to use.
399  

403  

400  
        @see get_frame_allocator
404  
        @see get_frame_allocator
401  
    */
405  
    */
402  
    template<class Allocator>
406  
    template<class Allocator>
403  
        requires (!std::is_pointer_v<Allocator>)
407  
        requires (!std::is_pointer_v<Allocator>)
404  
    void
408  
    void
405  
    set_frame_allocator(Allocator const& a)
409  
    set_frame_allocator(Allocator const& a)
406  
    {
410  
    {
407  
        static_assert(
411  
        static_assert(
408  
            requires { typename std::allocator_traits<Allocator>::value_type; },
412  
            requires { typename std::allocator_traits<Allocator>::value_type; },
409  
            "Allocator must satisfy allocator requirements");
413  
            "Allocator must satisfy allocator requirements");
410  
        static_assert(
414  
        static_assert(
411  
            std::is_copy_constructible_v<Allocator>,
415  
            std::is_copy_constructible_v<Allocator>,
412  
            "Allocator must be copy constructible");
416  
            "Allocator must be copy constructible");
413  

417  

414  
        auto p = std::make_shared<
418  
        auto p = std::make_shared<
415  
            detail::frame_memory_resource<Allocator>>(a);
419  
            detail::frame_memory_resource<Allocator>>(a);
416  
        frame_alloc_ = p.get();
420  
        frame_alloc_ = p.get();
417  
        owned_ = std::move(p);
421  
        owned_ = std::move(p);
418  
    }
422  
    }
419  

423  

420  
    /** Return a pointer to this context if it matches the
424  
    /** Return a pointer to this context if it matches the
421  
        requested type.
425  
        requested type.
422  

426  

423  
        Performs a type check and downcasts `this` when the
427  
        Performs a type check and downcasts `this` when the
424  
        types match, or returns `nullptr` otherwise. Analogous
428  
        types match, or returns `nullptr` otherwise. Analogous
425  
        to `std::any_cast< ExecutionContext >( &a )`.
429  
        to `std::any_cast< ExecutionContext >( &a )`.
426  

430  

427  
        @tparam ExecutionContext The derived context type to
431  
        @tparam ExecutionContext The derived context type to
428  
            retrieve.
432  
            retrieve.
429  

433  

430  
        @return A pointer to this context as the requested
434  
        @return A pointer to this context as the requested
431  
            type, or `nullptr` if the type does not match.
435  
            type, or `nullptr` if the type does not match.
432  
    */
436  
    */
433  
    template< typename ExecutionContext >
437  
    template< typename ExecutionContext >
434  
    const ExecutionContext* target() const
438  
    const ExecutionContext* target() const
435  
    {
439  
    {
436  
        if ( ti_ && *ti_ == detail::type_id< ExecutionContext >() )
440  
        if ( ti_ && *ti_ == detail::type_id< ExecutionContext >() )
437  
           return static_cast< ExecutionContext const* >( this );
441  
           return static_cast< ExecutionContext const* >( this );
438  
        return nullptr;
442  
        return nullptr;
439  
    }
443  
    }
440  

444  

441  
    /// @copydoc target() const
445  
    /// @copydoc target() const
442  
    template< typename ExecutionContext >
446  
    template< typename ExecutionContext >
443  
    ExecutionContext* target()
447  
    ExecutionContext* target()
444  
    {
448  
    {
445  
        if ( ti_ && *ti_ == detail::type_id< ExecutionContext >() )
449  
        if ( ti_ && *ti_ == detail::type_id< ExecutionContext >() )
446  
           return static_cast< ExecutionContext* >( this );
450  
           return static_cast< ExecutionContext* >( this );
447  
        return nullptr;
451  
        return nullptr;
448  
    }
452  
    }
449  

453  

450  
protected:
454  
protected:
451  
    /** Shut down all services.
455  
    /** Shut down all services.
452  

456  

453  
        Calls `shutdown()` on each service in reverse order of creation.
457  
        Calls `shutdown()` on each service in reverse order of creation.
454  
        After this call, services remain allocated but are in a stopped
458  
        After this call, services remain allocated but are in a stopped
455  
        state. Derived classes should call this in their destructor
459  
        state. Derived classes should call this in their destructor
456  
        before any members are destroyed. This function is idempotent;
460  
        before any members are destroyed. This function is idempotent;
457  
        subsequent calls have no effect.
461  
        subsequent calls have no effect.
458  

462  

459  
        @par Effects
463  
        @par Effects
460  
        Each service's `shutdown()` member function is invoked once.
464  
        Each service's `shutdown()` member function is invoked once.
461  

465  

462  
        @par Postconditions
466  
        @par Postconditions
463  
        @li All services are in a stopped state.
467  
        @li All services are in a stopped state.
464  

468  

465  
        @par Exception Safety
469  
        @par Exception Safety
466  
        No-throw guarantee.
470  
        No-throw guarantee.
467  

471  

468  
        @par Thread Safety
472  
        @par Thread Safety
469  
        Not thread-safe. Must not be called concurrently with other
473  
        Not thread-safe. Must not be called concurrently with other
470  
        operations on this execution_context.
474  
        operations on this execution_context.
471  
    */
475  
    */
472  
    void shutdown() noexcept;
476  
    void shutdown() noexcept;
473  

477  

474  
    /** Destroy all services.
478  
    /** Destroy all services.
475  

479  

476  
        Deletes all services in reverse order of creation. Derived
480  
        Deletes all services in reverse order of creation. Derived
477  
        classes should call this as the final step of destruction.
481  
        classes should call this as the final step of destruction.
478  
        This function is idempotent; subsequent calls have no effect.
482  
        This function is idempotent; subsequent calls have no effect.
479  

483  

480  
        @par Preconditions
484  
        @par Preconditions
481  
        @li `shutdown()` has been called.
485  
        @li `shutdown()` has been called.
482  

486  

483  
        @par Effects
487  
        @par Effects
484  
        All services are deleted and removed from the container.
488  
        All services are deleted and removed from the container.
485  

489  

486  
        @par Postconditions
490  
        @par Postconditions
487  
        @li The service container is empty.
491  
        @li The service container is empty.
488  

492  

489  
        @par Exception Safety
493  
        @par Exception Safety
490  
        No-throw guarantee.
494  
        No-throw guarantee.
491  

495  

492  
        @par Thread Safety
496  
        @par Thread Safety
493  
        Not thread-safe. Must not be called concurrently with other
497  
        Not thread-safe. Must not be called concurrently with other
494  
        operations on this execution_context.
498  
        operations on this execution_context.
495  
    */
499  
    */
496  
    void destroy() noexcept;
500  
    void destroy() noexcept;
497  

501  

498  
private:
502  
private:
499  
    struct BOOST_CAPY_DECL
503  
    struct BOOST_CAPY_DECL
500  
        factory
504  
        factory
501  
    {
505  
    {
 
506 +
#ifdef _MSC_VER
 
507 +
# pragma warning(push)
 
508 +
# pragma warning(disable: 4251)
 
509 +
#endif
502 -
        BOOST_CAPY_MSVC_WARNING_PUSH
 
503 -
        BOOST_CAPY_MSVC_WARNING_DISABLE(4251)
 
504  
// warning C4251: 'std::type_index' needs to have dll-interface
510  
// warning C4251: 'std::type_index' needs to have dll-interface
505  
        detail::type_index t0;
511  
        detail::type_index t0;
506  
        detail::type_index t1;
512  
        detail::type_index t1;
507 -
        BOOST_CAPY_MSVC_WARNING_POP
513 +
#ifdef _MSC_VER
 
514 +
# pragma warning(pop)
 
515 +
#endif
508  

516  

509  
        factory(
517  
        factory(
510  
            detail::type_info const& t0_,
518  
            detail::type_info const& t0_,
511  
            detail::type_info const& t1_)
519  
            detail::type_info const& t1_)
512  
            : t0(t0_), t1(t1_)
520  
            : t0(t0_), t1(t1_)
513  
        {
521  
        {
514  
        }
522  
        }
515  

523  

516  
        virtual service* create(execution_context&) = 0;
524  
        virtual service* create(execution_context&) = 0;
517  

525  

518  
    protected:
526  
    protected:
519  
        ~factory() = default;
527  
        ~factory() = default;
520  
    };
528  
    };
521  

529  

522  
    service* find_impl(detail::type_index ti) const noexcept;
530  
    service* find_impl(detail::type_index ti) const noexcept;
523  
    service& use_service_impl(factory& f);
531  
    service& use_service_impl(factory& f);
524  
    service& make_service_impl(factory& f);
532  
    service& make_service_impl(factory& f);
525  

533  

526 -
// warning C4251: std::mutex, std::shared_ptr need dll-interface
534 +
#ifdef _MSC_VER
527 -
    BOOST_CAPY_MSVC_WARNING_PUSH
535 +
# pragma warning(push)
528 -
    BOOST_CAPY_MSVC_WARNING_DISABLE(4251)
536 +
# pragma warning(disable: 4251)
 
537 +
#endif
 
538 +
// warning C4251: 'std::type_index' needs to have dll-interface
529  
    mutable std::mutex mutex_;
539  
    mutable std::mutex mutex_;
530  
    std::shared_ptr<void> owned_;
540  
    std::shared_ptr<void> owned_;
531 -
    BOOST_CAPY_MSVC_WARNING_POP
541 +
#ifdef _MSC_VER
 
542 +
# pragma warning(pop)
 
543 +
#endif
532  
    std::pmr::memory_resource* frame_alloc_ = nullptr;
544  
    std::pmr::memory_resource* frame_alloc_ = nullptr;
533  
    service* head_ = nullptr;
545  
    service* head_ = nullptr;
534  
    bool shutdown_ = false;
546  
    bool shutdown_ = false;
535  
};
547  
};
536  

548  

537  
template< typename Derived >
549  
template< typename Derived >
538  
execution_context::
550  
execution_context::
539  
execution_context( Derived* ) noexcept
551  
execution_context( Derived* ) noexcept
540  
    : execution_context()
552  
    : execution_context()
541  
{
553  
{
542  
    ti_ = &detail::type_id< Derived >();
554  
    ti_ = &detail::type_id< Derived >();
543  
}
555  
}
544  

556  

545  
} // namespace capy
557  
} // namespace capy
546  
} // namespace boost
558  
} // namespace boost
547  

559  

548  
#endif
560  
#endif