1  
//
1  
//
2  
// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
2  
// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot 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  
    template<class T, class = void>
88  
    template<class T, class = void>
89  
    struct get_key : std::false_type
89  
    struct get_key : std::false_type
90  
    {};
90  
    {};
91  

91  

92  
    template<class T>
92  
    template<class T>
93  
    struct get_key<T, std::void_t<typename T::key_type>> : std::true_type
93  
    struct get_key<T, std::void_t<typename T::key_type>> : std::true_type
94  
    {
94  
    {
95  
        using type = typename T::key_type;
95  
        using type = typename T::key_type;
96  
    };
96  
    };
97  

97  

98  
public:
98  
public:
99  
    //------------------------------------------------
99  
    //------------------------------------------------
100  

100  

101  
    /** Abstract base class for services owned by an execution context.
101  
    /** Abstract base class for services owned by an execution context.
102  

102  

103  
        Services provide extensible functionality to an execution context.
103  
        Services provide extensible functionality to an execution context.
104  
        Each service type can be registered at most once. Services are
104  
        Each service type can be registered at most once. Services are
105  
        created via `use_service()` or `make_service()` and are owned by
105  
        created via `use_service()` or `make_service()` and are owned by
106  
        the execution context for their lifetime.
106  
        the execution context for their lifetime.
107  

107  

108  
        Derived classes must implement the pure virtual `shutdown()` member
108  
        Derived classes must implement the pure virtual `shutdown()` member
109  
        function, which is called when the owning execution context is
109  
        function, which is called when the owning execution context is
110  
        being destroyed. The `shutdown()` function should release resources
110  
        being destroyed. The `shutdown()` function should release resources
111  
        and cancel outstanding operations without blocking.
111  
        and cancel outstanding operations without blocking.
112  

112  

113  
        @par Deriving from service
113  
        @par Deriving from service
114  
        @li Implement `shutdown()` to perform cleanup.
114  
        @li Implement `shutdown()` to perform cleanup.
115  
        @li Accept `execution_context&` as the first constructor parameter.
115  
        @li Accept `execution_context&` as the first constructor parameter.
116  
        @li Optionally define `key_type` to enable base-class lookup.
116  
        @li Optionally define `key_type` to enable base-class lookup.
117  

117  

118  
        @par Example
118  
        @par Example
119  
        @code
119  
        @code
120  
        struct my_service : execution_context::service
120  
        struct my_service : execution_context::service
121  
        {
121  
        {
122  
            explicit my_service(execution_context&) {}
122  
            explicit my_service(execution_context&) {}
123  

123  

124  
        protected:
124  
        protected:
125  
            void shutdown() override
125  
            void shutdown() override
126  
            {
126  
            {
127  
                // Cancel pending operations, release resources
127  
                // Cancel pending operations, release resources
128  
            }
128  
            }
129  
        };
129  
        };
130  
        @endcode
130  
        @endcode
131  

131  

132  
        @see execution_context
132  
        @see execution_context
133  
    */
133  
    */
134  
    class BOOST_CAPY_DECL
134  
    class BOOST_CAPY_DECL
135  
        service
135  
        service
