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

26  

25  
namespace boost {
27  
namespace boost {
26  
namespace capy {
28  
namespace capy {
27  

29  

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

31  

30  
    An execution context represents a place where function objects are
32  
    An execution context represents a place where function objects are
31  
    executed. It provides a service registry where polymorphic services
33  
    executed. It provides a service registry where polymorphic services
32  
    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
33  
    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
34  
    lookup by a base class type.
36  
    lookup by a base class type.
35  

37  

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

42  

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

48  

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

53  

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

61  

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

65  

64  
        explicit posix_file_service(execution_context&) {}
66  
        explicit posix_file_service(execution_context&) {}
65  
    };
67  
    };
66  

68  

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

78  

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

84  

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

91  

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

95  

94  
    template<class T>
96  
    template<class T>
95  
    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
96  
    {
98  
    {
97  
        using type = typename T::key_type;
99  
        using type = typename T::key_type;
98  
    };
100  
    };
99  
protected:
101  
protected:
100  
    template< typename Derived >
102  
    template< typename Derived >
101  
    explicit execution_context( Derived* ) noexcept;
103  
    explicit execution_context( Derived* ) noexcept;
102  

104  

103  
public:
105  
public:
104  
    //------------------------------------------------
106  
    //------------------------------------------------
105  

107  

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

109  

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

114  

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

119  

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

124  

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

130  

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

138  

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

146  

145  
    protected:
147  
    protected:
146  
        service() = default;
148  
        service() = default;
147  

149  

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

151  

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

156  

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

161  

160  
    private:
162  
    private:
161  
        friend class execution_context;
163  
        friend class execution_context;
162  

164  

163  
        service* next_ = nullptr;
165  
        service* next_ = nullptr;
164  

166  

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

174  

173  
    //------------------------------------------------
175  
    //------------------------------------------------
174  

176  

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

178  

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

180  

179  
    /** Destructor.
181  
    /** Destructor.
180  

182  

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

184  

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

188  

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

193  

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

195  

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

200  

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

202  

201  
        @par Thread Safety
203  
        @par Thread Safety
202  
        Thread-safe.
204  
        Thread-safe.
203  

205  

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

207  

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

215  

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

217  

216  
        @par Thread Safety
218  
        @par Thread Safety
217  
        Thread-safe.
219  
        Thread-safe.
218  

220  

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

222  

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

239  

230  
    /** 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.
231  

241  

232  
        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
233  
        `T(execution_context&)`. If T has a nested `key_type`, the
243  
        `T(execution_context&)`. If T has a nested `key_type`, the
234  
        service is also indexed under that type.
244  
        service is also indexed under that type.
235  

245  

236  
        @par Constraints
246  
        @par Constraints
237  
        @li `T` must derive from `service`.
247  
        @li `T` must derive from `service`.
238  
        @li `T` must be constructible from `execution_context&`.
248  
        @li `T` must be constructible from `execution_context&`.
239  

249  

240  
        @par Exception Safety
250  
        @par Exception Safety
241  
        Strong guarantee. If service creation throws, the container
251  
        Strong guarantee. If service creation throws, the container
242  
        is unchanged.
252  
        is unchanged.
243  

253  

244  
        @par Thread Safety
254  
        @par Thread Safety
245  
        Thread-safe.
255  
        Thread-safe.
246  

256  

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

258  

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

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

286  

259  
        struct impl : factory
287  
        struct impl : factory
260  
        {
288  
        {
261  
            impl()
289  
            impl()
262  
                : factory(
290  
                : factory(
263  
                    detail::type_id<T>(),
291  
                    detail::type_id<T>(),
264  
                    get_key<T>::value
292  
                    get_key<T>::value
265  
                        ? detail::type_id<typename get_key<T>::type>()
293  
                        ? detail::type_id<typename get_key<T>::type>()
266 -
                        : detail::type_id<T>())
294 +
                        : detail::type_id<T>(),
 
295 +
                    detail::service_slot<T>(),
 
296 +
                    get_key<T>::value
 
297 +
                        ? detail::service_slot<typename get_key<T>::type>()
 
298 +
                        : detail::service_slot<T>())
267  
            {
299  
            {
268  
            }
300  
            }
269  

301  

270  
            service* create(execution_context& ctx) override
302  
            service* create(execution_context& ctx) override
271  
            {
303  
            {
272  
                return new T(ctx);
304  
                return new T(ctx);
273  
            }
305  
            }
274  
        };
306  
        };
275  

307  

276  
        impl f;
308  
        impl f;
277  
        return static_cast<T&>(use_service_impl(f));
309  
        return static_cast<T&>(use_service_impl(f));
278  
    }
310  
    }
279  

311  

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

313  

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

317  

286  
        @par Constraints
318  
        @par Constraints
287  
        @li `T` must derive from `service`.
319  
        @li `T` must derive from `service`.
288  
        @li `T` must be constructible from `execution_context&, Args...`.
320  
        @li `T` must be constructible from `execution_context&, Args...`.
289  
        @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&`.
290  

322  

291  
        @par Exception Safety
323  
        @par Exception Safety
292  
        Strong guarantee. If service creation throws, the container
324  
        Strong guarantee. If service creation throws, the container
293  
        is unchanged.
325  
        is unchanged.
294  

326  

295  
        @par Thread Safety
327  
        @par Thread Safety
296  
        Thread-safe.
328  
        Thread-safe.
297  

329  

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

332  

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

334  

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

336  

305  
        @return A reference to the created service.
337  
        @return A reference to the created service.
306  
    */
338  
    */
307  
    template<class T, class... Args>
339  
    template<class T, class... Args>
308  
    T& make_service(Args&&... args)
340  
    T& make_service(Args&&... args)
309  
    {
341  
    {
310  
        static_assert(std::is_base_of<service, T>::value,
342  
        static_assert(std::is_base_of<service, T>::value,
311  
            "T must derive from service");
343  
            "T must derive from service");
312  
        if constexpr(get_key<T>::value)
344  
        if constexpr(get_key<T>::value)
313  
        {
345  
        {
314  
            static_assert(
346  
            static_assert(
315  
                std::is_convertible<T&, typename get_key<T>::type&>::value,
347  
                std::is_convertible<T&, typename get_key<T>::type&>::value,
316  
                "T& must be convertible to key_type&");
348  
                "T& must be convertible to key_type&");
317  
        }
349  
        }
318  

350  

319  
        struct impl : factory
351  
        struct impl : factory
320  
        {
352  
        {
321  
            std::tuple<Args&&...> args_;
353  
            std::tuple<Args&&...> args_;
322  

354  

323  
            explicit impl(Args&&... a)
355  
            explicit impl(Args&&... a)
324  
                : factory(
356  
                : factory(
325  
                    detail::type_id<T>(),
357  
                    detail::type_id<T>(),
326  
                    get_key<T>::value
358  
                    get_key<T>::value
327  
                        ? detail::type_id<typename get_key<T>::type>()
359  
                        ? detail::type_id<typename get_key<T>::type>()
328 -
                        : detail::type_id<T>())
360 +
                        : detail::type_id<T>(),
 
361 +
                    detail::service_slot<T>(),
 
362 +
                    get_key<T>::value
 
363 +
                        ? detail::service_slot<typename get_key<T>::type>()
 
364 +
                        : detail::service_slot<T>())
329  
                , args_(std::forward<Args>(a)...)
365  
                , args_(std::forward<Args>(a)...)
330  
            {
366  
            {
331  
            }
367  
            }
332  

368  

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

376  

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

380  

345  
    //------------------------------------------------
381  
    //------------------------------------------------
346  

382  

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

384  

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

388  

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

390  

355  
        @see set_frame_allocator
391  
        @see set_frame_allocator
356  
    */
392  
    */
357  
    std::pmr::memory_resource*
393  
    std::pmr::memory_resource*
358  
    get_frame_allocator() const noexcept
394  
    get_frame_allocator() const noexcept
359  
    {
395  
    {
360  
        return frame_alloc_;
396  
        return frame_alloc_;
361  
    }
397  
    }
362  

398  

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

400  

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

404  

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

408  

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

410  

375  
        @see get_frame_allocator
411  
        @see get_frame_allocator
376  
    */
412  
    */
377  
    void
413  
    void
378  
    set_frame_allocator(std::pmr::memory_resource* mr) noexcept
414  
    set_frame_allocator(std::pmr::memory_resource* mr) noexcept
379  
    {
415  
    {
380  
        owned_.reset();
416  
        owned_.reset();
381  
        frame_alloc_ = mr;
417  
        frame_alloc_ = mr;
382  
    }
418  
    }
383  

419  

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

421  

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

426  

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

430  

395  
        @tparam Allocator The allocator type satisfying the
431  
        @tparam Allocator The allocator type satisfying the
396  
            standard Allocator requirements.
432  
            standard Allocator requirements.
397  

433  

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

435  

400  
        @see get_frame_allocator
436  
        @see get_frame_allocator
401  
    */
437  
    */
402  
    template<class Allocator>
438  
    template<class Allocator>
403  
        requires (!std::is_pointer_v<Allocator>)
439  
        requires (!std::is_pointer_v<Allocator>)
404  
    void
440  
    void
405  
    set_frame_allocator(Allocator const& a)
441  
    set_frame_allocator(Allocator const& a)
406  
    {
442  
    {
407  
        static_assert(
443  
        static_assert(
408  
            requires { typename std::allocator_traits<Allocator>::value_type; },
444  
            requires { typename std::allocator_traits<Allocator>::value_type; },
409  
            "Allocator must satisfy allocator requirements");
445  
            "Allocator must satisfy allocator requirements");
410  
        static_assert(
446  
        static_assert(
411  
            std::is_copy_constructible_v<Allocator>,
447  
            std::is_copy_constructible_v<Allocator>,
412  
            "Allocator must be copy constructible");
448  
            "Allocator must be copy constructible");
413  

449  

414  
        auto p = std::make_shared<
450  
        auto p = std::make_shared<
415  
            detail::frame_memory_resource<Allocator>>(a);
451  
            detail::frame_memory_resource<Allocator>>(a);
416  
        frame_alloc_ = p.get();
452  
        frame_alloc_ = p.get();
417  
        owned_ = std::move(p);
453  
        owned_ = std::move(p);
418  
    }
454  
    }
419  

455  

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

458  

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

462  

427  
        @tparam ExecutionContext The derived context type to
463  
        @tparam ExecutionContext The derived context type to
428  
            retrieve.
464  
            retrieve.
429  

465  

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

476  

441  
    /// @copydoc target() const
477  
    /// @copydoc target() const
442  
    template< typename ExecutionContext >
478  
    template< typename ExecutionContext >
443  
    ExecutionContext* target()
479  
    ExecutionContext* target()
444  
    {
480  
    {
445  
        if ( ti_ && *ti_ == detail::type_id< ExecutionContext >() )
481  
        if ( ti_ && *ti_ == detail::type_id< ExecutionContext >() )
446  
           return static_cast< ExecutionContext* >( this );
482  
           return static_cast< ExecutionContext* >( this );
447  
        return nullptr;
483  
        return nullptr;
448  
    }
484  
    }
449  

485  

450  
protected:
486  
protected:
451  
    /** Shut down all services.
487  
    /** Shut down all services.
452  

488  

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

494  

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

497  

462  
        @par Postconditions
498  
        @par Postconditions
463  
        @li All services are in a stopped state.
499  
        @li All services are in a stopped state.
464  

500  

465  
        @par Exception Safety
501  
        @par Exception Safety
466  
        No-throw guarantee.
502  
        No-throw guarantee.
467  

503  

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

509  

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

511  

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

515  

480  
        @par Preconditions
516  
        @par Preconditions
481  
        @li `shutdown()` has been called.
517  
        @li `shutdown()` has been called.
482  

518  

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

521  

486  
        @par Postconditions
522  
        @par Postconditions
487  
        @li The service container is empty.
523  
        @li The service container is empty.
488  

524  

489  
        @par Exception Safety
525  
        @par Exception Safety
490  
        No-throw guarantee.
526  
        No-throw guarantee.
491  

527  

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

533  

498  
private:
534  
private:
499  
    struct BOOST_CAPY_DECL
535  
    struct BOOST_CAPY_DECL
500  
        factory
536  
        factory
501  
    {
537  
    {
502  
// warning C4251: 'std::type_index' needs to have dll-interface
538  
// warning C4251: 'std::type_index' needs to have dll-interface
503  
        BOOST_CAPY_MSVC_WARNING_PUSH
539  
        BOOST_CAPY_MSVC_WARNING_PUSH
504  
        BOOST_CAPY_MSVC_WARNING_DISABLE(4251)
540  
        BOOST_CAPY_MSVC_WARNING_DISABLE(4251)
505  
        detail::type_index t0;
541  
        detail::type_index t0;
506  
        detail::type_index t1;
542  
        detail::type_index t1;
507  
        BOOST_CAPY_MSVC_WARNING_POP
543  
        BOOST_CAPY_MSVC_WARNING_POP
 
544 +
        std::size_t slot0;
 
545 +
        std::size_t slot1;
508  

546  

509  
        factory(
547  
        factory(
510  
            detail::type_info const& t0_,
548  
            detail::type_info const& t0_,
511 -
            detail::type_info const& t1_)
549 +
            detail::type_info const& t1_,
 
550 +
            std::size_t s0,
 
551 +
            std::size_t s1)
512  
            : t0(t0_), t1(t1_)
552  
            : t0(t0_), t1(t1_)
 
553 +
            , slot0(s0), slot1(s1)
513  
        {
554  
        {
514  
        }
555  
        }
515  

556  

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

558  

518  
    protected:
559  
    protected:
519  
        ~factory() = default;
560  
        ~factory() = default;
520  
    };
561  
    };
521  

562  

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

566  

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

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

583  

537  
template< typename Derived >
584  
template< typename Derived >
538  
execution_context::
585  
execution_context::
539  
execution_context( Derived* ) noexcept
586  
execution_context( Derived* ) noexcept
540  
    : execution_context()
587  
    : execution_context()
541  
{
588  
{
542  
    ti_ = &detail::type_id< Derived >();
589  
    ti_ = &detail::type_id< Derived >();
543  
}
590  
}
544  

591  

545  
} // namespace capy
592  
} // namespace capy
546  
} // namespace boost
593  
} // namespace boost
547  

594  

548  
#endif
595  
#endif