1  
//
1  
//
2  
// Copyright (c) 2026 Steve Gerbino
2  
// Copyright (c) 2026 Steve Gerbino
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/corosio
7  
// Official repository: https://github.com/cppalliance/corosio
8  
//
8  
//
9  

9  

10  
#ifndef BOOST_COROSIO_BASIC_IO_CONTEXT_HPP
10  
#ifndef BOOST_COROSIO_BASIC_IO_CONTEXT_HPP
11  
#define BOOST_COROSIO_BASIC_IO_CONTEXT_HPP
11  
#define BOOST_COROSIO_BASIC_IO_CONTEXT_HPP
12  

12  

13  
#include <boost/corosio/detail/config.hpp>
13  
#include <boost/corosio/detail/config.hpp>
14  
#include <boost/corosio/detail/scheduler.hpp>
14  
#include <boost/corosio/detail/scheduler.hpp>
15  
#include <boost/capy/coro.hpp>
15  
#include <boost/capy/coro.hpp>
16  
#include <boost/capy/ex/execution_context.hpp>
16  
#include <boost/capy/ex/execution_context.hpp>
17  

17  

18  
#include <chrono>
18  
#include <chrono>
19  
#include <cstddef>
19  
#include <cstddef>
20  
#include <limits>
20  
#include <limits>
21  

21  