136  
    {
136  
    {
137  
    public:
137  
    public:
138  
        virtual ~service() = default;
138  
        virtual ~service() = default;
139  

139  

140  
    protected:
140  
    protected:
141  
        service() = default;
141  
        service() = default;
142  

142  

143  
        /** Called when the owning execution context shuts down.
143  
        /** Called when the owning execution context shuts down.
144  

144  

145  
            Implementations should release resources and cancel any
145  
            Implementations should release resources and cancel any
146  
            outstanding asynchronous operations. This function must
146  
            outstanding asynchronous operations. This function must
147  
            not block and must not throw exceptions. Services are
147  
            not block and must not throw exceptions. Services are
148  
            shut down in reverse order of creation.
148  
            shut down in reverse order of creation.
149  

149  

150  
            @par Exception Safety
150  
            @par Exception Safety
151  
            No-throw guarantee.
151  
            No-throw guarantee.
152  
        */
152  
        */
153  
        virtual void shutdown() = 0;
153  
        virtual void shutdown() = 0;
154  

154  

155  
    private:
155  
    private:
156  
        friend class execution_context;
156  
        friend class execution_context;
157  

157  

158  
        service* next_ = nullptr;
158  
        service* next_ = nullptr;
159  

159  

160  
// warning C4251: 'std::type_index' needs to have dll-interface
160  
// warning C4251: 'std::type_index' needs to have dll-interface
161  
#ifdef _MSC_VER
161  
#ifdef _MSC_VER
162  
# pragma warning(push)
162  
# pragma warning(push)
163  
# pragma warning(disable: 4251)
163  
# pragma warning(disable: 4251)
164  
#endif
164  
#endif
165  
        detail::type_index t0_{detail::type_id<void>()};
165  
        detail::type_index t0_{detail::type_id<void>()};
166  
        detail::type_index t1_{detail::type_id<void>()};
166  
        detail::type_index t1_{detail::type_id<void>()};
167  
#ifdef _MSC_VER
167  
#ifdef _MSC_VER
168  
# pragma warning(pop)
168  
# pragma warning(pop)
169  
#endif
169  
#endif
170  
    };
170  
    };
171  

171  

172  
    //------------------------------------------------
172  
    //------------------------------------------------
173  

173  

174  
    execution_context(execution_context const&) = delete;
174  
    execution_context(execution_context const&) = delete;
175  

175  

176  
    execution_context& operator=(execution_context const&) = delete;
176  
    execution_context& operator=(execution_context const&) = delete;
177  

177  

178  
    /** Destructor.
178  
    /** Destructor.
179  

179  

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

181  

182  
        @par Effects
182  
        @par Effects
183  
        All services are shut down and deleted in reverse order
183  
        All services are shut down and deleted in reverse order
184  
        of creation.
184  
        of creation.
185  

185  

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

190  

191  
    /** Default constructor.
191  
    /** Default constructor.
192  

192  

193  
        @par Exception Safety
193  
        @par Exception Safety
194  
        Strong guarantee.
194  
        Strong guarantee.
195  
    */
195  
    */
196  
    execution_context();
196  
    execution_context();
197  

197  

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

199  

200  
        @par Thread Safety
200  
        @par Thread Safety
201  
        Thread-safe.
201  
        Thread-safe.
202  

202  

203  
        @tparam T The type of service to check.
203  
        @tparam T The type of service to check.
204  

204  

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

212  

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

214  

215  
        @par Thread Safety
215  
        @par Thread Safety
216  
        Thread-safe.
216  
        Thread-safe.
217  

217  

218  
        @tparam T The type of service to find.
218  
        @tparam T The type of service to find.
219  

219  

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

228  

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

230  

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

234  

235  
        @par Constraints
235  
        @par Constraints
236  
        @li `T` must derive from `service`.
236  
        @li `T` must derive from `service`.
237  
        @li `T` must be constructible from `execution_context&`.
237  
        @li `T` must be constructible from `execution_context&`.
238  

238  

239  
        @par Exception Safety
239  
        @par Exception Safety
240  
        Strong guarantee. If service creation throws, the container
240  
        Strong guarantee. If service creation throws, the container
241  
        is unchanged.
241  
        is unchanged.
242  

242  

243  
        @par Thread Safety
243  
        @par Thread Safety
244  
        Thread-safe.
244  
        Thread-safe.
245  

245  

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

247  

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

257  

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

268  

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

274  

275  
        impl f;
275  
        impl f;
276  
        return static_cast<T&>(use_service_impl(f));
276  
        return static_cast<T&>(use_service_impl(f));
277  
    }
277  
    }
278  

278  

