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/service_slot.hpp>
15  
#include <boost/capy/detail/service_slot.hpp>
16  
#include <boost/capy/detail/type_id.hpp>
16  
#include <boost/capy/detail/type_id.hpp>
17  
#include <boost/capy/concept/executor.hpp>
17  
#include <boost/capy/concept/executor.hpp>
18  
#include <atomic>
18  
#include <atomic>
19  
#include <concepts>
19  
#include <concepts>
20  
#include <memory>
20  
#include <memory>
21  
#include <memory_resource>
21  
#include <memory_resource>
22  
#include <mutex>
22  
#include <mutex>
23  
#include <tuple>
23  
#include <tuple>
24  
#include <type_traits>
24  
#include <type_traits>
25  
#include <utility>
25  
#include <utility>
26  

26  

27  
namespace boost {
27  
namespace boost {
28  
namespace capy {
28  
namespace capy {
29  

29  

30  
/** Base class for I/O object containers providing service management.
30  
/** Base class for I/O object containers providing service management.
31  

31  

32  
    An execution context represents a place where function objects are
32  
    An execution context represents a place where function objects are
33  
    executed. It provides a service registry where polymorphic services
33  
    executed. It provides a service registry where polymorphic services
34  
    can be stored and retrieved by type. Each service type may be stored
34  
    can be stored and retrieved by type. Each service type may be stored
35  
    at most once. Services may specify a nested `key_type` to enable
35  
    at most once. Services may specify a nested `key_type` to enable
36  
    lookup by a base class type.
36  
    lookup by a base class type.
37  

37  

38  
    Derived classes such as `io_context` extend this to provide
38  
    Derived classes such as `io_context` extend this to provide
39  
    execution facilities like event loops and thread pools. Derived
39  
    execution facilities like event loops and thread pools. Derived
40  
    class destructors must call `shutdown()` and `destroy()` to ensure
40  
    class destructors must call `shutdown()` and `destroy()` to ensure
41  
    proper service cleanup before member destruction.
41  
    proper service cleanup before member destruction.
42  

42  

43  
    @par Service Lifecycle
43  
    @par Service Lifecycle
44  
    Services are created on first use via `use_service()` or explicitly
44  
    Services are created on first use via `use_service()` or explicitly
45  
    via `make_service()`. During destruction, `shutdown()` is called on
45  
    via `make_service()`. During destruction, `shutdown()` is called on
46  
    each service in reverse order of creation, then `destroy()` deletes
46  
    each service in reverse order of creation, then `destroy()` deletes
47  
    them. Both functions are idempotent.
47  
    them. Both functions are idempotent.
48  

48  

49  
    @par Thread Safety
49  
    @par Thread Safety
50  
    Service registration and lookup functions are thread-safe.
50  
    Service registration and lookup functions are thread-safe.
51  
    The `shutdown()` and `destroy()` functions are not thread-safe
51  
    The `shutdown()` and `destroy()` functions are not thread-safe
52  
    and must only be called during destruction.
52  
    and must only be called during destruction.
53  

53  

54  
    @par Example
54  
    @par Example
55  
    @code
55  
    @code
56  
    struct file_service : execution_context::service
56  
    struct file_service : execution_context::service
57  
    {
57  
    {
58  
    protected:
58  
    protected:
59  
        void shutdown() override {}
59  
        void shutdown() override {}
60  
    };
60  
    };
61  

61  

62  
    struct posix_file_service : file_service
62  
    struct posix_file_service : file_service
63  
    {
63  
    {
64  
        using key_type = file_service;
64  
        using key_type = file_service;
65  

65  

66  
        explicit posix_file_service(execution_context&) {}
66  
        explicit posix_file_service(execution_context&) {}
67  
    };
67  
    };
68  

68  

69  
    class io_context : public execution_context
69  
    class io_context : public execution_context
70  
    {
70  
    {
71  
    public:
71  
    public:
72  
        ~io_context()
72  
        ~io_context()
73  
        {
73  
        {
74  
            shutdown();
74  
            shutdown();
75  
            destroy();
75  
            destroy();
76  
        }
76  
        }
77  
    };
77  
    };
78  

78  

79  
    io_context ctx;
79  
    io_context ctx;
80  
    ctx.make_service<posix_file_service>();
80  
    ctx.make_service<posix_file_service>();
81  
    ctx.find_service<file_service>();       // returns posix_file_service*
81  
    ctx.find_service<file_service>();       // returns posix_file_service*
82  
    ctx.find_service<posix_file_service>(); // also works
82  
    ctx.find_service<posix_file_service>(); // also works
83  
    @endcode
83  
    @endcode
84  

84  

85  
    @see service, is_execution_context
85  
    @see service, is_execution_context
86  
*/
86  
*/
87  
class BOOST_CAPY_DECL
87  
class BOOST_CAPY_DECL
88  
    execution_context
88  
    execution_context
89  
{
89  
{
90  
    detail::type_info const* ti_ = nullptr;
90  
    detail::type_info const* ti_ = nullptr;
91  

91  

92  
    template<class T, class = void>
92  
    template<class T, class = void>
93  
    struct get_key : std::false_type
93  
    struct get_key : std::false_type
94  
    {};
94  
    {};
95  

95  

96  
    template<class T>
96  
    template<class T>
97  
    struct get_key<T, std::void_t<typename T::key_type>> : std::true_type
97  
    struct get_key<T, std::void_t<typename T::key_type>> : std::true_type
98  
    {
98  
    {
99  
        using type = typename T::key_type;
99  
        using type = typename T::key_type;
100  
    };
100  
    };
101  
protected:
101  
protected:
102  
    template< typename Derived >
102  
    template< typename Derived >
103  
    explicit execution_context( Derived* ) noexcept;
103  
    explicit execution_context( Derived* ) noexcept;
104  

104  

105  
public:
105  
public:
106  
    //------------------------------------------------
106  
    //------------------------------------------------
107  

107  

108  
    /** Abstract base class for services owned by an execution context.
108  
    /** Abstract base class for services owned by an execution context.
109  

109  

110  
        Services provide extensible functionality to an execution context.
110  
        Services provide extensible functionality to an execution context.
111  
        Each service type can be registered at most once. Services are
111  
        Each service type can be registered at most once. Services are
112  
        created via `use_service()` or `make_service()` and are owned by
112  
        created via `use_service()` or `make_service()` and are owned by
113  
        the execution context for their lifetime.
113  
        the execution context for their lifetime.
114  

114  

115  
        Derived classes must implement the pure virtual `shutdown()` member
115  
        Derived classes must implement the pure virtual `shutdown()` member
116  
        function, which is called when the owning execution context is
116  
        function, which is called when the owning execution context is
117  
        being destroyed. The `shutdown()` function should release resources
117  
        being destroyed. The `shutdown()` function should release resources
118  
        and cancel outstanding operations without blocking.
118  
        and cancel outstanding operations without blocking.
119  

119  

120  
        @par Deriving from service
120  
        @par Deriving from service
121  
        @li Implement `shutdown()` to perform cleanup.
121  
        @li Implement `shutdown()` to perform cleanup.
122  
        @li Accept `execution_context&` as the first constructor parameter.
122  
        @li Accept `execution_context&` as the first constructor parameter.
123  
        @li Optionally define `key_type` to enable base-class lookup.
123  
        @li Optionally define `key_type` to enable base-class lookup.
124  

124  

125  
        @par Example
125  
        @par Example
126  
        @code
126  
        @code
127  
        struct my_service : execution_context::service
127  
        struct my_service : execution_context::service
128  
        {
128  
        {
129  
            explicit my_service(execution_context&) {}
129  
            explicit my_service(execution_context&) {}
130  

130  

131  
        protected:
131  
        protected:
132  
            void shutdown() override
132  
            void shutdown() override
133  
            {
133  
            {
134  
                // Cancel pending operations, release resources
134  
                // Cancel pending operations, release resources
135  
            }
135  
            }
136  
        };
136  
        };
137  
        @endcode
137  
        @endcode
138  

138  

139  
        @see execution_context
139  
        @see execution_context
140  
    */
140  
    */
141  
    class BOOST_CAPY_DECL
141  
    class BOOST_CAPY_DECL
142  
        service
142  
        service
143  
    {
143  
    {
144  
    public:
144  
    public:
145  
        virtual ~service() = default;
145  
        virtual ~service() = default;
146  

146  

147  
    protected:
147  
    protected:
148  
        service() = default;
148  
        service() = default;
149  

149  

150  
        /** Called when the owning execution context shuts down.
150  
        /** Called when the owning execution context shuts down.
151  

151  

152  
            Implementations should release resources and cancel any
152  
            Implementations should release resources and cancel any
153  
            outstanding asynchronous operations. This function must
153  
            outstanding asynchronous operations. This function must
154  
            not block and must not throw exceptions. Services are
154  
            not block and must not throw exceptions. Services are
155  
            shut down in reverse order of creation.
155  
            shut down in reverse order of creation.
156  

156  

157  
            @par Exception Safety
157  
            @par Exception Safety
158  
            No-throw guarantee.
158  
            No-throw guarantee.
159  
        */
159  
        */
160  
        virtual void shutdown() = 0;
160  
        virtual void shutdown() = 0;
161  

161  

162  
    private:
162  
    private:
163  
        friend class execution_context;
163  
        friend class execution_context;
164  

164  

165  
        service* next_ = nullptr;
165  
        service* next_ = nullptr;
166  

166  

167  
// warning C4251: 'std::type_index' needs to have dll-interface
167  
// warning C4251: 'std::type_index' needs to have dll-interface
168  
        BOOST_CAPY_MSVC_WARNING_PUSH
168  
        BOOST_CAPY_MSVC_WARNING_PUSH
169  
        BOOST_CAPY_MSVC_WARNING_DISABLE(4251)
169  
        BOOST_CAPY_MSVC_WARNING_DISABLE(4251)
170  
        detail::type_index t0_{detail::type_id<void>()};
170  
        detail::type_index t0_{detail::type_id<void>()};
171  
        detail::type_index t1_{detail::type_id<void>()};
171  
        detail::type_index t1_{detail::type_id<void>()};
172  
        BOOST_CAPY_MSVC_WARNING_POP
172  
        BOOST_CAPY_MSVC_WARNING_POP
173  
    };
173  
    };
174  

174  

175  
    //------------------------------------------------
175  
    //------------------------------------------------
176  

176  

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

178  

179  
    execution_context& operator=(execution_context const&) = delete;
179  
    execution_context& operator=(execution_context const&) = delete;
180  

180  

181  
    /** Destructor.
181  
    /** Destructor.
182  

182  

183  
        Calls `shutdown()` then `destroy()` to clean up all services.
183  
        Calls `shutdown()` then `destroy()` to clean up all services.
184  

184  

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

188  

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

193  

194  
    /** Construct a default instance.
194  
    /** Construct a default instance.
195  

195  

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

200  

201  
    /** Return true if a service of type T exists.
201  
    /** Return true if a service of type T exists.
202  

202  

203  
        @par Thread Safety
203  
        @par Thread Safety
204  
        Thread-safe.
204  
        Thread-safe.
205  

205  

206  
        @tparam T The type of service to check.
206  
        @tparam T The type of service to check.
207  

207  

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

215  

216  
    /** Return a pointer to the service of type T, or nullptr.
216  
    /** Return a pointer to the service of type T, or nullptr.
217  

217  

218  
        @par Thread Safety
218  
        @par Thread Safety
219  
        Thread-safe.
219  
        Thread-safe.
220  

220  

221  
        @tparam T The type of service to find.
221  
        @tparam T The type of service to find.
222  

222  

223  
        @return A pointer to the service, or `nullptr` if not present.
223  
        @return A pointer to the service, or `nullptr` if not present.
224  
    */
224  
    */
225  
    template<class T>
225  
    template<class T>
226  
    T* find_service() const noexcept
226  
    T* find_service() const noexcept
227  
    {
227  
    {
228  
        auto id = detail::service_slot<T>();
228  
        auto id = detail::service_slot<T>();
229  
        if(id < max_service_slots)
229  
        if(id < max_service_slots)
230  
        {
230  
        {
231  
            auto* p = slots_[id].load(
231  
            auto* p = slots_[id].load(
232  
                std::memory_order_acquire);
232  
                std::memory_order_acquire);
233  
            if(p)
233  
            if(p)
234  
                return static_cast<T*>(p);
234  
                return static_cast<T*>(p);
235  
        }
235  
        }
236  
        std::lock_guard<std::mutex> lock(mutex_);
236  
        std::lock_guard<std::mutex> lock(mutex_);
237  
        return static_cast<T*>(find_impl(detail::type_id<T>()));
237  
        return static_cast<T*>(find_impl(detail::type_id<T>()));
238  
    }
238  
    }
239  

239  

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

241  

242  
        If no service of type T exists, one is created by calling
242  
        If no service of type T exists, one is created by calling
243  
        `T(execution_context&)`. If T has a nested `key_type`, the
243  
        `T(execution_context&)`. If T has a nested `key_type`, the
244  
        service is also indexed under that type.
244  
        service is also indexed under that type.
245  

245  

246  
        @par Constraints
246  
        @par Constraints
247  
        @li `T` must derive from `service`.
247  
        @li `T` must derive from `service`.
248  
        @li `T` must be constructible from `execution_context&`.
248  
        @li `T` must be constructible from `execution_context&`.
249  

249  

250  
        @par Exception Safety
250  
        @par Exception Safety
251  
        Strong guarantee. If service creation throws, the container
251  
        Strong guarantee. If service creation throws, the container
252  
        is unchanged.
252  
        is unchanged.
253  

253  

254  
        @par Thread Safety
254  
        @par Thread Safety
255  
        Thread-safe.
255  
        Thread-safe.
256  

256  

257  
        @tparam T The type of service to retrieve or create.
257  
        @tparam T The type of service to retrieve or create.
258  

258  

259  
        @return A reference to the service.
259  
        @return A reference to the service.
260  
    */
260  
    */
261  
    template<class T>
261  
    template<class T>
262  
    T& use_service()
262  
    T& use_service()
263  
    {
263  
    {
264  
        static_assert(std::is_base_of<service, T>::value,
264  
        static_assert(std::is_base_of<service, T>::value,
265  
            "T must derive from service");
265  
            "T must derive from service");
266  
        static_assert(std::is_constructible<T, execution_context&>::value,
266  
        static_assert(std::is_constructible<T, execution_context&>::value,
267  
            "T must be constructible from execution_context&");
267  
            "T must be constructible from execution_context&");
268  
        if constexpr(get_key<T>::value)
268  
        if constexpr(get_key<T>::value)
269  
        {
269  
        {
270  
            static_assert(
270  
            static_assert(
271  
                std::is_convertible<T&, typename get_key<T>::type&>::value,
271  
                std::is_convertible<T&, typename get_key<T>::type&>::value,
272  
                "T& must be convertible to key_type&");
272  
                "T& must be convertible to key_type&");
273  
        }
273  
        }
274  

274  

275  
        // Fast path: O(1) slot lookup
275  
        // Fast path: O(1) slot lookup
276  
        {
276  
        {
277  
            auto id = detail::service_slot<T>();
277  
            auto id = detail::service_slot<T>();
278  
            if(id < max_service_slots)
278  
            if(id < max_service_slots)
279  
            {
279  
            {
280  
                auto* p = slots_[id].load(
280  
                auto* p = slots_[id].load(
281  
                    std::memory_order_acquire);
281  
                    std::memory_order_acquire);
282  
                if(p)
282  
                if(p)
283  
                    return static_cast<T&>(*p);
283  
                    return static_cast<T&>(*p);
284  
            }
284  
            }
285  
        }
285  
        }
286  

286  

287  
        struct impl : factory
287  
        struct impl : factory
288  
        {
288  
        {
289  
            impl()
289  
            impl()
290  
                : factory(
290  
                : factory(
291  
                    detail::type_id<T>(),
291  
                    detail::type_id<T>(),
292  
                    get_key<T>::value
292  
                    get_key<T>::value
293  
                        ? detail::type_id<typename get_key<T>::type>()
293  
                        ? detail::type_id<typename get_key<T>::type>()
294  
                        : detail::type_id<T>(),
294  
                        : detail::type_id<T>(),
295  
                    detail::service_slot<T>(),
295  
                    detail::service_slot<T>(),
296  
                    get_key<T>::value
296  
                    get_key<T>::value
297  
                        ? detail::service_slot<typename get_key<T>::type>()
297  
                        ? detail::service_slot<typename get_key<T>::type>()
298  
                        : detail::service_slot<T>())
298  
                        : detail::service_slot<T>())
299  
            {
299  
            {
300  
            }
300  
            }
301  

301  

302  
            service* create(execution_context& ctx) override
302  
            service* create(execution_context& ctx) override
303  
            {
303  
            {
304  
                return new T(ctx);
304  
                return new T(ctx);
305  
            }
305  
            }
306  
        };
306  
        };
307  

307  

308  
        impl f;
308  
        impl f;
309  
        return static_cast<T&>(use_service_impl(f));
309  
        return static_cast<T&>(use_service_impl(f));
310  
    }
310  
    }
311  

311  

312  
    /** Construct and add a service.
312  
    /** Construct and add a service.
313  

313  

314  
        A new service of type T is constructed using the provided
314  
        A new service of type T is constructed using the provided
315  
        arguments and added to the container. If T has a nested
315  
        arguments and added to the container. If T has a nested
316  
        `key_type`, the service is also indexed under that type.
316  
        `key_type`, the service is also indexed under that type.
317  

317  

318  
        @par Constraints
318  
        @par Constraints
319  
        @li `T` must derive from `service`.
319  
        @li `T` must derive from `service`.
320  
        @li `T` must be constructible from `execution_context&, Args...`.
320  
        @li `T` must be constructible from `execution_context&, Args...`.
321  
        @li If `T::key_type` exists, `T&` must be convertible to `key_type&`.
321  
        @li If `T::key_type` exists, `T&` must be convertible to `key_type&`.
322  

322  

323  
        @par Exception Safety
323  
        @par Exception Safety
324  
        Strong guarantee. If service creation throws, the container
324  
        Strong guarantee. If service creation throws, the container
325  
        is unchanged.
325  
        is unchanged.
326  

326  

327  
        @par Thread Safety
327  
        @par Thread Safety
328  
        Thread-safe.
328  
        Thread-safe.
329  

329  

330  
        @throws std::invalid_argument if a service of the same type
330  
        @throws std::invalid_argument if a service of the same type
331  
            or `key_type` already exists.
331  
            or `key_type` already exists.
332  

332  

333  
        @tparam T The type of service to create.
333  
        @tparam T The type of service to create.
334  

334  

335  
        @param args Arguments forwarded to the constructor of T.
335  
        @param args Arguments forwarded to the constructor of T.
336  

336  

337  
        @return A reference to the created service.
337  
        @return A reference to the created service.
338  
    */
338  
    */
339  
    template<class T, class... Args>
339  
    template<class T, class... Args>
340  
    T& make_service(Args&&... args)
340  
    T& make_service(Args&&... args)
341  
    {
341  
    {
342  
        static_assert(std::is_base_of<service, T>::value,
342  
        static_assert(std::is_base_of<service, T>::value,
343  
            "T must derive from service");
343  
            "T must derive from service");
344  
        if constexpr(get_key<T>::value)
344  
        if constexpr(get_key<T>::value)
345  
        {
345  
        {
346  
            static_assert(
346  
            static_assert(
347  
                std::is_convertible<T&, typename get_key<T>::type&>::value,
347  
                std::is_convertible<T&, typename get_key<T>::type&>::value,
348  
                "T& must be convertible to key_type&");
348  
                "T& must be convertible to key_type&");
349  
        }
349  
        }
350  

350  

351  
        struct impl : factory
351  
        struct impl : factory
352  
        {
352  
        {
353  
            std::tuple<Args&&...> args_;
353  
            std::tuple<Args&&...> args_;
354  

354  

355  
            explicit impl(Args&&... a)
355  
            explicit impl(Args&&... a)
356  
                : factory(
356  
                : factory(
357  
                    detail::type_id<T>(),
357  
                    detail::type_id<T>(),
358  
                    get_key<T>::value
358  
                    get_key<T>::value
359  
                        ? detail::type_id<typename get_key<T>::type>()
359  
                        ? detail::type_id<typename get_key<T>::type>()
360  
                        : detail::type_id<T>(),
360  
                        : detail::type_id<T>(),
361  
                    detail::service_slot<T>(),
361  
                    detail::service_slot<T>(),
362  
                    get_key<T>::value
362  
                    get_key<T>::value
363  
                        ? detail::service_slot<typename get_key<T>::type>()
363  
                        ? detail::service_slot<typename get_key<T>::type>()
364  
                        : detail::service_slot<T>())
364  
                        : detail::service_slot<T>())
365  
                , args_(std::forward<Args>(a)...)
365  
                , args_(std::forward<Args>(a)...)
366  
            {
366  
            {
367  
            }
367  
            }
368  

368  

369  
            service* create(execution_context& ctx) override
369  
            service* create(execution_context& ctx) override
370  
            {
370  
            {
371  
                return std::apply([&ctx](auto&&... a) {
371  
                return std::apply([&ctx](auto&&... a) {
372  
                    return new T(ctx, std::forward<decltype(a)>(a)...);
372  
                    return new T(ctx, std::forward<decltype(a)>(a)...);
373  
                }, std::move(args_));
373  
                }, std::move(args_));
374  
            }
374  
            }
375  
        };
375  
        };
376  

376  

377  
        impl f(std::forward<Args>(args)...);
377  
        impl f(std::forward<Args>(args)...);
378  
        return static_cast<T&>(make_service_impl(f));
378  
        return static_cast<T&>(make_service_impl(f));
379  
    }
379  
    }
380  

380  

381  
    //------------------------------------------------
381  
    //------------------------------------------------
382  

382  

383  
    /** Return the memory resource used for coroutine frame allocation.
383  
    /** Return the memory resource used for coroutine frame allocation.
384  

384  

385  
        The returned pointer is valid for the lifetime of this context.
385  
        The returned pointer is valid for the lifetime of this context.
386  
        By default, this returns a pointer to the recycling memory
386  
        By default, this returns a pointer to the recycling memory
387  
        resource which pools frame allocations for reuse.
387  
        resource which pools frame allocations for reuse.
388  

388  

389  
        @return Pointer to the frame allocator.
389  
        @return Pointer to the frame allocator.
390  

390  

391  
        @see set_frame_allocator
391  
        @see set_frame_allocator
392  
    */
392  
    */
393  
    std::pmr::memory_resource*
393  
    std::pmr::memory_resource*
394  
    get_frame_allocator() const noexcept
394  
    get_frame_allocator() const noexcept
395  
    {
395  
    {
396  
        return frame_alloc_;
396  
        return frame_alloc_;
397  
    }
397  
    }
398  

398  

399  
    /** Set the memory resource used for coroutine frame allocation.
399  
    /** Set the memory resource used for coroutine frame allocation.
400  

400  

401  
        The caller is responsible for ensuring the memory resource
401  
        The caller is responsible for ensuring the memory resource
402  
        remains valid for the lifetime of all coroutines launched
402  
        remains valid for the lifetime of all coroutines launched
403  
        using this context's executor.
403  
        using this context's executor.
404  

404  

405  
        @par Thread Safety
405  
        @par Thread Safety
406  
        Not thread-safe. Must not be called while any thread may
406  
        Not thread-safe. Must not be called while any thread may
407  
        be referencing this execution context or its executor.
407  
        be referencing this execution context or its executor.
408  

408  

409  
        @param mr Pointer to the memory resource.
409  
        @param mr Pointer to the memory resource.
410  

410  

411  
        @see get_frame_allocator
411  
        @see get_frame_allocator
412  
    */
412  
    */
413  
    void
413  
    void
414  
    set_frame_allocator(std::pmr::memory_resource* mr) noexcept
414  
    set_frame_allocator(std::pmr::memory_resource* mr) noexcept
415  
    {
415  
    {
416  
        owned_.reset();
416  
        owned_.reset();
417  
        frame_alloc_ = mr;
417  
        frame_alloc_ = mr;
418  
    }
418  
    }
419  

419  

420  
    /** Set the frame allocator from a standard Allocator.
420  
    /** Set the frame allocator from a standard Allocator.
421  

421  

422  
        The allocator is wrapped in an internal memory resource
422  
        The allocator is wrapped in an internal memory resource
423  
        adapter owned by this context. The wrapper remains valid
423  
        adapter owned by this context. The wrapper remains valid
424  
        for the lifetime of this context or until a subsequent
424  
        for the lifetime of this context or until a subsequent
425  
        call to set_frame_allocator.
425  
        call to set_frame_allocator.
426  

426  

427  
        @par Thread Safety
427  
        @par Thread Safety
428  
        Not thread-safe. Must not be called while any thread may
428  
        Not thread-safe. Must not be called while any thread may
429  
        be referencing this execution context or its executor.
429  
        be referencing this execution context or its executor.
430  

430  

431  
        @tparam Allocator The allocator type satisfying the
431  
        @tparam Allocator The allocator type satisfying the
432  
            standard Allocator requirements.
432  
            standard Allocator requirements.
433  

433  

434  
        @param a The allocator to use.
434  
        @param a The allocator to use.
435  

435  

436  
        @see get_frame_allocator
436  
        @see get_frame_allocator
437  
    */
437  
    */
438  
    template<class Allocator>
438  
    template<class Allocator>
439  
        requires (!std::is_pointer_v<Allocator>)
439  
        requires (!std::is_pointer_v<Allocator>)
440  
    void
440  
    void
441  
    set_frame_allocator(Allocator const& a)
441  
    set_frame_allocator(Allocator const& a)
442  
    {
442  
    {
443  
        static_assert(
443  
        static_assert(
444  
            requires { typename std::allocator_traits<Allocator>::value_type; },
444  
            requires { typename std::allocator_traits<Allocator>::value_type; },
445  
            "Allocator must satisfy allocator requirements");
445  
            "Allocator must satisfy allocator requirements");
446  
        static_assert(
446  
        static_assert(
447  
            std::is_copy_constructible_v<Allocator>,
447  
            std::is_copy_constructible_v<Allocator>,
448  
            "Allocator must be copy constructible");
448  
            "Allocator must be copy constructible");
449  

449  

450  
        auto p = std::make_shared<
450  
        auto p = std::make_shared<
451  
            detail::frame_memory_resource<Allocator>>(a);
451  
            detail::frame_memory_resource<Allocator>>(a);
452  
        frame_alloc_ = p.get();
452  
        frame_alloc_ = p.get();
453  
        owned_ = std::move(p);
453  
        owned_ = std::move(p);
454  
    }
454  
    }
455  

455  

456  
    /** Return a pointer to this context if it matches the
456  
    /** Return a pointer to this context if it matches the
457  
        requested type.
457  
        requested type.
458  

458  

459  
        Performs a type check and downcasts `this` when the
459  
        Performs a type check and downcasts `this` when the
460  
        types match, or returns `nullptr` otherwise. Analogous
460  
        types match, or returns `nullptr` otherwise. Analogous
461  
        to `std::any_cast< ExecutionContext >( &a )`.
461  
        to `std::any_cast< ExecutionContext >( &a )`.
462  

462  

463  
        @tparam ExecutionContext The derived context type to
463  
        @tparam ExecutionContext The derived context type to
464  
            retrieve.
464  
            retrieve.
465  

465  

466  
        @return A pointer to this context as the requested
466  
        @return A pointer to this context as the requested
467  
            type, or `nullptr` if the type does not match.
467  
            type, or `nullptr` if the type does not match.
468  
    */
468  
    */
469  
    template< typename ExecutionContext >
469  
    template< typename ExecutionContext >
470  
    const ExecutionContext* target() const
470  
    const ExecutionContext* target() const
471  
    {
471  
    {
472  
        if ( ti_ && *ti_ == detail::type_id< ExecutionContext >() )
472  
        if ( ti_ && *ti_ == detail::type_id< ExecutionContext >() )
473  
           return static_cast< ExecutionContext const* >( this );
473  
           return static_cast< ExecutionContext const* >( this );
474  
        return nullptr;
474  
        return nullptr;
475  
    }
475  
    }
476  

476  

477  
    /// @copydoc target() const
477  
    /// @copydoc target() const
478  
    template< typename ExecutionContext >
478  
    template< typename ExecutionContext >
479  
    ExecutionContext* target()
479  
    ExecutionContext* target()
480  
    {
480  
    {
481  
        if ( ti_ && *ti_ == detail::type_id< ExecutionContext >() )
481  
        if ( ti_ && *ti_ == detail::type_id< ExecutionContext >() )
482  
           return static_cast< ExecutionContext* >( this );
482  
           return static_cast< ExecutionContext* >( this );
483  
        return nullptr;
483  
        return nullptr;
484  
    }
484  
    }
485  

485  

486  
protected:
486  
protected:
487  
    /** Shut down all services.
487  
    /** Shut down all services.
488  

488  

489  
        Calls `shutdown()` on each service in reverse order of creation.
489  
        Calls `shutdown()` on each service in reverse order of creation.
490  
        After this call, services remain allocated but are in a stopped
490  
        After this call, services remain allocated but are in a stopped
491  
        state. Derived classes should call this in their destructor
491  
        state. Derived classes should call this in their destructor
492  
        before any members are destroyed. This function is idempotent;
492  
        before any members are destroyed. This function is idempotent;
493  
        subsequent calls have no effect.
493  
        subsequent calls have no effect.
494  

494  

495  
        @par Effects
495  
        @par Effects
496  
        Each service's `shutdown()` member function is invoked once.
496  
        Each service's `shutdown()` member function is invoked once.
497  

497  

498  
        @par Postconditions
498  
        @par Postconditions
499  
        @li All services are in a stopped state.
499  
        @li All services are in a stopped state.
500  

500  

501  
        @par Exception Safety
501  
        @par Exception Safety
502  
        No-throw guarantee.
502  
        No-throw guarantee.
503  

503  

504  
        @par Thread Safety
504  
        @par Thread Safety
505  
        Not thread-safe. Must not be called concurrently with other
505  
        Not thread-safe. Must not be called concurrently with other
506  
        operations on this execution_context.
506  
        operations on this execution_context.
507  
    */
507  
    */
508  
    void shutdown() noexcept;
508  
    void shutdown() noexcept;
509  

509  

510  
    /** Destroy all services.
510  
    /** Destroy all services.
511  

511  

512  
        Deletes all services in reverse order of creation. Derived
512  
        Deletes all services in reverse order of creation. Derived
513  
        classes should call this as the final step of destruction.
513  
        classes should call this as the final step of destruction.
514  
        This function is idempotent; subsequent calls have no effect.
514  
        This function is idempotent; subsequent calls have no effect.
515  

515  

516  
        @par Preconditions
516  
        @par Preconditions
517  
        @li `shutdown()` has been called.
517  
        @li `shutdown()` has been called.
518  

518  

519  
        @par Effects
519  
        @par Effects
520  
        All services are deleted and removed from the container.
520  
        All services are deleted and removed from the container.
521  

521  

522  
        @par Postconditions
522  
        @par Postconditions
523  
        @li The service container is empty.
523  
        @li The service container is empty.
524  

524  

525  
        @par Exception Safety
525  
        @par Exception Safety
526  
        No-throw guarantee.
526  
        No-throw guarantee.
527  

527  

528  
        @par Thread Safety
528  
        @par Thread Safety
529  
        Not thread-safe. Must not be called concurrently with other
529  
        Not thread-safe. Must not be called concurrently with other
530  
        operations on this execution_context.
530  
        operations on this execution_context.
531  
    */
531  
    */
532  
    void destroy() noexcept;
532  
    void destroy() noexcept;
533  

533  

534  
private:
534  
private:
535  
    struct BOOST_CAPY_DECL
535  
    struct BOOST_CAPY_DECL
536  
        factory
536  
        factory
537  
    {
537  
    {
538  
// warning C4251: 'std::type_index' needs to have dll-interface
538  
// warning C4251: 'std::type_index' needs to have dll-interface
539  
        BOOST_CAPY_MSVC_WARNING_PUSH
539  
        BOOST_CAPY_MSVC_WARNING_PUSH
540  
        BOOST_CAPY_MSVC_WARNING_DISABLE(4251)
540  
        BOOST_CAPY_MSVC_WARNING_DISABLE(4251)
541  
        detail::type_index t0;
541  
        detail::type_index t0;
542  
        detail::type_index t1;
542  
        detail::type_index t1;
543  
        BOOST_CAPY_MSVC_WARNING_POP
543  
        BOOST_CAPY_MSVC_WARNING_POP
544  
        std::size_t slot0;
544  
        std::size_t slot0;
545  
        std::size_t slot1;
545  
        std::size_t slot1;
546  

546  

547  
        factory(
547  
        factory(
548  
            detail::type_info const& t0_,
548  
            detail::type_info const& t0_,
549  
            detail::type_info const& t1_,
549  
            detail::type_info const& t1_,
550  
            std::size_t s0,
550  
            std::size_t s0,
551  
            std::size_t s1)
551  
            std::size_t s1)
552  
            : t0(t0_), t1(t1_)
552  
            : t0(t0_), t1(t1_)
553  
            , slot0(s0), slot1(s1)
553  
            , slot0(s0), slot1(s1)
554  
        {
554  
        {
555  
        }
555  
        }
556  

556  

557  
        virtual service* create(execution_context&) = 0;
557  
        virtual service* create(execution_context&) = 0;
558  

558  

559  
    protected:
559  
    protected:
560  
        ~factory() = default;
560  
        ~factory() = default;
561  
    };
561  
    };
562  

562  

563  
    service* find_impl(detail::type_index ti) const noexcept;
563  
    service* find_impl(detail::type_index ti) const noexcept;
564  
    service& use_service_impl(factory& f);
564  
    service& use_service_impl(factory& f);
565  
    service& make_service_impl(factory& f);
565  
    service& make_service_impl(factory& f);
566  

566  

567  
// warning C4251: std::mutex, std::shared_ptr, std::atomic need dll-interface
567  
// warning C4251: std::mutex, std::shared_ptr, std::atomic need dll-interface
568  
    BOOST_CAPY_MSVC_WARNING_PUSH
568  
    BOOST_CAPY_MSVC_WARNING_PUSH
569  
    BOOST_CAPY_MSVC_WARNING_DISABLE(4251)
569  
    BOOST_CAPY_MSVC_WARNING_DISABLE(4251)
570  
    mutable std::mutex mutex_;
570  
    mutable std::mutex mutex_;
571  
    std::shared_ptr<void> owned_;
571  
    std::shared_ptr<void> owned_;
572  
    BOOST_CAPY_MSVC_WARNING_POP
572  
    BOOST_CAPY_MSVC_WARNING_POP
573  
    std::pmr::memory_resource* frame_alloc_ = nullptr;
573  
    std::pmr::memory_resource* frame_alloc_ = nullptr;
574  
    service* head_ = nullptr;
574  
    service* head_ = nullptr;
575  
    bool shutdown_ = false;
575  
    bool shutdown_ = false;
576  

576  

577  
    static constexpr std::size_t max_service_slots = 32;
577  
    static constexpr std::size_t max_service_slots = 32;
578  
    BOOST_CAPY_MSVC_WARNING_PUSH
578  
    BOOST_CAPY_MSVC_WARNING_PUSH
579  
    BOOST_CAPY_MSVC_WARNING_DISABLE(4251)
579  
    BOOST_CAPY_MSVC_WARNING_DISABLE(4251)
580  
    std::atomic<service*> slots_[max_service_slots] = {};
580  
    std::atomic<service*> slots_[max_service_slots] = {};
581  
    BOOST_CAPY_MSVC_WARNING_POP
581  
    BOOST_CAPY_MSVC_WARNING_POP
582  
};
582  
};
583  

583  

584  
template< typename Derived >
584  
template< typename Derived >
585  
execution_context::
585  
execution_context::
586  
execution_context( Derived* ) noexcept
586  
execution_context( Derived* ) noexcept
587  
    : execution_context()
587  
    : execution_context()
588  
{
588  
{
589  
    ti_ = &detail::type_id< Derived >();
589  
    ti_ = &detail::type_id< Derived >();
590  
}
590  
}
591  

591  

592  
} // namespace capy
592  
} // namespace capy
593  
} // namespace boost
593  
} // namespace boost
594  

594  

595  
#endif
595  
#endif