libs/corosio/include/boost/corosio/basic_io_context.hpp

90.9% Lines (60/66) 95.2% Functions (20/21) 76.9% Branches (20/26)
libs/corosio/include/boost/corosio/basic_io_context.hpp
Line Branch Hits Source Code
1 //
2 // Copyright (c) 2026 Steve Gerbino
3 //
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)
6 //
7 // Official repository: https://github.com/cppalliance/corosio
8 //
9
10 #ifndef BOOST_COROSIO_BASIC_IO_CONTEXT_HPP
11 #define BOOST_COROSIO_BASIC_IO_CONTEXT_HPP
12
13 #include <boost/corosio/detail/config.hpp>
14 #include <boost/corosio/detail/scheduler.hpp>
15 #include <boost/capy/coro.hpp>
16 #include <boost/capy/ex/execution_context.hpp>
17
18 #include <chrono>
19 #include <cstddef>
20 #include <limits>
21
22 namespace boost::corosio {
23
24 /** Base class for I/O context implementations.
25
26 This class provides the common API for all I/O context types.
27 Concrete context implementations (epoll_context, iocp_context, etc.)
28 inherit from this class to gain the standard io_context interface.
29
30 @par Thread Safety
31 Distinct objects: Safe.@n
32 Shared objects: Safe, if using a concurrency hint greater than 1.
33 */
34 class BOOST_COROSIO_DECL basic_io_context : public capy::execution_context
35 {
36 public:
37 /** The executor type for this context. */
38 class executor_type;
39
40 /** Return an executor for this context.
41
42 The returned executor can be used to dispatch coroutines
43 and post work items to this context.
44
45 @return An executor associated with this context.
46 */
47 executor_type
48 get_executor() const noexcept;
49
50 /** Signal the context to stop processing.
51
52 This causes `run()` to return as soon as possible. Any pending
53 work items remain queued.
54 */
55 void
56 1 stop()
57 {
58 1 sched_->stop();
59 1 }
60
61 /** Return whether the context has been stopped.
62
63 @return `true` if `stop()` has been called and `restart()`
64 has not been called since.
65 */
66 bool
67 17 stopped() const noexcept
68 {
69 17 return sched_->stopped();
70 }
71
72 /** Restart the context after being stopped.
73
74 This function must be called before `run()` can be called
75 again after `stop()` has been called.
76 */
77 void
78 83 restart()
79 {
80 83 sched_->restart();
81 83 }
82
83 /** Process all pending work items.
84
85 This function blocks until all pending work items have been
86 executed or `stop()` is called. The context is stopped
87 when there is no more outstanding work.
88
89 @note The context must be restarted with `restart()` before
90 calling this function again after it returns.
91
92 @return The number of handlers executed.
93 */
94 std::size_t
95 264 run()
96 {
97 264 return sched_->run();
98 }
99
100 /** Process at most one pending work item.
101
102 This function blocks until one work item has been executed
103 or `stop()` is called. The context is stopped when there
104 is no more outstanding work.
105
106 @note The context must be restarted with `restart()` before
107 calling this function again after it returns.
108
109 @return The number of handlers executed (0 or 1).
110 */
111 std::size_t
112 2 run_one()
113 {
114 2 return sched_->run_one();
115 }
116
117 /** Process work items for the specified duration.
118
119 This function blocks until work items have been executed for
120 the specified duration, or `stop()` is called. The context
121 is stopped when there is no more outstanding work.
122
123 @note The context must be restarted with `restart()` before
124 calling this function again after it returns.
125
126 @param rel_time The duration for which to process work.
127
128 @return The number of handlers executed.
129 */
130 template<class Rep, class Period>
131 std::size_t
132 4 run_for(std::chrono::duration<Rep, Period> const& rel_time)
133 {
134
2/2
✓ Branch 2 taken 4 times.
✓ Branch 5 taken 4 times.
4 return run_until(std::chrono::steady_clock::now() + rel_time);
135 }
136
137 /** Process work items until the specified time.
138
139 This function blocks until the specified time is reached
140 or `stop()` is called. The context is stopped when there
141 is no more outstanding work.
142
143 @note The context must be restarted with `restart()` before
144 calling this function again after it returns.
145
146 @param abs_time The time point until which to process work.
147
148 @return The number of handlers executed.
149 */
150 template<class Clock, class Duration>
151 std::size_t
152 4 run_until(std::chrono::time_point<Clock, Duration> const& abs_time)
153 {
154 4 std::size_t n = 0;
155
2/2
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 4 times.
9 while (run_one_until(abs_time))
156
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 if (n != (std::numeric_limits<std::size_t>::max)())
157 5 ++n;
158 4 return n;
159 }
160
161 /** Process at most one work item for the specified duration.
162
163 This function blocks until one work item has been executed,
164 the specified duration has elapsed, or `stop()` is called.
165 The context is stopped when there is no more outstanding work.
166
167 @note The context must be restarted with `restart()` before
168 calling this function again after it returns.
169
170 @param rel_time The duration for which the call may block.
171
172 @return The number of handlers executed (0 or 1).
173 */
174 template<class Rep, class Period>
175 std::size_t
176 2 run_one_for(std::chrono::duration<Rep, Period> const& rel_time)
177 {
178
2/2
✓ Branch 2 taken 2 times.
✓ Branch 5 taken 2 times.
2 return run_one_until(std::chrono::steady_clock::now() + rel_time);
179 }
180
181 /** Process at most one work item until the specified time.
182
183 This function blocks until one work item has been executed,
184 the specified time is reached, or `stop()` is called.
185 The context is stopped when there is no more outstanding work.
186
187 @note The context must be restarted with `restart()` before
188 calling this function again after it returns.
189
190 @param abs_time The time point until which the call may block.
191
192 @return The number of handlers executed (0 or 1).
193 */
194 template<class Clock, class Duration>
195 std::size_t
196 13 run_one_until(std::chrono::time_point<Clock, Duration> const& abs_time)
197 {
198 13 typename Clock::time_point now = Clock::now();
199
2/3
✓ Branch 1 taken 13 times.
✓ Branch 4 taken 13 times.
✗ Branch 5 not taken.
13 while (now < abs_time)
200 {
201
1/1
✓ Branch 1 taken 13 times.
13 auto rel_time = abs_time - now;
202
2/3
✓ Branch 2 taken 13 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 13 times.
13 if (rel_time > std::chrono::seconds(1))
203 rel_time = std::chrono::seconds(1);
204
205
1/1
✓ Branch 1 taken 13 times.
13 std::size_t s = sched_->wait_one(
206 static_cast<long>(std::chrono::duration_cast<
207
1/1
✓ Branch 1 taken 13 times.
13 std::chrono::microseconds>(rel_time).count()));
208
209
4/6
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 7 times.
✓ Branch 3 taken 6 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 13 times.
✗ Branch 6 not taken.
13 if (s || stopped())
210 13 return s;
211
212 now = Clock::now();
213 }
214 return 0;
215 }
216
217 /** Process all ready work items without blocking.
218
219 This function executes all work items that are ready to run
220 without blocking for more work. The context is stopped
221 when there is no more outstanding work.
222
223 @note The context must be restarted with `restart()` before
224 calling this function again after it returns.
225
226 @return The number of handlers executed.
227 */
228 std::size_t
229 2 poll()
230 {
231 2 return sched_->poll();
232 }
233
234 /** Process at most one ready work item without blocking.
235
236 This function executes at most one work item that is ready
237 to run without blocking for more work. The context is
238 stopped when there is no more outstanding work.
239
240 @note The context must be restarted with `restart()` before
241 calling this function again after it returns.
242
243 @return The number of handlers executed (0 or 1).
244 */
245 std::size_t
246 4 poll_one()
247 {
248 4 return sched_->poll_one();
249 }
250
251 protected:
252 /** Default constructor.
253
254 Derived classes must set sched_ in their constructor body.
255 */
256 304 basic_io_context()
257 304 : sched_(nullptr)
258 {
259 304 }
260
261 detail::scheduler* sched_;
262 };
263
264 //------------------------------------------------------------------------------
265
266 /** An executor for dispatching work to an I/O context.
267
268 The executor provides the interface for posting work items and
269 dispatching coroutines to the associated context. It satisfies
270 the `capy::Executor` concept.
271
272 Executors are lightweight handles that can be copied and compared
273 for equality. Two executors compare equal if they refer to the
274 same context.
275
276 @par Thread Safety
277 Distinct objects: Safe.@n
278 Shared objects: Safe.
279 */
280 class basic_io_context::executor_type
281 {
282 basic_io_context* ctx_ = nullptr;
283
284 public:
285 /** Default constructor.
286
287 Constructs an executor not associated with any context.
288 */
289 executor_type() = default;
290
291 /** Construct an executor from a context.
292
293 @param ctx The context to associate with this executor.
294 */
295 explicit
296 295 executor_type(basic_io_context& ctx) noexcept
297 295 : ctx_(&ctx)
298 {
299 295 }
300
301 /** Return a reference to the associated execution context.
302
303 @return Reference to the context.
304 */
305 basic_io_context&
306 837 context() const noexcept
307 {
308 837 return *ctx_;
309 }
310
311 /** Check if the current thread is running this executor's context.
312
313 @return `true` if `run()` is being called on this thread.
314 */
315 bool
316 171229 running_in_this_thread() const noexcept
317 {
318 171229 return ctx_->sched_->running_in_this_thread();
319 }
320
321 /** Informs the executor that work is beginning.
322
323 Must be paired with `on_work_finished()`.
324 */
325 void
326 26 on_work_started() const noexcept
327 {
328 26 ctx_->sched_->on_work_started();
329 26 }
330
331 /** Informs the executor that work has completed.
332
333 @par Preconditions
334 A preceding call to `on_work_started()` on an equal executor.
335 */
336 void
337 on_work_finished() const noexcept
338 {
339 ctx_->sched_->on_work_finished();
340 }
341
342 /** Dispatch a coroutine handle.
343
344 This is the executor interface for capy coroutines. If called
345 from within `run()`, resumes the coroutine inline via a normal
346 function call. Otherwise posts the coroutine for later execution.
347
348 @param h The coroutine handle to dispatch.
349 */
350 void
351 171227 dispatch(capy::coro h) const
352 {
353
2/2
✓ Branch 1 taken 170863 times.
✓ Branch 2 taken 364 times.
171227 if (running_in_this_thread())
354 170863 h.resume();
355 else
356 364 ctx_->sched_->post(h);
357 171227 }
358
359 /** Post a coroutine for deferred execution.
360
361 The coroutine will be resumed during a subsequent call to
362 `run()`.
363
364 @param h The coroutine handle to post.
365 */
366 void
367 1443 post(capy::coro h) const
368 {
369 1443 ctx_->sched_->post(h);
370 1443 }
371
372 /** Compare two executors for equality.
373
374 @return `true` if both executors refer to the same context.
375 */
376 bool
377 1 operator==(executor_type const& other) const noexcept
378 {
379 1 return ctx_ == other.ctx_;
380 }
381
382 /** Compare two executors for inequality.
383
384 @return `true` if the executors refer to different contexts.
385 */
386 bool
387 operator!=(executor_type const& other) const noexcept
388 {
389 return ctx_ != other.ctx_;
390 }
391 };
392
393 //------------------------------------------------------------------------------
394
395 inline
396 basic_io_context::executor_type
397 295 basic_io_context::
398 get_executor() const noexcept
399 {
400 295 return executor_type(const_cast<basic_io_context&>(*this));
401 }
402
403 } // namespace boost::corosio
404
405 #endif // BOOST_COROSIO_BASIC_IO_CONTEXT_HPP
406