279  
    /** Construct and add a service.
279  
    /** Construct and add a service.
280  

280  

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

284  

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

289  

290  
        @par Exception Safety
290  
        @par Exception Safety
291  
        Strong guarantee. If service creation throws, the container
291  
        Strong guarantee. If service creation throws, the container
292  
        is unchanged.
292  
        is unchanged.
293  

293  

294  
        @par Thread Safety
294  
        @par Thread Safety
295  
        Thread-safe.
295  
        Thread-safe.
296  

296  

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

299  

300  
        @tparam T The type of service to create.
300  
        @tparam T The type of service to create.
301  

301  

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

303  

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

317  

318  
        struct impl : factory
318  
        struct impl : factory
319  
        {
319  
        {
320  
            std::tuple<Args&&...> args_;
320  
            std::tuple<Args&&...> args_;
321  

321  

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

331  

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

339  

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

343  

344  
    //------------------------------------------------
344  
    //------------------------------------------------
345  

345  

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

347  

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

351  

352  
        @return Pointer to the frame allocator.
352  
        @return Pointer to the frame allocator.
353  

353  

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

361  

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

363  

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

367  

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

371  

372  
        @param mr Pointer to the memory resource.
372  
        @param mr Pointer to the memory resource.
373  

373  

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

382  

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

384  

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

389  

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

393  

394  
        @tparam Allocator The allocator type satisfying the
394  
        @tparam Allocator The allocator type satisfying the
395  
            standard Allocator requirements.
395  
            standard Allocator requirements.
396  

396  

397  
        @param a The allocator to use.
397  
        @param a The allocator to use.
398  

398  

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

412  

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

418  

419  
protected:
419  
protected:
420  
    /** Shut down all services.
420  
    /** Shut down all services.
421  

421  

422  
        Calls `shutdown()` on each service in reverse order of creation.
422  
        Calls `shutdown()` on each service in reverse order of creation.
423  
        After this call, services remain allocated but are in a stopped
423  
        After this call, services remain allocated but are in a stopped
424  
        state. Derived classes should call this in their destructor
424  
        state. Derived classes should call this in their destructor
425  
        before any members are destroyed. This function is idempotent;
425  
        before any members are destroyed. This function is idempotent;
426  
        subsequent calls have no effect.
426  
        subsequent calls have no effect.
427  

427  

428  
        @par Effects
428  
        @par Effects
429  
        Each service's `shutdown()` member function is invoked once.
429  
        Each service's `shutdown()` member function is invoked once.
430  

430  

431  
        @par Postconditions
431  
        @par Postconditions
432  
        @li All services are in a stopped state.
432  
        @li All services are in a stopped state.
433  

433  

434  
        @par Exception Safety
434  
        @par Exception Safety
435  
        No-throw guarantee.
435  
        No-throw guarantee.
436  

436  

437  
        @par Thread Safety
437  
        @par Thread Safety
438  
        Not thread-safe. Must not be called concurrently with other
438  
        Not thread-safe. Must not be called concurrently with other
439  
        operations on this execution_context.
439  
        operations on this execution_context.
440  
    */
440  
    */
441  
    void shutdown() noexcept;
441  
    void shutdown() noexcept;
442  

442  

443  
    /** Destroy all services.
443  
    /** Destroy all services.
444  

444  

445  
        Deletes all services in reverse order of creation. Derived
445  
        Deletes all services in reverse order of creation. Derived
446  
        classes should call this as the final step of destruction.
446  
        classes should call this as the final step of destruction.
447  
        This function is idempotent; subsequent calls have no effect.
447  
        This function is idempotent; subsequent calls have no effect.
448  

448  

449  
        @par Preconditions
449  
        @par Preconditions
450  
        @li `shutdown()` has been called.
450  
        @li `shutdown()` has been called.
451  

451  

452  
        @par Effects
452  
        @par Effects
453  
        All services are deleted and removed from the container.
453  
        All services are deleted and removed from the container.
454  

454  

455  
        @par Postconditions
455  
        @par Postconditions
456  
        @li The service container is empty.
456  
        @li The service container is empty.
457  

457  

458  
        @par Exception Safety
458  
        @par Exception Safety
459  
        No-throw guarantee.
459  
        No-throw guarantee.
460  

460  

461  
        @par Thread Safety
461  
        @par Thread Safety
462  
        Not thread-safe. Must not be called concurrently with other
462  
        Not thread-safe. Must not be called concurrently with other
463  
        operations on this execution_context.
463  
        operations on this execution_context.
464  
    */
