include/boost/corosio/io_context.hpp

96.1% Lines (73/76) 100.0% List of functions (23/23)
io_context.hpp
f(x) Functions (23)
Function Calls Lines Blocks
boost::corosio::io_context::io_context<boost::corosio::epoll_t>(boost::corosio::epoll_t, unsigned int) :93 195x 100.0% 80.0% boost::corosio::io_context::io_context<boost::corosio::select_t>(boost::corosio::select_t, unsigned int) :93 195x 100.0% 80.0% boost::corosio::io_context::stop() :122 5x 100.0% 100.0% boost::corosio::io_context::stopped() const :132 21x 100.0% 100.0% boost::corosio::io_context::restart() :142 91x 100.0% 100.0% boost::corosio::io_context::run() :158 388x 100.0% 100.0% boost::corosio::io_context::run_one() :174 2x 100.0% 100.0% unsigned long boost::corosio::io_context::run_for<long, std::ratio<1l, 1000l> >(std::chrono::duration<long, std::ratio<1l, 1000l> > const&) :193 8x 100.0% 88.0% unsigned long boost::corosio::io_context::run_until<std::chrono::_V2::steady_clock, std::chrono::duration<long, std::ratio<1l, 1000000000l> > >(std::chrono::time_point<std::chrono::_V2::steady_clock, std::chrono::duration<long, std::ratio<1l, 1000000000l> > > const&) :213 8x 100.0% 100.0% unsigned long boost::corosio::io_context::run_one_for<long, std::ratio<1l, 1000l> >(std::chrono::duration<long, std::ratio<1l, 1000l> > const&) :236 2x 100.0% 88.0% unsigned long boost::corosio::io_context::run_one_until<std::chrono::_V2::steady_clock, std::chrono::duration<long, std::ratio<1l, 1000000000l> > >(std::chrono::time_point<std::chrono::_V2::steady_clock, std::chrono::duration<long, std::ratio<1l, 1000000000l> > > const&) :256 61x 76.9% 61.0% boost::corosio::io_context::poll() :290 6x 100.0% 100.0% boost::corosio::io_context::poll_one() :306 4x 100.0% 100.0% boost::corosio::io_context::executor_type::executor_type(boost::corosio::io_context&) :341 606x 100.0% 100.0% boost::corosio::io_context::executor_type::context() const :347 1278x 100.0% 100.0% boost::corosio::io_context::executor_type::running_in_this_thread() const :356 1290x 100.0% 100.0% boost::corosio::io_context::executor_type::on_work_started() const :365 1433x 100.0% 100.0% boost::corosio::io_context::executor_type::on_work_finished() const :375 1407x 100.0% 100.0% boost::corosio::io_context::executor_type::dispatch(boost::capy::continuation&) const :392 1288x 100.0% 100.0% boost::corosio::io_context::executor_type::post(boost::capy::continuation&) const :407 9323x 100.0% 100.0% boost::corosio::io_context::executor_type::post(std::__n4861::coroutine_handle<void>) const :424 1426x 100.0% 100.0% boost::corosio::io_context::executor_type::operator==(boost::corosio::io_context::executor_type const&) const :433 1x 100.0% 100.0% boost::corosio::io_context::get_executor() const :449 606x 100.0% 100.0%
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3 // Copyright (c) 2026 Steve Gerbino
4 // Copyright (c) 2026 Michael Vandeberg
5 //
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)
8 //
9 // Official repository: https://github.com/cppalliance/corosio
10 //
11
12 #ifndef BOOST_COROSIO_IO_CONTEXT_HPP
13 #define BOOST_COROSIO_IO_CONTEXT_HPP
14
15 #include <boost/corosio/detail/config.hpp>
16 #include <boost/corosio/detail/continuation_op.hpp>
17 #include <boost/corosio/detail/platform.hpp>
18 #include <boost/corosio/detail/scheduler.hpp>
19 #include <boost/capy/continuation.hpp>
20 #include <boost/capy/ex/execution_context.hpp>
21
22 #include <chrono>
23 #include <coroutine>
24 #include <cstddef>
25 #include <limits>
26 #include <thread>
27
28 namespace boost::corosio {
29
30 namespace detail {
31 struct timer_service_access;
32 } // namespace detail
33
34 /** An I/O context for running asynchronous operations.
35
36 The io_context provides an execution environment for async
37 operations. It maintains a queue of pending work items and
38 processes them when `run()` is called.
39
40 The default and unsigned constructors select the platform's
41 native backend:
42 - Windows: IOCP
43 - Linux: epoll
44 - BSD/macOS: kqueue
45 - Other POSIX: select
46
47 The template constructor accepts a backend tag value to
48 choose a specific backend at compile time:
49
50 @par Example
51 @code
52 io_context ioc; // platform default
53 io_context ioc2(corosio::epoll); // explicit backend
54 @endcode
55
56 @par Thread Safety
57 Distinct objects: Safe.@n
58 Shared objects: Safe, if using a concurrency hint greater
59 than 1.
60
61 @see epoll_t, select_t, kqueue_t, iocp_t
62 */
63 class BOOST_COROSIO_DECL io_context : public capy::execution_context
64 {
65 friend struct detail::timer_service_access;
66
67 protected:
68 detail::scheduler* sched_;
69
70 public:
71 /** The executor type for this context. */
72 class executor_type;
73
74 /** Construct with default concurrency and platform backend. */
75 io_context();
76
77 /** Construct with a concurrency hint and platform backend.
78
79 @param concurrency_hint Hint for the number of threads
80 that will call `run()`.
81 */
82 explicit io_context(unsigned concurrency_hint);
83
84 /** Construct with an explicit backend tag.
85
86 @param backend The backend tag value selecting the I/O
87 multiplexer (e.g. `corosio::epoll`).
88 @param concurrency_hint Hint for the number of threads
89 that will call `run()`.
90 */
91 template<class Backend>
92 requires requires { Backend::construct; }
93 390x explicit io_context(
94 Backend backend,
95 unsigned concurrency_hint = std::thread::hardware_concurrency())
96 : capy::execution_context(this)
97 390x , sched_(nullptr)
98 {
99 (void)backend;
100 390x sched_ = &Backend::construct(*this, concurrency_hint);
101 390x }
102
103 ~io_context();
104
105 io_context(io_context const&) = delete;
106 io_context& operator=(io_context const&) = delete;
107
108 /** Return an executor for this context.
109
110 The returned executor can be used to dispatch coroutines
111 and post work items to this context.
112
113 @return An executor associated with this context.
114 */
115 executor_type get_executor() const noexcept;
116
117 /** Signal the context to stop processing.
118
119 This causes `run()` to return as soon as possible. Any pending
120 work items remain queued.
121 */
122 5x void stop()
123 {
124 5x sched_->stop();
125 5x }
126
127 /** Return whether the context has been stopped.
128
129 @return `true` if `stop()` has been called and `restart()`
130 has not been called since.
131 */
132 21x bool stopped() const noexcept
133 {
134 21x return sched_->stopped();
135 }
136
137 /** Restart the context after being stopped.
138
139 This function must be called before `run()` can be called
140 again after `stop()` has been called.
141 */
142 91x void restart()
143 {
144 91x sched_->restart();
145 91x }
146
147 /** Process all pending work items.
148
149 This function blocks until all pending work items have been
150 executed or `stop()` is called. The context is stopped
151 when there is no more outstanding work.
152
153 @note The context must be restarted with `restart()` before
154 calling this function again after it returns.
155
156 @return The number of handlers executed.
157 */
158 388x std::size_t run()
159 {
160 388x return sched_->run();
161 }
162
163 /** Process at most one pending work item.
164
165 This function blocks until one work item has been executed
166 or `stop()` is called. The context is stopped when there
167 is no more outstanding work.
168
169 @note The context must be restarted with `restart()` before
170 calling this function again after it returns.
171
172 @return The number of handlers executed (0 or 1).
173 */
174 2x std::size_t run_one()
175 {
176 2x return sched_->run_one();
177 }
178
179 /** Process work items for the specified duration.
180
181 This function blocks until work items have been executed for
182 the specified duration, or `stop()` is called. The context
183 is stopped when there is no more outstanding work.
184
185 @note The context must be restarted with `restart()` before
186 calling this function again after it returns.
187
188 @param rel_time The duration for which to process work.
189
190 @return The number of handlers executed.
191 */
192 template<class Rep, class Period>
193 8x std::size_t run_for(std::chrono::duration<Rep, Period> const& rel_time)
194 {
195 8x return run_until(std::chrono::steady_clock::now() + rel_time);
196 }
197
198 /** Process work items until the specified time.
199
200 This function blocks until the specified time is reached
201 or `stop()` is called. The context is stopped when there
202 is no more outstanding work.
203
204 @note The context must be restarted with `restart()` before
205 calling this function again after it returns.
206
207 @param abs_time The time point until which to process work.
208
209 @return The number of handlers executed.
210 */
211 template<class Clock, class Duration>
212 std::size_t
213 8x run_until(std::chrono::time_point<Clock, Duration> const& abs_time)
214 {
215 8x std::size_t n = 0;
216 57x while (run_one_until(abs_time))
217 49x if (n != (std::numeric_limits<std::size_t>::max)())
218 49x ++n;
219 8x return n;
220 }
221
222 /** Process at most one work item for the specified duration.
223
224 This function blocks until one work item has been executed,
225 the specified duration has elapsed, or `stop()` is called.
226 The context is stopped when there is no more outstanding work.
227
228 @note The context must be restarted with `restart()` before
229 calling this function again after it returns.
230
231 @param rel_time The duration for which the call may block.
232
233 @return The number of handlers executed (0 or 1).
234 */
235 template<class Rep, class Period>
236 2x std::size_t run_one_for(std::chrono::duration<Rep, Period> const& rel_time)
237 {
238 2x return run_one_until(std::chrono::steady_clock::now() + rel_time);
239 }
240
241 /** Process at most one work item until the specified time.
242
243 This function blocks until one work item has been executed,
244 the specified time is reached, or `stop()` is called.
245 The context is stopped when there is no more outstanding work.
246
247 @note The context must be restarted with `restart()` before
248 calling this function again after it returns.
249
250 @param abs_time The time point until which the call may block.
251
252 @return The number of handlers executed (0 or 1).
253 */
254 template<class Clock, class Duration>
255 std::size_t
256 61x run_one_until(std::chrono::time_point<Clock, Duration> const& abs_time)
257 {
258 61x typename Clock::time_point now = Clock::now();
259 61x while (now < abs_time)
260 {
261 61x auto rel_time = abs_time - now;
262 61x if (rel_time > std::chrono::seconds(1))
263 rel_time = std::chrono::seconds(1);
264
265 61x std::size_t s = sched_->wait_one(
266 static_cast<long>(
267 61x std::chrono::duration_cast<std::chrono::microseconds>(
268 rel_time)
269 61x .count()));
270
271 61x if (s || stopped())
272 61x return s;
273
274 now = Clock::now();
275 }
276 return 0;
277 }
278
279 /** Process all ready work items without blocking.
280
281 This function executes all work items that are ready to run
282 without blocking for more work. The context is stopped
283 when there is no more outstanding work.
284
285 @note The context must be restarted with `restart()` before
286 calling this function again after it returns.
287
288 @return The number of handlers executed.
289 */
290 6x std::size_t poll()
291 {
292 6x return sched_->poll();
293 }
294
295 /** Process at most one ready work item without blocking.
296
297 This function executes at most one work item that is ready
298 to run without blocking for more work. The context is
299 stopped when there is no more outstanding work.
300
301 @note The context must be restarted with `restart()` before
302 calling this function again after it returns.
303
304 @return The number of handlers executed (0 or 1).
305 */
306 4x std::size_t poll_one()
307 {
308 4x return sched_->poll_one();
309 }
310 };
311
312 /** An executor for dispatching work to an I/O context.
313
314 The executor provides the interface for posting work items and
315 dispatching coroutines to the associated context. It satisfies
316 the `capy::Executor` concept.
317
318 Executors are lightweight handles that can be copied and compared
319 for equality. Two executors compare equal if they refer to the
320 same context.
321
322 @par Thread Safety
323 Distinct objects: Safe.@n
324 Shared objects: Safe.
325 */
326 class io_context::executor_type
327 {
328 io_context* ctx_ = nullptr;
329
330 public:
331 /** Default constructor.
332
333 Constructs an executor not associated with any context.
334 */
335 executor_type() = default;
336
337 /** Construct an executor from a context.
338
339 @param ctx The context to associate with this executor.
340 */
341 606x explicit executor_type(io_context& ctx) noexcept : ctx_(&ctx) {}
342
343 /** Return a reference to the associated execution context.
344
345 @return Reference to the context.
346 */
347 1278x io_context& context() const noexcept
348 {
349 1278x return *ctx_;
350 }
351
352 /** Check if the current thread is running this executor's context.
353
354 @return `true` if `run()` is being called on this thread.
355 */
356 1290x bool running_in_this_thread() const noexcept
357 {
358 1290x return ctx_->sched_->running_in_this_thread();
359 }
360
361 /** Informs the executor that work is beginning.
362
363 Must be paired with `on_work_finished()`.
364 */
365 1433x void on_work_started() const noexcept
366 {
367 1433x ctx_->sched_->work_started();
368 1433x }
369
370 /** Informs the executor that work has completed.
371
372 @par Preconditions
373 A preceding call to `on_work_started()` on an equal executor.
374 */
375 1407x void on_work_finished() const noexcept
376 {
377 1407x ctx_->sched_->work_finished();
378 1407x }
379
380 /** Dispatch a continuation.
381
382 Returns a handle for symmetric transfer. If called from
383 within `run()`, returns `c.h`. Otherwise posts the
384 enclosing continuation_op as a scheduler_op for later
385 execution and returns `std::noop_coroutine()`.
386
387 @param c The continuation to dispatch. Must be the `cont`
388 member of a `detail::continuation_op`.
389
390 @return A handle for symmetric transfer or `std::noop_coroutine()`.
391 */
392 1288x std::coroutine_handle<> dispatch(capy::continuation& c) const
393 {
394 1288x if (running_in_this_thread())
395 600x return c.h;
396 688x post(c);
397 688x return std::noop_coroutine();
398 }
399
400 /** Post a continuation for deferred execution.
401
402 If the continuation is backed by a continuation_op
403 (tagged), posts it directly as a scheduler_op — zero
404 heap allocation. Otherwise falls back to the
405 heap-allocating post(coroutine_handle<>) path.
406 */
407 9323x void post(capy::continuation& c) const
408 {
409 9323x auto* op = detail::continuation_op::try_from_continuation(c);
410 9323x if (op)
411 8632x ctx_->sched_->post(op);
412 else
413 691x ctx_->sched_->post(c.h);
414 9323x }
415
416 /** Post a bare coroutine handle for deferred execution.
417
418 Heap-allocates a scheduler_op to wrap the handle. Prefer
419 posting through a continuation_op-backed continuation when
420 the continuation has suitable lifetime.
421
422 @param h The coroutine handle to post.
423 */
424 1426x void post(std::coroutine_handle<> h) const
425 {
426 1426x ctx_->sched_->post(h);
427 1426x }
428
429 /** Compare two executors for equality.
430
431 @return `true` if both executors refer to the same context.
432 */
433 1x bool operator==(executor_type const& other) const noexcept
434 {
435 1x return ctx_ == other.ctx_;
436 }
437
438 /** Compare two executors for inequality.
439
440 @return `true` if the executors refer to different contexts.
441 */
442 bool operator!=(executor_type const& other) const noexcept
443 {
444 return ctx_ != other.ctx_;
445 }
446 };
447
448 inline io_context::executor_type
449 606x io_context::get_executor() const noexcept
450 {
451 606x return executor_type(const_cast<io_context&>(*this));
452 }
453
454 } // namespace boost::corosio
455
456 #endif // BOOST_COROSIO_IO_CONTEXT_HPP
457