1  
//
1  
//
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3  
// Copyright (c) 2026 Steve Gerbino
3  
// Copyright (c) 2026 Steve Gerbino
4  
// Copyright (c) 2026 Michael Vandeberg
4  
// Copyright (c) 2026 Michael Vandeberg
5  
//
5  
//
6  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
6  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
7  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
8  
//
8  
//
9  
// Official repository: https://github.com/cppalliance/corosio
9  
// Official repository: https://github.com/cppalliance/corosio
10  
//
10  
//
11  

11  

12  
#ifndef BOOST_COROSIO_IO_CONTEXT_HPP
12  
#ifndef BOOST_COROSIO_IO_CONTEXT_HPP
13  
#define BOOST_COROSIO_IO_CONTEXT_HPP
13  
#define BOOST_COROSIO_IO_CONTEXT_HPP
14  

14  

15  
#include <boost/corosio/detail/config.hpp>
15  
#include <boost/corosio/detail/config.hpp>
16  
#include <boost/corosio/detail/platform.hpp>
16  
#include <boost/corosio/detail/platform.hpp>
17  
#include <boost/corosio/detail/scheduler.hpp>
17  
#include <boost/corosio/detail/scheduler.hpp>
18  
#include <boost/capy/ex/execution_context.hpp>
18  
#include <boost/capy/ex/execution_context.hpp>
19  

19  

20  
#include <chrono>
20  
#include <chrono>
21  
#include <coroutine>
21  
#include <coroutine>
22  
#include <cstddef>
22  
#include <cstddef>
23  
#include <limits>
23  
#include <limits>
24  
#include <thread>
24  
#include <thread>
25  

25  

