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 -
#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  
    /** Construct a default instance.
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>
213  
    bool has_service() const noexcept
209  
    bool has_service() const noexcept
214  
    {
210  
    {
215  
        return find_service<T>() != nullptr;
211  
        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>
228  
    T* find_service() const noexcept
224  
    T* find_service() const noexcept
229  
    {
225  
    {
230  
        std::lock_guard<std::mutex> lock(mutex_);
226  
        std::lock_guard<std::mutex> lock(mutex_);
231  
        return static_cast<T*>(find_impl(detail::type_id<T>()));
227  
        return static_cast<T*>(find_impl(detail::type_id<T>()));
232  
    }
228  
    }
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>
256  
    T& use_service()
252  
    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  
        {
265  
            impl()
261  
            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>()
270  
                        : detail::type_id<T>())
266  
                        : detail::type_id<T>())
271  
            {
267  
            {
272  
            }
268  
            }
273  

269  

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

275  

280  
        impl f;
276  
        impl f;
281  
        return static_cast<T&>(use_service_impl(f));
277  
        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>
312  
    T& make_service(Args&&... args)
308  
    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  

327  
            explicit impl(Args&&... a)
323  
            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>())
333  
                , args_(std::forward<Args>(a)...)
329  
                , args_(std::forward<Args>(a)...)
334  
            {
330  
            {
335  
            }
331  
            }
336  

332  

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

340  

345  
        impl f(std::forward<Args>(args)...);
341  
        impl f(std::forward<Args>(args)...);
346  
        return static_cast<T&>(make_service_impl(f));
342  
        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*
362  
    get_frame_allocator() const noexcept
358  
    get_frame_allocator() const noexcept
363  
    {
359  
    {
364  
        return frame_alloc_;
360  
        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
382  
    set_frame_allocator(std::pmr::memory_resource* mr) noexcept
378  
    set_frame_allocator(std::pmr::memory_resource* mr) noexcept
383  
    {
379  
    {
384  
        owned_.reset();
380  
        owned_.reset();
385  
        frame_alloc_ = mr;
381  
        frame_alloc_ = mr;
386  
    }
382  
    }
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
409  
    set_frame_allocator(Allocator const& a)
405  
    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  

418  
        auto p = std::make_shared<
414  
        auto p = std::make_shared<
419  
            detail::frame_memory_resource<Allocator>>(a);
415  
            detail::frame_memory_resource<Allocator>>(a);
420  
        frame_alloc_ = p.get();
416  
        frame_alloc_ = p.get();
421  
        owned_ = std::move(p);
417  
        owned_ = std::move(p);
422  
    }
418  
    }
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 >
438  
    const ExecutionContext* target() const
434  
    const ExecutionContext* target() const
439  
    {
435  
    {
440  
        if ( ti_ && *ti_ == detail::type_id< ExecutionContext >() )
436  
        if ( ti_ && *ti_ == detail::type_id< ExecutionContext >() )
441  
           return static_cast< ExecutionContext const* >( this );
437  
           return static_cast< ExecutionContext const* >( this );
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 >
447  
    ExecutionContext* target()
443  
    ExecutionContext* target()
448  
    {
444  
    {
449  
        if ( ti_ && *ti_ == detail::type_id< ExecutionContext >() )
445  
        if ( ti_ && *ti_ == detail::type_id< ExecutionContext >() )
450  
           return static_cast< ExecutionContext* >( this );
446  
           return static_cast< ExecutionContext* >( this );
451  
        return nullptr;
447  
        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  

517  
        factory(
509  
        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_)
520  
            : t0(t0_), t1(t1_)
512  
            : t0(t0_), t1(t1_)
521  
        {
513  
        {
522  
        }
514  
        }
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 >
550  
execution_context::
538  
execution_context::
551  
execution_context( Derived* ) noexcept
539  
execution_context( Derived* ) noexcept
552  
    : execution_context()
540  
    : execution_context()
553  
{
541  
{
554  
    ti_ = &detail::type_id< Derived >();
542  
    ti_ = &detail::type_id< Derived >();
555  
}
543  
}
556  

544  

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

547  

560  
#endif
548  
#endif