464  
    */
465  
    void destroy() noexcept;
465  
    void destroy() noexcept;
466  

466  

467  
private:
467  
private:
468  
    struct BOOST_CAPY_DECL
468  
    struct BOOST_CAPY_DECL
469  
        factory
469  
        factory
470  
    {
470  
    {
471  
#ifdef _MSC_VER
471  
#ifdef _MSC_VER
472  
# pragma warning(push)
472  
# pragma warning(push)
473  
# pragma warning(disable: 4251)
473  
# pragma warning(disable: 4251)
474  
#endif
474  
#endif
475  
// warning C4251: 'std::type_index' needs to have dll-interface
475  
// warning C4251: 'std::type_index' needs to have dll-interface
476  
        detail::type_index t0;
476  
        detail::type_index t0;
477  
        detail::type_index t1;
477  
        detail::type_index t1;
478  
#ifdef _MSC_VER
478  
#ifdef _MSC_VER
479  
# pragma warning(pop)
479  
# pragma warning(pop)
480  
#endif
480  
#endif
481  

481  

482  
        factory(
482  
        factory(
483  
            detail::type_info const& t0_,
483  
            detail::type_info const& t0_,
484  
            detail::type_info const& t1_)
484  
            detail::type_info const& t1_)
485  
            : t0(t0_), t1(t1_)
485  
            : t0(t0_), t1(t1_)
486  
        {
486  
        {
487  
        }
487  
        }
488  

488  

489  
        virtual service* create(execution_context&) = 0;
489  
        virtual service* create(execution_context&) = 0;
490  

490  

491  
    protected:
491  
    protected:
492  
        ~factory() = default;
492  
        ~factory() = default;
493  
    };
493  
    };
494  

494  

495  
    service* find_impl(detail::type_index ti) const noexcept;
495  
    service* find_impl(detail::type_index ti) const noexcept;
496  
    service& use_service_impl(factory& f);
496  
    service& use_service_impl(factory& f);
497  
    service& make_service_impl(factory& f);
497  
    service& make_service_impl(factory& f);
498  

498  

499  
#ifdef _MSC_VER
499  
#ifdef _MSC_VER
500  
# pragma warning(push)
500  
# pragma warning(push)
501  
# pragma warning(disable: 4251)
501  
# pragma warning(disable: 4251)
502  
#endif
502  
#endif
503  
// warning C4251: 'std::type_index' needs to have dll-interface
503  
// warning C4251: 'std::type_index' needs to have dll-interface
504  
    mutable std::mutex mutex_;
504  
    mutable std::mutex mutex_;
505  
    std::shared_ptr<void> owned_;
505  
    std::shared_ptr<void> owned_;
506  
#ifdef _MSC_VER
506  
#ifdef _MSC_VER
507  
# pragma warning(pop)
507  
# pragma warning(pop)
508  
#endif
508  
#endif
509  
    std::pmr::memory_resource* frame_alloc_ = nullptr;
509  
    std::pmr::memory_resource* frame_alloc_ = nullptr;
510  
    service* head_ = nullptr;
510  
    service* head_ = nullptr;
511  
    bool shutdown_ = false;
511  
    bool shutdown_ = false;
512  
};
512  
};
513  

513  

514  
} // namespace capy
514  
} // namespace capy
515  
} // namespace boost
515  
} // namespace boost
516  

516  

517  
#endif
517  
#endif