include/boost/corosio/native/detail/select/select_scheduler.hpp

99.5% Lines (182/184) 100.0% List of functions (13/13) 68.5% Branches (126/184)
select_scheduler.hpp
f(x) Functions (13)
Function Calls Lines Branches Blocks
boost::corosio::detail::select_scheduler::register_signal_reader(int) :135 55x 100.0% 100.0% boost::corosio::detail::select_scheduler::select_scheduler(boost::capy::execution_context&, int) :158 1672x 100.0% 59.6% 100.0% boost::corosio::detail::select_scheduler::select_scheduler(boost::capy::execution_context&, int)::'lambda'(void*)::__invoke(void*) :193 2027x 100.0% 100.0% boost::corosio::detail::select_scheduler::select_scheduler(boost::capy::execution_context&, int)::'lambda'(void*)::operator void (*)(void*)() const :193 829x 100.0% 100.0% boost::corosio::detail::select_scheduler::select_scheduler(boost::capy::execution_context&, int)::'lambda'(void*)::operator()(void*) const :193 2027x 100.0% 50.0% 100.0% boost::corosio::detail::select_scheduler::~select_scheduler() :205 2487x 100.0% 50.0% 100.0% boost::corosio::detail::select_scheduler::shutdown() :214 829x 100.0% 50.0% 100.0% boost::corosio::detail::select_scheduler::register_descriptor(int, boost::corosio::detail::reactor_descriptor_state*) const :223 4165x 100.0% 87.5% 78.0% boost::corosio::detail::select_scheduler::deregister_descriptor(int) const :261 4109x 92.3% 63.6% 78.0% boost::corosio::detail::select_scheduler::notify_reactor() const :283 2262x 100.0% 100.0% boost::corosio::detail::select_scheduler::interrupt_reactor() const :289 10009x 100.0% 100.0% boost::corosio::detail::select_scheduler::calculate_timeout(long) const :296 920838x 100.0% 87.5% 90.0% boost::corosio::detail::select_scheduler::run_task(boost::corosio::detail::conditionally_enabled_mutex::scoped_lock&, boost::corosio::detail::reactor_scheduler_context&, long) :329 936241x 100.0% 76.3% 98.0%
Line Branch TLA Hits Source Code
1 //
2 // Copyright (c) 2026 Steve Gerbino
3 // Copyright (c) 2026 Michael Vandeberg
4 //
5 // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 //
8 // Official repository: https://github.com/cppalliance/corosio
9 //
10
11 #ifndef BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_SCHEDULER_HPP
12 #define BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_SCHEDULER_HPP
13
14 #include <boost/corosio/detail/platform.hpp>
15
16 #if BOOST_COROSIO_HAS_SELECT
17
18 #include <boost/corosio/detail/config.hpp>
19 #include <boost/capy/ex/execution_context.hpp>
20
21 #include <boost/corosio/native/detail/reactor/reactor_scheduler.hpp>
22 #include <boost/corosio/native/detail/reactor/reactor_signal_pipe.hpp>
23
24 #include <boost/corosio/native/detail/select/select_traits.hpp>
25 #include <boost/corosio/detail/timer_service.hpp>
26 #include <boost/corosio/native/detail/make_err.hpp>
27 #include <boost/corosio/native/detail/posix/posix_resolver_service.hpp>
28 #include <boost/corosio/native/detail/posix/posix_signal_service.hpp>
29 #include <boost/corosio/native/detail/posix/posix_stream_file_service.hpp>
30 #include <boost/corosio/native/detail/posix/posix_random_access_file_service.hpp>
31
32 #include <boost/corosio/detail/except.hpp>
33
34 #include <sys/select.h>
35 #include <unistd.h>
36 #include <errno.h>
37 #include <fcntl.h>
38
39 #include <atomic>
40 #include <chrono>
41 #include <cstdint>
42 #include <limits>
43 #include <mutex>
44 #include <new>
45 #include <unordered_map>
46
47 namespace boost::corosio::detail {
48
49 struct select_op;
50
51 /** POSIX scheduler using select() for I/O multiplexing.
52
53 This scheduler implements the scheduler interface using the POSIX select()
54 call for I/O event notification. It inherits the shared reactor threading
55 model from reactor_scheduler: signal state machine, inline completion
56 budget, work counting, and the do_one event loop.
57
58 The design mirrors epoll_scheduler for behavioral consistency:
59 - Same single-reactor thread coordination model
60 - Same deferred I/O pattern (reactor marks ready; workers do I/O)
61 - Same timer integration pattern
62
63 Known Limitations:
64 - FD_SETSIZE (~1024) limits maximum concurrent connections
65 - O(n) scanning: rebuilds fd_sets each iteration
66 - Level-triggered only (no edge-triggered mode)
67
68 @par Thread Safety
69 All public member functions are thread-safe.
70 */
71 class BOOST_COROSIO_DECL select_scheduler final : public reactor_scheduler
72 {
73 public:
74 /** Construct the scheduler.
75
76 Creates a self-pipe for reactor interruption.
77
78 @param ctx Reference to the owning execution_context.
79 @param concurrency_hint Hint for expected thread count (unused).
80 */
81 select_scheduler(capy::execution_context& ctx, int concurrency_hint = -1);
82
83 /// Destroy the scheduler.
84 ~select_scheduler() override;
85
86 select_scheduler(select_scheduler const&) = delete;
87 select_scheduler& operator=(select_scheduler const&) = delete;
88
89 /// Shut down the scheduler, draining pending operations.
90 void shutdown() override;
91
92 /** Return the maximum file descriptor value supported.
93
94 Returns FD_SETSIZE - 1, the maximum fd value that can be
95 monitored by select(). Operations with fd >= FD_SETSIZE
96 will fail with EINVAL.
97
98 @return The maximum supported file descriptor value.
99 */
100 static constexpr int max_fd() noexcept
101 {
102 return FD_SETSIZE - 1;
103 }
104
105 /** Register a descriptor for persistent monitoring.
106
107 The fd is added to the registered_descs_ map and will be
108 included in subsequent select() calls. The reactor is
109 interrupted so a blocked select() rebuilds its fd_sets.
110
111 @param fd The file descriptor to register.
112 @param desc Pointer to descriptor state for this fd.
113
114 @return The error if the fd cannot be tracked, otherwise a
115 default constructed error code.
116 */
117 std::error_code
118 register_descriptor(int fd, reactor_descriptor_state* desc) const;
119
120 /** Deregister a persistently registered descriptor.
121
122 @param fd The file descriptor to deregister.
123 */
124 void deregister_descriptor(int fd) const;
125
126 /** Interrupt the reactor so it rebuilds its fd_sets.
127
128 Called when a write, connect, or write-wait op is registered
129 after the reactor's snapshot was taken. Without this,
130 select() may block not watching for writability on the fd.
131 */
132 void notify_reactor() const;
133
134 /// Watch the read end of the POSIX signal self-pipe (see scheduler.hpp).
135 55x [[nodiscard]] std::error_code register_signal_reader(int read_fd) override
136 {
137 55x return register_descriptor(read_fd, signal_pipe_reader_.arm());
138 }
139
140 private:
141 void run_task(lock_type& lock, context_type& ctx, long timeout_us) override;
142 void interrupt_reactor() const override;
143 long calculate_timeout(long requested_timeout_us) const;
144
145 // Watches the global signal self-pipe's read end (armed lazily by
146 // register_signal_reader on the first signal registration).
147 reactor_signal_pipe_reader signal_pipe_reader_;
148
149 // Self-pipe for interrupting select()
150 int pipe_fds_[2]; // [0]=read, [1]=write
151
152 // Per-fd tracking for fd_set building
153 mutable std::unordered_map<int, reactor_descriptor_state*>
154 registered_descs_;
155 mutable int max_fd_ = -1;
156 };
157
158 3344x inline select_scheduler::select_scheduler(capy::execution_context& ctx, int)
159 836x : pipe_fds_{-1, -1}
160 836x , max_fd_(-1)
161 1672x {
162
3/4
✓ Branch 0 taken 836 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 835 times.
836x if (::pipe(pipe_fds_) < 0)
163
2/4
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
1x detail::throw_system_error(make_err(errno), "pipe");
164
165
2/2
✓ Branch 0 taken 829 times.
✓ Branch 1 taken 1667 times.
2496x for (int i = 0; i < 2; ++i)
166 {
167
1/2
✓ Branch 0 taken 1667 times.
✗ Branch 1 not taken.
1667x int flags = ::fcntl(pipe_fds_[i], F_GETFL, 0);
168
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1665 times.
1667x if (flags == -1)
169 {
170
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2x int errn = errno;
171
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2x ::close(pipe_fds_[0]);
172
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2x ::close(pipe_fds_[1]);
173
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2x detail::throw_system_error(make_err(errn), "fcntl F_GETFL");
174 }
175
3/4
✓ Branch 0 taken 1665 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 1663 times.
1665x if (::fcntl(pipe_fds_[i], F_SETFL, flags | O_NONBLOCK) == -1)
176 {
177
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2x int errn = errno;
178
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2x ::close(pipe_fds_[0]);
179
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2x ::close(pipe_fds_[1]);
180
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2x detail::throw_system_error(make_err(errn), "fcntl F_SETFL");
181 }
182
3/4
✓ Branch 0 taken 1663 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 1661 times.
1663x if (::fcntl(pipe_fds_[i], F_SETFD, FD_CLOEXEC) == -1)
183 {
184
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2x int errn = errno;
185
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2x ::close(pipe_fds_[0]);
186
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2x ::close(pipe_fds_[1]);
187
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2x detail::throw_system_error(make_err(errn), "fcntl F_SETFD");
188 }
189 1661x }
190
191
1/2
✓ Branch 0 taken 829 times.
✗ Branch 1 not taken.
829x timer_svc_ = &get_timer_service(ctx, *this);
192
2/4
✓ Branch 0 taken 829 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 829 times.
✗ Branch 3 not taken.
1658x timer_svc_->set_on_earliest_changed(
193 2856x timer_service::callback(this, [](void* p) {
194 2027x static_cast<select_scheduler*>(p)->interrupt_reactor();
195 2027x }));
196
197
1/2
✓ Branch 0 taken 829 times.
✗ Branch 1 not taken.
829x get_resolver_service(ctx, *this);
198
1/2
✓ Branch 0 taken 829 times.
✗ Branch 1 not taken.
829x get_signal_service(ctx, *this);
199
1/2
✓ Branch 0 taken 829 times.
✗ Branch 1 not taken.
829x get_stream_file_service(ctx, *this);
200
1/2
✓ Branch 0 taken 829 times.
✗ Branch 1 not taken.
829x get_random_access_file_service(ctx, *this);
201
202 829x completed_ops_.push(&task_op_);
203 1672x }
204
205 2487x inline select_scheduler::~select_scheduler()
206 1658x {
207
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 829 times.
829x if (pipe_fds_[0] >= 0)
208
1/2
✓ Branch 0 taken 829 times.
✗ Branch 1 not taken.
829x ::close(pipe_fds_[0]);
209
1/2
✓ Branch 0 taken 829 times.
✗ Branch 1 not taken.
829x if (pipe_fds_[1] >= 0)
210
1/2
✓ Branch 0 taken 829 times.
✗ Branch 1 not taken.
829x ::close(pipe_fds_[1]);
211 2487x }
212
213 inline void
214 829x select_scheduler::shutdown()
215 {
216 829x shutdown_drain();
217
218
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 829 times.
829x if (pipe_fds_[1] >= 0)
219 829x interrupt_reactor();
220 829x }
221
222 inline std::error_code
223 4165x select_scheduler::register_descriptor(
224 int fd, reactor_descriptor_state* desc) const
225 {
226
3/4
✓ Branch 0 taken 4165 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 4164 times.
4165x if (fd < 0 || fd >= FD_SETSIZE)
227 1x return make_err(EMFILE);
228
229 4164x desc->registered_events = reactor_event_read | reactor_event_write;
230 4164x desc->fd = fd;
231 4164x desc->scheduler_ = this;
232 4164x desc->mutex.set_enabled(reactor_io_locking_);
233 4164x desc->ready_events_.store(0, std::memory_order_relaxed);
234
235 {
236 4164x conditionally_enabled_mutex::scoped_lock lock(desc->mutex);
237 4164x desc->impl_ref_.reset();
238 4164x desc->read_ready = false;
239 4164x desc->write_ready = false;
240 4164x }
241
242 {
243 4164x mutex_type::scoped_lock lock(mutex_);
244 try
245 {
246
2/2
✓ Branch 0 taken 4163 times.
✓ Branch 1 taken 1 time.
4164x registered_descs_[fd] = desc;
247 4164x }
248 catch (std::bad_alloc const&)
249 {
250 1x return make_err(ENOMEM);
251 1x }
252
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 4143 times.
4163x if (fd > max_fd_)
253 4143x max_fd_ = fd;
254 4164x }
255
256 4163x interrupt_reactor();
257 4163x return {};
258 4166x }
259
260 inline void
261 4109x select_scheduler::deregister_descriptor(int fd) const
262 {
263 4109x mutex_type::scoped_lock lock(mutex_);
264
265
1/2
✓ Branch 0 taken 4109 times.
✗ Branch 1 not taken.
4109x auto it = registered_descs_.find(fd);
266
2/4
✓ Branch 0 taken 4109 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4109 times.
✗ Branch 3 not taken.
4109x if (it == registered_descs_.end())
267 return;
268
269
1/2
✓ Branch 0 taken 4109 times.
✗ Branch 1 not taken.
4109x registered_descs_.erase(it);
270
271
2/2
✓ Branch 0 taken 233 times.
✓ Branch 1 taken 3876 times.
4109x if (fd == max_fd_)
272 {
273 3876x max_fd_ = pipe_fds_[0];
274
6/10
✓ Branch 0 taken 7386 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3533 times.
✓ Branch 3 taken 3853 times.
✓ Branch 4 taken 3480 times.
✓ Branch 4 taken 30 times.
✗ Branch 5 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 3480 times.
✗ Branch 7 not taken.
7386x for (auto& [registered_fd, state] : registered_descs_)
275 {
276
2/2
✓ Branch 0 taken 87 times.
✓ Branch 1 taken 3423 times.
3510x if (registered_fd > max_fd_)
277 3423x max_fd_ = registered_fd;
278 }
279 3876x }
280 4109x }
281
282 inline void
283 2262x select_scheduler::notify_reactor() const
284 {
285 2262x interrupt_reactor();
286 2262x }
287
288 inline void
289 10009x select_scheduler::interrupt_reactor() const
290 {
291 10009x char byte = 1;
292 10009x [[maybe_unused]] auto r = ::write(pipe_fds_[1], &byte, 1);
293 10009x }
294
295 inline long
296 920838x select_scheduler::calculate_timeout(long requested_timeout_us) const
297 {
298
1/2
✓ Branch 0 taken 920838 times.
✗ Branch 1 not taken.
920838x if (requested_timeout_us == 0)
299 return 0; // LCOV_EXCL_LINE run_task passes 0 via task_interrupted_, never through this argument
300
301 920838x auto nearest = timer_svc_->nearest_expiry();
302
2/2
✓ Branch 0 taken 1894 times.
✓ Branch 1 taken 918944 times.
920838x if (nearest == timer_service::time_point::max())
303 1894x return requested_timeout_us;
304
305 918944x auto now = std::chrono::steady_clock::now();
306
2/2
✓ Branch 0 taken 117 times.
✓ Branch 1 taken 918827 times.
918944x if (nearest <= now)
307 117x return 0;
308
309 918827x auto timer_timeout_us =
310 918827x std::chrono::duration_cast<std::chrono::microseconds>(nearest - now)
311 918827x .count();
312
313 918827x constexpr auto long_max =
314 static_cast<long long>((std::numeric_limits<long>::max)());
315 918827x auto capped_timer_us =
316 1837654x (std::min)((std::max)(static_cast<long long>(timer_timeout_us),
317 918827x static_cast<long long>(0)),
318 long_max);
319
320
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 918825 times.
918827x if (requested_timeout_us < 0)
321 918825x return static_cast<long>(capped_timer_us);
322
323 2x return static_cast<long>(
324 2x (std::min)(static_cast<long long>(requested_timeout_us),
325 capped_timer_us));
326 920838x }
327
328 inline void
329 936241x select_scheduler::run_task(lock_type& lock, context_type& ctx, long timeout_us)
330 {
331 936241x long effective_timeout_us =
332
2/2
✓ Branch 0 taken 15403 times.
✓ Branch 1 taken 920838 times.
936241x task_interrupted_ ? 0 : calculate_timeout(timeout_us);
333
334 // Snapshot registered descriptors while holding lock.
335 // Record which fds need write monitoring to avoid a hot loop:
336 // select is level-triggered so writable sockets (nearly always
337 // writable) would cause select() to return immediately every
338 // iteration if unconditionally added to write_fds. Membership
339 // stays opt-in: a parked write wait opts in the same way a
340 // parked write or connect op does.
341 struct fd_entry
342 {
343 int fd;
344 reactor_descriptor_state* desc;
345 bool needs_write;
346 };
347 fd_entry snapshot[FD_SETSIZE];
348 936241x int snapshot_count = 0;
349
350
2/2
✓ Branch 0 taken 1539686 times.
✓ Branch 1 taken 936241 times.
2475927x for (auto& [fd, desc] : registered_descs_)
351 {
352
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1539686 times.
1539686x if (snapshot_count < FD_SETSIZE)
353 {
354 1539686x conditionally_enabled_mutex::scoped_lock desc_lock(desc->mutex);
355 1539686x snapshot[snapshot_count].fd = fd;
356 1539686x snapshot[snapshot_count].desc = desc;
357 1539686x snapshot[snapshot_count].needs_write =
358
4/4
✓ Branch 0 taken 1538528 times.
✓ Branch 1 taken 1158 times.
✓ Branch 2 taken 3414 times.
✓ Branch 3 taken 1535114 times.
1539686x (desc->write_op || desc->connect_op || desc->wait_write_op);
359 1539686x ++snapshot_count;
360 1539686x }
361 }
362
363
2/2
✓ Branch 0 taken 15402 times.
✓ Branch 1 taken 920839 times.
936241x if (lock.owns_lock())
364 920839x lock.unlock();
365
366 936241x task_cleanup on_exit{this, &lock, ctx};
367
368 fd_set read_fds, write_fds, except_fds;
369 936241x FD_ZERO(&read_fds);
370 936241x FD_ZERO(&write_fds);
371 936241x FD_ZERO(&except_fds);
372
373
1/2
✓ Branch 0 taken 936241 times.
✗ Branch 1 not taken.
936241x FD_SET(pipe_fds_[0], &read_fds);
374 936241x int nfds = pipe_fds_[0];
375
376
2/2
✓ Branch 0 taken 936241 times.
✓ Branch 1 taken 1539686 times.
2475927x for (int i = 0; i < snapshot_count; ++i)
377 {
378 1539686x int fd = snapshot[i].fd;
379
1/2
✓ Branch 0 taken 1539686 times.
✗ Branch 1 not taken.
1539686x FD_SET(fd, &read_fds);
380
2/2
✓ Branch 0 taken 4577 times.
✓ Branch 1 taken 1535109 times.
1539686x if (snapshot[i].needs_write)
381
1/2
✓ Branch 0 taken 4577 times.
✗ Branch 1 not taken.
4577x FD_SET(fd, &write_fds);
382
1/2
✓ Branch 0 taken 1539686 times.
✗ Branch 1 not taken.
1539686x FD_SET(fd, &except_fds);
383
2/2
✓ Branch 0 taken 603919 times.
✓ Branch 1 taken 935767 times.
1539686x if (fd > nfds)
384 935767x nfds = fd;
385 1539686x }
386
387 struct timeval tv;
388 936241x struct timeval* tv_ptr = nullptr;
389
2/2
✓ Branch 0 taken 1876 times.
✓ Branch 1 taken 934365 times.
936241x if (effective_timeout_us >= 0)
390 {
391 934365x tv.tv_sec = effective_timeout_us / 1000000;
392 934365x tv.tv_usec = effective_timeout_us % 1000000;
393 934365x tv_ptr = &tv;
394 934365x }
395
396
1/2
✓ Branch 0 taken 936241 times.
✗ Branch 1 not taken.
936241x int ready = ::select(nfds + 1, &read_fds, &write_fds, &except_fds, tv_ptr);
397
398 // EINTR: signal interrupted select(), just retry.
399 // EBADF: an fd was closed between snapshot and select(); retry
400 // with a fresh snapshot from registered_descs_.
401 // Both fall through with no ready descriptors rather than
402 // returning: the caller handed this function an owned lock that
403 // only the epilogue below re-acquires.
404
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 936238 times.
936241x if (ready < 0)
405 {
406
6/8
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 time.
✓ Branch 7 taken 1 time.
3x if (errno != EINTR && errno != EBADF)
407
2/4
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
1x detail::throw_system_error(make_err(errno), "select");
408 2x ready = 0;
409 2x }
410
411 // Process timers outside the lock
412
1/2
✓ Branch 0 taken 936240 times.
✗ Branch 1 not taken.
936240x timer_svc_->process_expired();
413
414 936240x ready_queue local_ops;
415
416
2/2
✓ Branch 0 taken 930965 times.
✓ Branch 1 taken 5275 times.
936240x if (ready > 0)
417 {
418
3/4
✓ Branch 0 taken 930965 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 926019 times.
✓ Branch 3 taken 4946 times.
930965x if (FD_ISSET(pipe_fds_[0], &read_fds))
419 {
420 char buf[256];
421
3/4
✓ Branch 0 taken 9892 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4946 times.
✓ Branch 3 taken 4946 times.
9892x while (::read(pipe_fds_[0], buf, sizeof(buf)) > 0)
422 {
423 }
424 4946x }
425
426
2/2
✓ Branch 0 taken 1533060 times.
✓ Branch 1 taken 930965 times.
2464025x for (int i = 0; i < snapshot_count; ++i)
427 {
428 1533060x int fd = snapshot[i].fd;
429 1533060x reactor_descriptor_state* desc = snapshot[i].desc;
430
431 1533060x std::uint32_t flags = 0;
432
3/4
✓ Branch 0 taken 1533060 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 975613 times.
✓ Branch 3 taken 557447 times.
1533060x if (FD_ISSET(fd, &read_fds))
433 975613x flags |= reactor_event_read;
434
3/4
✓ Branch 0 taken 1533060 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1530802 times.
✓ Branch 3 taken 2258 times.
1533060x if (FD_ISSET(fd, &write_fds))
435 2258x flags |= reactor_event_write;
436
3/4
✓ Branch 0 taken 1533060 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1533049 times.
✓ Branch 3 taken 11 times.
1533060x if (FD_ISSET(fd, &except_fds))
437 11x flags |= reactor_event_error;
438
439
2/2
✓ Branch 0 taken 977863 times.
✓ Branch 1 taken 555197 times.
1533060x if (flags == 0)
440 555197x continue;
441
442 977863x desc->add_ready_events(flags);
443
444 977863x bool expected = false;
445
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 977863 times.
977863x if (desc->is_enqueued_.compare_exchange_strong(
446 expected, true, std::memory_order_release,
447 std::memory_order_relaxed))
448 {
449 977863x local_ops.push(desc);
450 977863x }
451 977863x }
452 930965x }
453
454
1/2
✓ Branch 0 taken 936240 times.
✗ Branch 1 not taken.
936240x lock.lock();
455
456 936240x completed_ops_.splice(local_ops);
457 936241x }
458
459 } // namespace boost::corosio::detail
460
461 #endif // BOOST_COROSIO_HAS_SELECT
462
463 #endif // BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_SCHEDULER_HPP
464