26  
namespace boost::corosio {
26  
namespace boost::corosio {
27  

27  

28  
namespace detail {
28  
namespace detail {
29  
struct timer_service_access;
29  
struct timer_service_access;
30  
} // namespace detail
30  
} // namespace detail
31  

31  

32  
/** An I/O context for running asynchronous operations.
32  
/** An I/O context for running asynchronous operations.
33  

33  

34  
    The io_context provides an execution environment for async
34  
    The io_context provides an execution environment for async
35  
    operations. It maintains a queue of pending work items and
35  
    operations. It maintains a queue of pending work items and
36  
    processes them when `run()` is called.
36  
    processes them when `run()` is called.
37  

37  

38  
    The default and unsigned constructors select the platform's
38  
    The default and unsigned constructors select the platform's
39  
    native backend:
39  
    native backend:
40  
    - Windows: IOCP
40  
    - Windows: IOCP
41  
    - Linux: epoll
41  
    - Linux: epoll
42  
    - BSD/macOS: kqueue
42  
    - BSD/macOS: kqueue
43  
    - Other POSIX: select
43  
    - Other POSIX: select
44  

44  

45  
    The template constructor accepts a backend tag value to
45  
    The template constructor accepts a backend tag value to
46  
    choose a specific backend at compile time:
46  
    choose a specific backend at compile time:
47  

47  

48  
    @par Example
48  
    @par Example
49  
    @code
49  
    @code
50  
    io_context ioc;                   // platform default
50  
    io_context ioc;                   // platform default
51  
    io_context ioc2(corosio::epoll);  // explicit backend
51  
    io_context ioc2(corosio::epoll);  // explicit backend
52  
    @endcode
52  
    @endcode
53  

53  

54  
    @par Thread Safety
54  
    @par Thread Safety
55  
    Distinct objects: Safe.@n
55  
    Distinct objects: Safe.@n
56  
    Shared objects: Safe, if using a concurrency hint greater
56  
    Shared objects: Safe, if using a concurrency hint greater
57  
    than 1.
57  
    than 1.
58  

58  

59  
    @see epoll_t, select_t, kqueue_t, iocp_t
59  
    @see epoll_t, select_t, kqueue_t, iocp_t
60  
*/
60  
*/
61  
class BOOST_COROSIO_DECL io_context : public capy::execution_context
61  
class BOOST_COROSIO_DECL io_context : public capy::execution_context
62  
{
62  
{
63  
    friend struct detail::timer_service_access;
63  
    friend struct detail::timer_service_access;
64  

64  

65  
protected:
65  
protected:
66  
    detail::scheduler* sched_;
66  
    detail::scheduler* sched_;
67  

67  

68  
public:
68  
public:
69  
    /** The executor type for this context. */
69  
    /** The executor type for this context. */
70  
    class executor_type;
70  
    class executor_type;
71  

71  

72  
    /** Construct with default concurrency and platform backend. */
72  
    /** Construct with default concurrency and platform backend. */
73  
    io_context();
73  
    io_context();
74  

74  

75  
    /** Construct with a concurrency hint and platform backend.
75  
    /** Construct with a concurrency hint and platform backend.
76  

76  

77  
        @param concurrency_hint Hint for the number of threads
77  
        @param concurrency_hint Hint for the number of threads
78  
            that will call `run()`.
78  
            that will call `run()`.
79  
    */
79  
    */
80  
    explicit io_context(unsigned concurrency_hint);
80  
    explicit io_context(unsigned concurrency_hint);
81  

81  

82  
    /** Construct with an explicit backend tag.
82  
    /** Construct with an explicit backend tag.
83  

83  

84  
        @param backend The backend tag value selecting the I/O
84  
        @param backend The backend tag value selecting the I/O
85  
            multiplexer (e.g. `corosio::epoll`).
85  
            multiplexer (e.g. `corosio::epoll`).
86  
        @param concurrency_hint Hint for the number of threads
86  
        @param concurrency_hint Hint for the number of threads
87  
            that will call `run()`.
87  
            that will call `run()`.
88  
    */
88  
    */
89  
    template<class Backend>
89  
    template<class Backend>
90  
        requires requires { Backend::construct; }
90  
        requires requires { Backend::construct; }
91  
    explicit io_context(
91  
    explicit io_context(
92  
        Backend backend,
92  
        Backend backend,
93  
        unsigned concurrency_hint = std::thread::hardware_concurrency())
93  
        unsigned concurrency_hint = std::thread::hardware_concurrency())
94  
        : capy::execution_context(this)
94  
        : capy::execution_context(this)
95  
        , sched_(nullptr)
95  
        , sched_(nullptr)
96  
    {
96  
    {
97  
        (void)backend;
97  
        (void)backend;
98  
        sched_ = &Backend::construct(*this, concurrency_hint);
98  
        sched_ = &Backend::construct(*this, concurrency_hint);
99  
    }
99  
    }
100  

100  

101  
    ~io_context();
101  
    ~io_context();
102  

102  

103  
    io_context(io_context const&)            = delete;
103  
    io_context(io_context const&)            = delete;
104  
    io_context& operator=(io_context const&) = delete;
104  
    io_context& operator=(io_context const&) = delete;
105  

105  

106  
    /** Return an executor for this context.
106  
    /** Return an executor for this context.
107  

107  

108  
        The returned executor can be used to dispatch coroutines
108  
        The returned executor can be used to dispatch coroutines
109  
        and post work items to this context.
109  
        and post work items to this context.
110  

110  

111  
        @return An executor associated with this context.
111  
        @return An executor associated with this context.
112  
    */
112  
    */
113  
    executor_type get_executor() const noexcept;
113  
    executor_type get_executor() const noexcept;
114  

114  

115  
    /** Signal the context to stop processing.
115  
    /** Signal the context to stop processing.
116  

116  

117  
        This causes `run()` to return as soon as possible. Any pending
117  
        This causes `run()` to return as soon as possible. Any pending
118  
        work items remain queued.
118  
        work items remain queued.
119  
    */
119  
    */
120  
    void stop()
120  
    void stop()
121  
    {
121  
    {
122  
        sched_->stop();
122  
        sched_->stop();
123  
    }
123  
    }
124  

124  

125  
    /** Return whether the context has been stopped.
125  
    /** Return whether the context has been stopped.
126  

126  

127  
        @return `true` if `stop()` has been called and `restart()`
127  
        @return `true` if `stop()` has been called and `restart()`
128  
            has not been called since.
128  
            has not been called since.
129  
    */
129  
    */
130  
    bool stopped() const noexcept
130  
    bool stopped() const noexcept
131  
    {
131  
    {
132  
        return sched_->stopped();
132  
        return sched_->stopped();
133  
    }
133  
    }
134  

134  

135  
    /** Restart the context after being stopped.
135  
    /** Restart the context after being stopped.
136  

136  

137  
        This function must be called before `run()` can be called
137  
        This function must be called before `run()` can be called
138  
        again after `stop()` has been called.
138  
        again after `stop()` has been called.
139  
    */
139  
    */
140  
    void restart()
140  
    void restart()
141  
    {
141  
    {
142  
        sched_->restart();
142  
        sched_->restart();
143  
    }
143  
    }
144  

144  

145  
    /** Process all pending work items.
145  
    /** Process all pending work items.
146  

146  

147  
        This function blocks until all pending work items have been
147  
        This function blocks until all pending work items have been
148  
        executed or `stop()` is called. The context is stopped
148  
        executed or `stop()` is called. The context is stopped
149  
        when there is no more outstanding work.
149  
        when there is no more outstanding work.
150  

150  

151  
        @note The context must be restarted with `restart()` before
151  
        @note The context must be restarted with `restart()` before
152  
            calling this function again after it returns.
152  
            calling this function again after it returns.
153  

153  

154  
        @return The number of handlers executed.
154  
        @return The number of handlers executed.
155  
    */
155  
    */
156  
    std::size_t run()
156  
    std::size_t run()
157  
    {
157  
    {
158  
        return sched_->run();
158  
        return sched_->run();
159  
    }
159  
    }
160  

160  

161  
    /** Process at most one pending work item.
161  
    /** Process at most one pending work item.
162  

162  

163  
        This function blocks until one work item has been executed
163  
        This function blocks until one work item has been executed
164  
        or `stop()` is called. The context is stopped when there
164  
        or `stop()` is called. The context is stopped when there
165  
        is no more outstanding work.
165  
        is no more outstanding work.
166  

166  

167  
        @note The context must be restarted with `restart()` before
167  
        @note The context must be restarted with `restart()` before
168  
            calling this function again after it returns.
168  
            calling this function again after it returns.
169  

169  

170  
        @return The number of handlers executed (0 or 1).
170  
        @return The number of handlers executed (0 or 1).
171  
    */
171  
    */
172  
    std::size_t run_one()
172  
    std::size_t run_one()
173  
    {
173  
    {
174  
        return sched_->run_one();
174  
        return sched_->run_one();
175  
    }
175  
    }
176  

176  

177  
    /** Process work items for the specified duration.
177  
    /** Process work items for the specified duration.
178  

178  

179  
        This function blocks until work items have been executed for
179  
        This function blocks until work items have been executed for
180  
        the specified duration, or `stop()` is called. The context
180  
        the specified duration, or `stop()` is called. The context
181  
        is stopped when there is no more outstanding work.
181  
        is stopped when there is no more outstanding work.
182  

182  

183  
        @note The context must be restarted with `restart()` before
183  
        @note The context must be restarted with `restart()` before
184  
            calling this function again after it returns.
184  
            calling this function again after it returns.
185  

185  

186  
        @param rel_time The duration for which to process work.
186  
        @param rel_time The duration for which to process work.
187  

187  

188  
        @return The number of handlers executed.
188  
        @return The number of handlers executed.
189  
    */
189  
    */
190  
    template<class Rep, class Period>
190  
    template<class Rep, class Period>
191  
    std::size_t run_for(std::chrono::duration<Rep, Period> const& rel_time)
191  
    std::size_t run_for(std::chrono::duration<Rep, Period> const& rel_time)
192  
    {
192  
    {
193  
        return run_until(std::chrono::steady_clock::now() + rel_time);
193  
        return run_until(std::chrono::steady_clock::now() + rel_time);
194  
    }
194  
    }
195  

195  

196  
    /** Process work items until the specified time.
196  
    /** Process work items until the specified time.
197  

197  

198  
        This function blocks until the specified time is reached
198  
        This function blocks until the specified time is reached
199  
        or `stop()` is called. The context is stopped when there
199  
        or `stop()` is called. The context is stopped when there
200  
        is no more outstanding work.
200  
        is no more outstanding work.
201  

201  

202  
        @note The context must be restarted with `restart()` before
202  
        @note The context must be restarted with `restart()` before
203  
            calling this function again after it returns.
203  
            calling this function again after it returns.
204  

204  

205  
        @param abs_time The time point until which to process work.
205  
        @param abs_time The time point until which to process work.
206  

206  

207  
        @return The number of handlers executed.
207  
        @return The number of handlers executed.
208  
    */
208  
    */
209  
    template<class Clock, class Duration>
209  
    template<class Clock, class Duration>
210  
    std::size_t
210  
    std::size_t
211  
    run_until(std::chrono::time_point<Clock, Duration> const& abs_time)
211  
    run_until(std::chrono::time_point<Clock, Duration> const& abs_time)
212  
    {
212  
    {
213  
        std::size_t n = 0;
213  
        std::size_t n = 0;
214  
        while (run_one_until(abs_time))
214  
        while (run_one_until(abs_time))
215  
            if (n != (std::numeric_limits<std::size_t>::max)())
215  
            if (n != (std::numeric_limits<std::size_t>::max)())
216  
                ++n;
216  
                ++n;
217  
        return n;
217  
        return n;
218  
    }
218  
    }
219  

219  

220  
    /** Process at most one work item for the specified duration.
220  
    /** Process at most one work item for the specified duration.
221  

221  

222  
        This function blocks until one work item has been executed,
222  
        This function blocks until one work item has been executed,
223  
        the specified duration has elapsed, or `stop()` is called.
223  
        the specified duration has elapsed, or `stop()` is called.
224  
        The context is stopped when there is no more outstanding work.
224  
        The context is stopped when there is no more outstanding work.
225  

225  

226  
        @note The context must be restarted with `restart()` before
226  
        @note The context must be restarted with `restart()` before
227  
            calling this function again after it returns.
227  
            calling this function again after it returns.
228  

228  

229  
        @param rel_time The duration for which the call may block.
229  
        @param rel_time The duration for which the call may block.
230  

230  

231  
        @return The number of handlers executed (0 or 1).
231  
        @return The number of handlers executed (0 or 1).
232  
    */
232  
    */
233  
    template<class Rep, class Period>
233  
    template<class Rep, class Period>
234  
    std::size_t run_one_for(std::chrono::duration<Rep, Period> const& rel_time)
234  
    std::size_t run_one_for(std::chrono::duration<Rep, Period> const& rel_time)
235  
    {
235  
    {
236  
        return run_one_until(std::chrono::steady_clock::now() + rel_time);
236  
        return run_one_until(std::chrono::steady_clock::now() + rel_time);
237  
    }
237  
    }
238  

238  

239  
    /** Process at most one work item until the specified time.
239  
    /** Process at most one work item until the specified time.
240  

240  

241  
        This function blocks until one work item has been executed,
241  
        This function blocks until one work item has been executed,
242  
        the specified time is reached, or `stop()` is called.
242  
        the specified time is reached, or `stop()` is called.
243  
        The context is stopped when there is no more outstanding work.
243  
        The context is stopped when there is no more outstanding work.
244  

244  

245  
        @note The context must be restarted with `restart()` before
245  
        @note The context must be restarted with `restart()` before
246  
            calling this function again after it returns.
246  
            calling this function again after it returns.
247  

247  

248  
        @param abs_time The time point until which the call may block.
248  
        @param abs_time The time point until which the call may block.
249  

249  

250  
        @return The number of handlers executed (0 or 1).
250  
        @return The number of handlers executed (0 or 1).
251  
    */
251  
    */
252  
    template<class Clock, class Duration>
252  
    template<class Clock, class Duration>
253  
    std::size_t
253  
    std::size_t
254  
    run_one_until(std::chrono::time_point<Clock, Duration> const& abs_time)
254  
    run_one_until(std::chrono::time_point<Clock, Duration> const& abs_time)
255  
    {
255  
    {
256  
        typename Clock::time_point now = Clock::now();
256  
        typename Clock::time_point now = Clock::now();
257  
        while (now < abs_time)
257  
        while (now < abs_time)
258  
        {
258  
        {
259  
            auto rel_time = abs_time - now;
259  
            auto rel_time = abs_time - now;
260  
            if (rel_time > std::chrono::seconds(1))
260  
            if (rel_time > std::chrono::seconds(1))
261  
                rel_time = std::chrono::seconds(1);
261  
                rel_time = std::chrono::seconds(1);
262  

262  

263  
            std::size_t s = sched_->wait_one(
263  
            std::size_t s = sched_->wait_one(
264  
                static_cast<long>(
264  
                static_cast<long>(
265  
                    std::chrono::duration_cast<std::chrono::microseconds>(
265  
                    std::chrono::duration_cast<std::chrono::microseconds>(
266  
                        rel_time)
266  
                        rel_time)
267  
                        .count()));
267  
                        .count()));
268  

268  

269  
            if (s || stopped())
269  
            if (s || stopped())
270  
                return s;
270  
                return s;
271  

271  

272  
            now = Clock::now();
272  
            now = Clock::now();
273  
        }
273  
        }
274  
        return 0;
274  
        return 0;
275  
    }
275  
    }
276  

276  

277  
    /** Process all ready work items without blocking.
277  
    /** Process all ready work items without blocking.
278  

278  

279  
        This function executes all work items that are ready to run
279  
        This function executes all work items that are ready to run
280  
        without blocking for more work. The context is stopped
280  
        without blocking for more work. The context is stopped
281  
        when there is no more outstanding work.
281  
        when there is no more outstanding work.
282  

282  

283  
        @note The context must be restarted with `restart()` before
283  
        @note The context must be restarted with `restart()` before
284  
            calling this function again after it returns.
284  
            calling this function again after it returns.
285  

285  

286  
        @return The number of handlers executed.
286  
        @return The number of handlers executed.
287  
    */
287  
    */
288  
    std::size_t poll()
288  
    std::size_t poll()
289  
    {
289  
    {
290  
        return sched_->poll();
290  
        return sched_->poll();
291  
    }
291  
    }
292  

292  

293  
    /** Process at most one ready work item without blocking.
293  
    /** Process at most one ready work item without blocking.
294  

294  

295  
        This function executes at most one work item that is ready
295  
        This function executes at most one work item that is ready
296  
        to run without blocking for more work. The context is
296  
        to run without blocking for more work. The context is
297  
        stopped when there is no more outstanding work.
297  
        stopped when there is no more outstanding work.
298  

298  

299  
        @note The context must be restarted with `restart()` before
299  
        @note The context must be restarted with `restart()` before
300  
            calling this function again after it returns.
300  
            calling this function again after it returns.
301  

301  

302  
        @return The number of handlers executed (0 or 1).
302  
        @return The number of handlers executed (0 or 1).
303  
    */
303  
    */
304  
    std::size_t poll_one()
304  
    std::size_t poll_one()
305  
    {
305  
    {
306  
        return sched_->poll_one();
306  
        return sched_->poll_one();
307  
    }
307  
    }
308  
};
308  
};
309  

309  

310  
/** An executor for dispatching work to an I/O context.
310  
/** An executor for dispatching work to an I/O context.
311  

311  

312  
    The executor provides the interface for posting work items and
312  
    The executor provides the interface for posting work items and
313  
    dispatching coroutines to the associated context. It satisfies
313  
    dispatching coroutines to the associated context. It satisfies
314  
    the `capy::Executor` concept.
314  
    the `capy::Executor` concept.
315  

315  

316  
    Executors are lightweight handles that can be copied and compared
316  
    Executors are lightweight handles that can be copied and compared
317  
    for equality. Two executors compare equal if they refer to the
317  
    for equality. Two executors compare equal if they refer to the
318  
    same context.
318  
    same context.
319  

319  

320  
    @par Thread Safety
320  
    @par Thread Safety
321  
    Distinct objects: Safe.@n
321  
    Distinct objects: Safe.@n
322  
    Shared objects: Safe.
322  
    Shared objects: Safe.
323  
*/
323  
*/
324  
class io_context::executor_type
324  
class io_context::executor_type
325  
{
325  
{
326  
    io_context* ctx_ = nullptr;
326  
    io_context* ctx_ = nullptr;
327  

327  

328  
public:
328  
public:
329  
    /** Default constructor.
329  
    /** Default constructor.
330  

330  

331  
        Constructs an executor not associated with any context.
331  
        Constructs an executor not associated with any context.
332  
    */
332  
    */
333  
    executor_type() = default;
333  
    executor_type() = default;
334  

334  

335  
    /** Construct an executor from a context.
335  
    /** Construct an executor from a context.
336  

336  

337  
        @param ctx The context to associate with this executor.
337  
        @param ctx The context to associate with this executor.
338  
    */
338  
    */
339  
    explicit executor_type(io_context& ctx) noexcept : ctx_(&ctx) {}
339  
    explicit executor_type(io_context& ctx) noexcept : ctx_(&ctx) {}
340  

340  

341  
    /** Return a reference to the associated execution context.
341  
    /** Return a reference to the associated execution context.
342  

342  

343  
        @return Reference to the context.
343  
        @return Reference to the context.
344  
    */
344  
    */
345  
    io_context& context() const noexcept
345  
    io_context& context() const noexcept
346  
    {
346  
    {
347  
        return *ctx_;
347  
        return *ctx_;
348  
    }
348  
    }
349  

349  

350  
    /** Check if the current thread is running this executor's context.
350  
    /** Check if the current thread is running this executor's context.
351  

351  

352  
        @return `true` if `run()` is being called on this thread.
352  
        @return `true` if `run()` is being called on this thread.
353  
    */
353  
    */
354  
    bool running_in_this_thread() const noexcept
354  
    bool running_in_this_thread() const noexcept
355  
    {
355  
    {
356  
        return ctx_->sched_->running_in_this_thread();
356  
        return ctx_->sched_->running_in_this_thread();
357  
    }
357  
    }
358  

358  

359  
    /** Informs the executor that work is beginning.
359  
    /** Informs the executor that work is beginning.
360  

360  

361  
        Must be paired with `on_work_finished()`.
361  
        Must be paired with `on_work_finished()`.
362  
    */
362  
    */
363  
    void on_work_started() const noexcept
363  
    void on_work_started() const noexcept
364  
    {
364  
    {
365  
        ctx_->sched_->work_started();
365  
        ctx_->sched_->work_started();
366  
    }
366  
    }
367  

367  

368  
    /** Informs the executor that work has completed.
368  
    /** Informs the executor that work has completed.
369  

369  

370  
        @par Preconditions
370  
        @par Preconditions
371  
        A preceding call to `on_work_started()` on an equal executor.
371  
        A preceding call to `on_work_started()` on an equal executor.
372  
    */
372  
    */
373  
    void on_work_finished() const noexcept
373  
    void on_work_finished() const noexcept
374  
    {
374  
    {
375  
        ctx_->sched_->work_finished();
375  
        ctx_->sched_->work_finished();
376  
    }
376  
    }
377  

377  

378  
    /** Dispatch a coroutine handle.
378  
    /** Dispatch a coroutine handle.
379  

379  

380  
        Returns a handle for symmetric transfer. If called from
380  
        Returns a handle for symmetric transfer. If called from
381  
        within `run()`, returns `h`. Otherwise posts the coroutine
381  
        within `run()`, returns `h`. Otherwise posts the coroutine
382  
        for later execution and returns `std::noop_coroutine()`.
382  
        for later execution and returns `std::noop_coroutine()`.
383  

383  

384  
        @param h The coroutine handle to dispatch.
384  
        @param h The coroutine handle to dispatch.
385  

385  

386  
        @return A handle for symmetric transfer or `std::noop_coroutine()`.
386  
        @return A handle for symmetric transfer or `std::noop_coroutine()`.
387  
    */
387  
    */
388  
    std::coroutine_handle<> dispatch(std::coroutine_handle<> h) const
388  
    std::coroutine_handle<> dispatch(std::coroutine_handle<> h) const
389  
    {
389  
    {
390  
        if (running_in_this_thread())
390  
        if (running_in_this_thread())
391  
            return h;
391  
            return h;
392  
        ctx_->sched_->post(h);
392  
        ctx_->sched_->post(h);
393  
        return std::noop_coroutine();
393  
        return std::noop_coroutine();
394  
    }
394  
    }
395  

395  

396  
    /** Post a coroutine for deferred execution.
396  
    /** Post a coroutine for deferred execution.
397  

397  

398  
        The coroutine will be resumed during a subsequent call to
398  
        The coroutine will be resumed during a subsequent call to
399  
        `run()`.
399  
        `run()`.
400  

400  

401  
        @param h The coroutine handle to post.
401  
        @param h The coroutine handle to post.
402  
    */
402  
    */
403  
    void post(std::coroutine_handle<> h) const
403  
    void post(std::coroutine_handle<> h) const
404  
    {
404  
    {
405  
        ctx_->sched_->post(h);
405  
        ctx_->sched_->post(h);
406  
    }
406  
    }
407  

407  

408  
    /** Compare two executors for equality.
408  
    /** Compare two executors for equality.
409  

409  

410  
        @return `true` if both executors refer to the same context.
410  
        @return `true` if both executors refer to the same context.
411  
    */
411  
    */
412  
    bool operator==(executor_type const& other) const noexcept
412  
    bool operator==(executor_type const& other) const noexcept
413  
    {
413  
    {
414  
        return ctx_ == other.ctx_;
414  
        return ctx_ == other.ctx_;
415  
    }
415  
    }
416  

416  

417  
    /** Compare two executors for inequality.
417  
    /** Compare two executors for inequality.
418  

418  

419  
        @return `true` if the executors refer to different contexts.
419  
        @return `true` if the executors refer to different contexts.
420  
    */
420  
    */
421  
    bool operator!=(executor_type const& other) const noexcept
421  
    bool operator!=(executor_type const& other) const noexcept
422  
    {
422  
    {
423  
        return ctx_ != other.ctx_;
423  
        return ctx_ != other.ctx_;
424  
    }
424  
    }
425  
};
425  
};
426  

426  

427  
inline io_context::executor_type
427  
inline io_context::executor_type
428  
io_context::get_executor() const noexcept
428  
io_context::get_executor() const noexcept
429  
{
429  
{
430  
    return executor_type(const_cast<io_context&>(*this));
430  
    return executor_type(const_cast<io_context&>(*this));
431  
}
431  
}
432  

432  

433  
} // namespace boost::corosio
433  
} // namespace boost::corosio
434  

434  

435  
#endif // BOOST_COROSIO_IO_CONTEXT_HPP
435  
#endif // BOOST_COROSIO_IO_CONTEXT_HPP