22  
namespace boost::corosio {
22  
namespace boost::corosio {
23  

23  

24  
/** Base class for I/O context implementations.
24  
/** Base class for I/O context implementations.
25  

25  

26  
    This class provides the common API for all I/O context types.
26  
    This class provides the common API for all I/O context types.
27  
    Concrete context implementations (epoll_context, iocp_context, etc.)
27  
    Concrete context implementations (epoll_context, iocp_context, etc.)
28  
    inherit from this class to gain the standard io_context interface.
28  
    inherit from this class to gain the standard io_context interface.
29  

29  

30  
    @par Thread Safety
30  
    @par Thread Safety
31  
    Distinct objects: Safe.@n
31  
    Distinct objects: Safe.@n
32  
    Shared objects: Safe, if using a concurrency hint greater than 1.
32  
    Shared objects: Safe, if using a concurrency hint greater than 1.
33  
*/
33  
*/
34  
class BOOST_COROSIO_DECL basic_io_context : public capy::execution_context
34  
class BOOST_COROSIO_DECL basic_io_context : public capy::execution_context
35  
{
35  
{
36  
public:
36  
public:
37  
    /** The executor type for this context. */
37  
    /** The executor type for this context. */
38  
    class executor_type;
38  
    class executor_type;
39  

39  

40  
    /** Return an executor for this context.
40  
    /** Return an executor for this context.
41  

41  

42  
        The returned executor can be used to dispatch coroutines
42  
        The returned executor can be used to dispatch coroutines
43  
        and post work items to this context.
43  
        and post work items to this context.
44  

44  

45  
        @return An executor associated with this context.
45  
        @return An executor associated with this context.
46  
    */
46  
    */
47  
    executor_type
47  
    executor_type
48  
    get_executor() const noexcept;
48  
    get_executor() const noexcept;
49  

49  

50  
    /** Signal the context to stop processing.
50  
    /** Signal the context to stop processing.
51  

51  

52  
        This causes `run()` to return as soon as possible. Any pending
52  
        This causes `run()` to return as soon as possible. Any pending
53  
        work items remain queued.
53  
        work items remain queued.
54  
    */
54  
    */
55  
    void
55  
    void
56  
    stop()
56  
    stop()
57  
    {
57  
    {
58  
        sched_->stop();
58  
        sched_->stop();
59  
    }
59  
    }
60  

60  

61  
    /** Return whether the context has been stopped.
61  
    /** Return whether the context has been stopped.
62  

62  

63  
        @return `true` if `stop()` has been called and `restart()`
63  
        @return `true` if `stop()` has been called and `restart()`
64  
            has not been called since.
64  
            has not been called since.
65  
    */
65  
    */
66  
    bool
66  
    bool
67  
    stopped() const noexcept
67  
    stopped() const noexcept
68  
    {
68  
    {
69  
        return sched_->stopped();
69  
        return sched_->stopped();
70  
    }
70  
    }
71  

71  

72  
    /** Restart the context after being stopped.
72  
    /** Restart the context after being stopped.
73  

73  

74  
        This function must be called before `run()` can be called
74  
        This function must be called before `run()` can be called
75  
        again after `stop()` has been called.
75  
        again after `stop()` has been called.
76  
    */
76  
    */
77  
    void
77  
    void
78  
    restart()
78  
    restart()
79  
    {
79  
    {
80  
        sched_->restart();
80  
        sched_->restart();
81  
    }
81  
    }
82  

82  

83  
    /** Process all pending work items.
83  
    /** Process all pending work items.
84  

84  

85  
        This function blocks until all pending work items have been
85  
        This function blocks until all pending work items have been
86  
        executed or `stop()` is called. The context is stopped
86  
        executed or `stop()` is called. The context is stopped
87  
        when there is no more outstanding work.
87  
        when there is no more outstanding work.
88  

88  

89  
        @note The context must be restarted with `restart()` before
89  
        @note The context must be restarted with `restart()` before
90  
            calling this function again after it returns.
90  
            calling this function again after it returns.
91  

91  

92  
        @return The number of handlers executed.
92  
        @return The number of handlers executed.
93  
    */
93  
    */
94  
    std::size_t
94  
    std::size_t
95  
    run()
95  
    run()
96  
    {
96  
    {
97  
        return sched_->run();
97  
        return sched_->run();
98  
    }
98  
    }
99  

99  

100  
    /** Process at most one pending work item.
100  
    /** Process at most one pending work item.
101  

101  

102  
        This function blocks until one work item has been executed
102  
        This function blocks until one work item has been executed
103  
        or `stop()` is called. The context is stopped when there
103  
        or `stop()` is called. The context is stopped when there
104  
        is no more outstanding work.
104  
        is no more outstanding work.
105  

105  

106  
        @note The context must be restarted with `restart()` before
106  
        @note The context must be restarted with `restart()` before
107  
            calling this function again after it returns.
107  
            calling this function again after it returns.
108  

108  

109  
        @return The number of handlers executed (0 or 1).
109  
        @return The number of handlers executed (0 or 1).
110  
    */
110  
    */
111  
    std::size_t
111  
    std::size_t
112  
    run_one()
112  
    run_one()
113  
    {
113  
    {
114  
        return sched_->run_one();
114  
        return sched_->run_one();
115  
    }
115  
    }
116  

116  

117  
    /** Process work items for the specified duration.
117  
    /** Process work items for the specified duration.
118  

118  

119  
        This function blocks until work items have been executed for
119  
        This function blocks until work items have been executed for
120  
        the specified duration, or `stop()` is called. The context
120  
        the specified duration, or `stop()` is called. The context
121  
        is stopped when there is no more outstanding work.
121  
        is stopped when there is no more outstanding work.
122  

122  

123  
        @note The context must be restarted with `restart()` before
123  
        @note The context must be restarted with `restart()` before
124  
            calling this function again after it returns.
124  
            calling this function again after it returns.
125  

125  

126  
        @param rel_time The duration for which to process work.
126  
        @param rel_time The duration for which to process work.
127  

127  

128  
        @return The number of handlers executed.
128  
        @return The number of handlers executed.
129  
    */
129  
    */
130  
    template<class Rep, class Period>
130  
    template<class Rep, class Period>
131  
    std::size_t
131  
    std::size_t
132  
    run_for(std::chrono::duration<Rep, Period> const& rel_time)
132  
    run_for(std::chrono::duration<Rep, Period> const& rel_time)
133  
    {
133  
    {
134  
        return run_until(std::chrono::steady_clock::now() + rel_time);
134  
        return run_until(std::chrono::steady_clock::now() + rel_time);
135  
    }
135  
    }
136  

136  

137  
    /** Process work items until the specified time.
137  
    /** Process work items until the specified time.
138  

138  

139  
        This function blocks until the specified time is reached
139  
        This function blocks until the specified time is reached
140  
        or `stop()` is called. The context is stopped when there
140  
        or `stop()` is called. The context is stopped when there
141  
        is no more outstanding work.
141  
        is no more outstanding work.
142  

142  

143  
        @note The context must be restarted with `restart()` before
143  
        @note The context must be restarted with `restart()` before
144  
            calling this function again after it returns.
144  
            calling this function again after it returns.
145  

145  

146  
        @param abs_time The time point until which to process work.
146  
        @param abs_time The time point until which to process work.
147  

147  

148  
        @return The number of handlers executed.
148  
        @return The number of handlers executed.
149  
    */
149  
    */
150  
    template<class Clock, class Duration>
150  
    template<class Clock, class Duration>
151  
    std::size_t
151  
    std::size_t
152  
    run_until(std::chrono::time_point<Clock, Duration> const& abs_time)
152  
    run_until(std::chrono::time_point<Clock, Duration> const& abs_time)
153  
    {
153  
    {
154  
        std::size_t n = 0;
154  
        std::size_t n = 0;
155  
        while (run_one_until(abs_time))
155  
        while (run_one_until(abs_time))
156  
            if (n != (std::numeric_limits<std::size_t>::max)())
156  
            if (n != (std::numeric_limits<std::size_t>::max)())
157  
                ++n;
157  
                ++n;
158  
        return n;
158  
        return n;
159  
    }
159  
    }
160  

160  

161  
    /** Process at most one work item for the specified duration.
161  
    /** Process at most one work item for the specified duration.
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  
        the specified duration has elapsed, or `stop()` is called.
164  
        the specified duration has elapsed, or `stop()` is called.
165  
        The context is stopped when there is no more outstanding work.
165  
        The context is stopped when there 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  
        @param rel_time The duration for which the call may block.
170  
        @param rel_time The duration for which the call may block.
171  

171  

172  
        @return The number of handlers executed (0 or 1).
172  
        @return The number of handlers executed (0 or 1).
173  
    */
173  
    */
174  
    template<class Rep, class Period>
174  
    template<class Rep, class Period>
175  
    std::size_t
175  
    std::size_t
176  
    run_one_for(std::chrono::duration<Rep, Period> const& rel_time)
176  
    run_one_for(std::chrono::duration<Rep, Period> const& rel_time)
177  
    {
177  
    {
178  
        return run_one_until(std::chrono::steady_clock::now() + rel_time);
178  
        return run_one_until(std::chrono::steady_clock::now() + rel_time);
179  
    }
179  
    }
180  

180  

181  
    /** Process at most one work item until the specified time.
181  
    /** Process at most one work item until the specified time.
182  

182  

183  
        This function blocks until one work item has been executed,
183  
        This function blocks until one work item has been executed,
184  
        the specified time is reached, or `stop()` is called.
184  
        the specified time is reached, or `stop()` is called.
185  
        The context is stopped when there is no more outstanding work.
185  
        The context is stopped when there is no more outstanding work.
186  

186  

187  
        @note The context must be restarted with `restart()` before
187  
        @note The context must be restarted with `restart()` before
188  
            calling this function again after it returns.
188  
            calling this function again after it returns.
189  

189  

190  
        @param abs_time The time point until which the call may block.
190  
        @param abs_time The time point until which the call may block.
191  

191  

192  
        @return The number of handlers executed (0 or 1).
192  
        @return The number of handlers executed (0 or 1).
193  
    */
193  
    */
194  
    template<class Clock, class Duration>
194  
    template<class Clock, class Duration>
195  
    std::size_t
195  
    std::size_t
196  
    run_one_until(std::chrono::time_point<Clock, Duration> const& abs_time)
196  
    run_one_until(std::chrono::time_point<Clock, Duration> const& abs_time)
197  
    {
197  
    {
198  
        typename Clock::time_point now = Clock::now();
198  
        typename Clock::time_point now = Clock::now();
199  
        while (now < abs_time)
199  
        while (now < abs_time)
200  
        {
200  
        {
201  
            auto rel_time = abs_time - now;
201  
            auto rel_time = abs_time - now;
202  
            if (rel_time > std::chrono::seconds(1))
202  
            if (rel_time > std::chrono::seconds(1))
203  
                rel_time = std::chrono::seconds(1);
203  
                rel_time = std::chrono::seconds(1);
204  

204  

205  
            std::size_t s = sched_->wait_one(
205  
            std::size_t s = sched_->wait_one(
206  
                static_cast<long>(std::chrono::duration_cast<
206  
                static_cast<long>(std::chrono::duration_cast<
207  
                    std::chrono::microseconds>(rel_time).count()));
207  
                    std::chrono::microseconds>(rel_time).count()));
208  

208  

209  
            if (s || stopped())
209  
            if (s || stopped())
210  
                return s;
210  
                return s;
211  

211  

212  
            now = Clock::now();
212  
            now = Clock::now();
213  
        }
213  
        }
214  
        return 0;
214  
        return 0;
215  
    }
215  
    }
216  

216  

217  
    /** Process all ready work items without blocking.
217  
    /** Process all ready work items without blocking.
218  

218  

219  
        This function executes all work items that are ready to run
219  
        This function executes all work items that are ready to run
220  
        without blocking for more work. The context is stopped
220  
        without blocking for more work. The context is stopped
221  
        when there is no more outstanding work.
221  
        when there is no more outstanding work.
222  

222  

223  
        @note The context must be restarted with `restart()` before
223  
        @note The context must be restarted with `restart()` before
224  
            calling this function again after it returns.
224  
            calling this function again after it returns.
225  

225  

226  
        @return The number of handlers executed.
226  
        @return The number of handlers executed.
227  
    */
227  
    */
228  
    std::size_t
228  
    std::size_t
229  
    poll()
229  
    poll()
230  
    {
230  
    {
231  
        return sched_->poll();
231  
        return sched_->poll();
232  
    }
232  
    }
233  

233  

234  
    /** Process at most one ready work item without blocking.
234  
    /** Process at most one ready work item without blocking.
235  

235  

236  
        This function executes at most one work item that is ready
236  
        This function executes at most one work item that is ready
237  
        to run without blocking for more work. The context is
237  
        to run without blocking for more work. The context is
238  
        stopped when there is no more outstanding work.
238  
        stopped when there is no more outstanding work.
239  

239  

240  
        @note The context must be restarted with `restart()` before
240  
        @note The context must be restarted with `restart()` before
241  
            calling this function again after it returns.
241  
            calling this function again after it returns.
242  

242  

243  
        @return The number of handlers executed (0 or 1).
243  
        @return The number of handlers executed (0 or 1).
244  
    */
244  
    */
245  
    std::size_t
245  
    std::size_t
246  
    poll_one()
246  
    poll_one()
247  
    {
247  
    {
248  
        return sched_->poll_one();
248  
        return sched_->poll_one();
249  
    }
249  
    }
250  

250  

251  
protected:
251  
protected:
252  
    /** Default constructor.
252  
    /** Default constructor.
253  

253  

254  
        Derived classes must set sched_ in their constructor body.
254  
        Derived classes must set sched_ in their constructor body.
255  
    */
255  
    */
256  
    basic_io_context()
256  
    basic_io_context()
257  
        : sched_(nullptr)
257  
        : sched_(nullptr)
258  
    {
258  
    {
259  
    }
259  
    }
260  

260  

261  
    detail::scheduler* sched_;
261  
    detail::scheduler* sched_;
262  
};
262  
};
263  

263  

264  
//------------------------------------------------------------------------------
264  
//------------------------------------------------------------------------------
265  

265  

266  
/** An executor for dispatching work to an I/O context.
266  
/** An executor for dispatching work to an I/O context.
267  

267  

268  
    The executor provides the interface for posting work items and
268  
    The executor provides the interface for posting work items and
269  
    dispatching coroutines to the associated context. It satisfies
269  
    dispatching coroutines to the associated context. It satisfies
270  
    the `capy::Executor` concept.
270  
    the `capy::Executor` concept.
271  

271  

272  
    Executors are lightweight handles that can be copied and compared
272  
    Executors are lightweight handles that can be copied and compared
273  
    for equality. Two executors compare equal if they refer to the
273  
    for equality. Two executors compare equal if they refer to the
274  
    same context.
274  
    same context.
275  

275  

276  
    @par Thread Safety
276  
    @par Thread Safety
277  
    Distinct objects: Safe.@n
277  
    Distinct objects: Safe.@n
278  
    Shared objects: Safe.
278  
    Shared objects: Safe.
279  
*/
279  
*/
280  
class basic_io_context::executor_type
280  
class basic_io_context::executor_type
281  
{
281  
{
282  
    basic_io_context* ctx_ = nullptr;
282  
    basic_io_context* ctx_ = nullptr;
283  

283  

284  
public:
284  
public:
285  
    /** Default constructor.
285  
    /** Default constructor.
286  

286  

287  
        Constructs an executor not associated with any context.
287  
        Constructs an executor not associated with any context.
288  
    */
288  
    */
289  
    executor_type() = default;
289  
    executor_type() = default;
290  

290  

291  
    /** Construct an executor from a context.
291  
    /** Construct an executor from a context.
292  

292  

293  
        @param ctx The context to associate with this executor.
293  
        @param ctx The context to associate with this executor.
294  
    */
294  
    */
295  
    explicit
295  
    explicit
296  
    executor_type(basic_io_context& ctx) noexcept
296  
    executor_type(basic_io_context& ctx) noexcept
297  
        : ctx_(&ctx)
297  
        : ctx_(&ctx)
298  
    {
298  
    {
299  
    }
299  
    }
300  

300  

301  
    /** Return a reference to the associated execution context.
301  
    /** Return a reference to the associated execution context.
302  

302  

303  
        @return Reference to the context.
303  
        @return Reference to the context.
304  
    */
304  
    */
305  
    basic_io_context&
305  
    basic_io_context&
306  
    context() const noexcept
306  
    context() const noexcept
307  
    {
307  
    {
308  
        return *ctx_;
308  
        return *ctx_;
309  
    }
309  
    }
310  

310  

311  
    /** Check if the current thread is running this executor's context.
311  
    /** Check if the current thread is running this executor's context.
312  

312  

313  
        @return `true` if `run()` is being called on this thread.
313  
        @return `true` if `run()` is being called on this thread.
314  
    */
314  
    */
315  
    bool
315  
    bool
316  
    running_in_this_thread() const noexcept
316  
    running_in_this_thread() const noexcept
317  
    {
317  
    {
318  
        return ctx_->sched_->running_in_this_thread();
318  
        return ctx_->sched_->running_in_this_thread();
319  
    }
319  
    }
320  

320  

321  
    /** Informs the executor that work is beginning.
321  
    /** Informs the executor that work is beginning.
322  

322  

323  
        Must be paired with `on_work_finished()`.
323  
        Must be paired with `on_work_finished()`.
324  
    */
324  
    */
325  
    void
325  
    void
326  
    on_work_started() const noexcept
326  
    on_work_started() const noexcept
327  
    {
327  
    {
328  
        ctx_->sched_->on_work_started();
328  
        ctx_->sched_->on_work_started();
329  
    }
329  
    }
330  

330  

331  
    /** Informs the executor that work has completed.
331  
    /** Informs the executor that work has completed.
332  

332  

333  
        @par Preconditions
333  
        @par Preconditions
334  
        A preceding call to `on_work_started()` on an equal executor.
334  
        A preceding call to `on_work_started()` on an equal executor.
335  
    */
335  
    */
336  
    void
336  
    void
337  
    on_work_finished() const noexcept
337  
    on_work_finished() const noexcept
338  
    {
338  
    {
339  
        ctx_->sched_->on_work_finished();
339  
        ctx_->sched_->on_work_finished();
340  
    }
340  
    }
341  

341  

342  
    /** Dispatch a coroutine handle.
342  
    /** Dispatch a coroutine handle.
343  

343  

344  
        If called from within `run()`, resumes the coroutine inline
344  
        If called from within `run()`, resumes the coroutine inline
345  
        by calling `h.resume()`. The call returns when the coroutine
345  
        by calling `h.resume()`. The call returns when the coroutine
346  
        suspends or completes. Otherwise posts the coroutine for
346  
        suspends or completes. Otherwise posts the coroutine for
347  
        later execution.
347  
        later execution.
348  

348  

349  
        After this function returns, the state of `h` is unspecified.
349  
        After this function returns, the state of `h` is unspecified.
350  
        The coroutine may have completed, been destroyed, or suspended
350  
        The coroutine may have completed, been destroyed, or suspended
351  
        at a different suspension point. Callers must not assume `h`
351  
        at a different suspension point. Callers must not assume `h`
352  
        remains valid after calling `dispatch`.
352  
        remains valid after calling `dispatch`.
353  

353  

354  
        @note Because this function may call `h.resume()` before
354  
        @note Because this function may call `h.resume()` before
355  
        returning, it cannot be used to implement symmetric transfer
355  
        returning, it cannot be used to implement symmetric transfer
356  
        from `await_suspend`.
356  
        from `await_suspend`.
357  

357  

358  
        @param h The coroutine handle to dispatch.
358  
        @param h The coroutine handle to dispatch.
359  
    */
359  
    */
360  
    void
360  
    void
361  
    dispatch(capy::coro h) const
361  
    dispatch(capy::coro h) const
362  
    {
362  
    {
363  
        if (running_in_this_thread())
363  
        if (running_in_this_thread())
364  
            h.resume();
364  
            h.resume();
365  
        else
365  
        else
366  
            ctx_->sched_->post(h);
366  
            ctx_->sched_->post(h);
367  
    }
367  
    }
368  

368  

369  
    /** Post a coroutine for deferred execution.
369  
    /** Post a coroutine for deferred execution.
370  

370  

371  
        The coroutine will be resumed during a subsequent call to
371  
        The coroutine will be resumed during a subsequent call to
372  
        `run()`.
372  
        `run()`.
373  

373  

374  
        @param h The coroutine handle to post.
374  
        @param h The coroutine handle to post.
375  
    */
375  
    */
376  
    void
376  
    void
377  
    post(capy::coro h) const
377  
    post(capy::coro h) const
378  
    {
378  
    {
379  
        ctx_->sched_->post(h);
379  
        ctx_->sched_->post(h);
380  
    }
380  
    }
381  

381  

382  
    /** Compare two executors for equality.
382  
    /** Compare two executors for equality.
383  

383  

384  
        @return `true` if both executors refer to the same context.
384  
        @return `true` if both executors refer to the same context.
385  
    */
385  
    */
386  
    bool
386  
    bool
387  
    operator==(executor_type const& other) const noexcept
387  
    operator==(executor_type const& other) const noexcept
388  
    {
388  
    {
389  
        return ctx_ == other.ctx_;
389  
        return ctx_ == other.ctx_;
390  
    }
390  
    }
391  

391  

392  
    /** Compare two executors for inequality.
392  
    /** Compare two executors for inequality.
393  

393  

394  
        @return `true` if the executors refer to different contexts.
394  
        @return `true` if the executors refer to different contexts.
395  
    */
395  
    */
396  
    bool
396  
    bool
397  
    operator!=(executor_type const& other) const noexcept
397  
    operator!=(executor_type const& other) const noexcept
398  
    {
398  
    {
399  
        return ctx_ != other.ctx_;
399  
        return ctx_ != other.ctx_;
400  
    }
400  
    }
401  
};
401  
};
402  

402  

403  
//------------------------------------------------------------------------------
403  
//------------------------------------------------------------------------------
404  

404  

405  
inline
405  
inline
406  
basic_io_context::executor_type
406  
basic_io_context::executor_type
407  
basic_io_context::
407  
basic_io_context::
408  
get_executor() const noexcept
408  
get_executor() const noexcept
409  
{
409  
{
410  
    return executor_type(const_cast<basic_io_context&>(*this));
410  
    return executor_type(const_cast<basic_io_context&>(*this));
411  
}
411  
}
412  

412  

413  
} // namespace boost::corosio
413  
} // namespace boost::corosio
414  

414  

415  
#endif // BOOST_COROSIO_BASIC_IO_CONTEXT_HPP
415  
#endif // BOOST_COROSIO_BASIC_IO_CONTEXT_HPP