relibc/header/sys_select/
mod.rs1use core::mem;
6
7use cbitset::BitSet;
8
9use crate::{
10 fs::File,
11 header::{
12 bits_sigset_t::sigset_t,
13 errno,
14 sys_epoll::{
15 EPOLL_CLOEXEC, EPOLL_CTL_ADD, EPOLLERR, EPOLLIN, EPOLLOUT, epoll_create1, epoll_ctl,
16 epoll_data, epoll_event, epoll_pwait,
17 },
18 time::timespec,
19 },
20 platform::types::{c_int, suseconds_t},
21};
22
23pub use crate::header::bits_timeval::timeval;
24
25pub const FD_SETSIZE: usize = 1024;
32type FdBitSet = BitSet<[u64; FD_SETSIZE / (8 * mem::size_of::<u64>())]>;
33
34#[allow(non_camel_case_types)]
37#[repr(C)]
38pub struct fd_set {
39 pub fds_bits: FdBitSet,
40}
41
42#[allow(clippy::needless_update)]
43pub unsafe fn select_epoll(
44 nfds: c_int,
45 readfds: Option<&mut fd_set>,
46 writefds: Option<&mut fd_set>,
47 exceptfds: Option<&mut fd_set>,
48 timeout: Option<&mut timeval>,
49 sigmask: *const sigset_t,
50) -> c_int {
51 if nfds < 0 || nfds > FD_SETSIZE as i32 {
52 crate::platform::ERRNO.set(errno::EINVAL);
53 return -1;
54 };
55
56 let ep = {
57 let epfd = epoll_create1(EPOLL_CLOEXEC);
58 if epfd < 0 {
59 return -1;
60 }
61 File::new(epfd)
62 };
63 let mut read_bitset: Option<&mut FdBitSet> = readfds.map(|fd_set| &mut fd_set.fds_bits);
64 let mut write_bitset: Option<&mut FdBitSet> = writefds.map(|fd_set| &mut fd_set.fds_bits);
65 let mut except_bitset: Option<&mut FdBitSet> = exceptfds.map(|fd_set| &mut fd_set.fds_bits);
66
67 let mut not_epoll = 0;
69 for fd in 0..nfds {
70 let mut events = 0;
71
72 if let Some(ref fd_set) = read_bitset
73 && fd_set.contains(fd as usize)
74 {
75 events |= EPOLLIN;
76 }
77
78 if let Some(ref fd_set) = write_bitset
79 && fd_set.contains(fd as usize)
80 {
81 events |= EPOLLOUT;
82 }
83
84 if let Some(ref fd_set) = except_bitset
85 && fd_set.contains(fd as usize)
86 {
87 events |= EPOLLERR;
88 }
89
90 if events > 0 {
91 let mut event = epoll_event {
92 events,
93 data: epoll_data { fd },
94 ..Default::default() };
96 if unsafe { epoll_ctl(*ep, EPOLL_CTL_ADD, fd, &raw mut event) } < 0 {
97 if crate::platform::ERRNO.get() == errno::EPERM {
98 not_epoll += 1;
99 } else {
100 return -1;
101 }
102 } else {
103 if let Some(ref mut fd_set) = read_bitset
104 && fd_set.contains(fd as usize)
105 {
106 fd_set.remove(fd as usize);
107 }
108
109 if let Some(ref mut fd_set) = write_bitset
110 && fd_set.contains(fd as usize)
111 {
112 fd_set.remove(fd as usize);
113 }
114
115 if let Some(ref mut fd_set) = except_bitset
116 && fd_set.contains(fd as usize)
117 {
118 fd_set.remove(fd as usize);
119 }
120 }
121 }
122 }
123
124 let mut events: [epoll_event; 32] = unsafe { mem::zeroed() };
125 let epoll_timeout = if not_epoll > 0 {
126 0
128 } else {
129 match timeout {
130 Some(timeout) => {
131 let sec_ms = (timeout.tv_sec as c_int).checked_mul(1000);
132 let usec_ms = (timeout.tv_usec as c_int) / 1000;
133 match sec_ms.and_then(|s| s.checked_add(usec_ms)) {
134 Some(s) => s as c_int,
135 None => c_int::MAX,
136 }
137 }
138 None => -1,
139 }
140 };
141
142 let res = unsafe {
143 epoll_pwait(
144 *ep,
145 events.as_mut_ptr(),
146 events.len() as c_int,
147 epoll_timeout,
148 sigmask,
149 )
150 };
151 if res < 0 {
152 return -1;
153 }
154
155 let mut count = not_epoll;
156 for event in events.iter().take(res as usize) {
157 let fd = unsafe { event.data.fd };
158 if fd >= 0 && fd < FD_SETSIZE as c_int {
160 if event.events & EPOLLIN > 0
161 && let Some(ref mut fd_set) = read_bitset
162 {
163 fd_set.insert(fd as usize);
164 count += 1;
165 }
166 if event.events & EPOLLOUT > 0
167 && let Some(ref mut fd_set) = write_bitset
168 {
169 fd_set.insert(fd as usize);
170 count += 1;
171 }
172 if event.events & EPOLLERR > 0
173 && let Some(ref mut fd_set) = except_bitset
174 {
175 fd_set.insert(fd as usize);
176 count += 1;
177 }
178 }
179 }
180 count
181}
182
183#[unsafe(no_mangle)]
197pub unsafe extern "C" fn select(
198 nfds: c_int,
199 readfds: *mut fd_set,
200 writefds: *mut fd_set,
201 exceptfds: *mut fd_set,
202 timeout: *mut timeval,
203) -> c_int {
204 trace_expr!(
205 unsafe {
206 select_epoll(
207 nfds,
208 if readfds.is_null() {
209 None
210 } else {
211 Some(&mut *readfds)
212 },
213 if writefds.is_null() {
214 None
215 } else {
216 Some(&mut *writefds)
217 },
218 if exceptfds.is_null() {
219 None
220 } else {
221 Some(&mut *exceptfds)
222 },
223 if timeout.is_null() {
224 None
225 } else {
226 Some(&mut *timeout)
227 },
228 core::ptr::null(),
229 )
230 },
231 "select({}, {:p}, {:p}, {:p}, {:p})",
232 nfds,
233 readfds,
234 writefds,
235 exceptfds,
236 timeout
237 )
238}
239
240#[unsafe(no_mangle)]
252pub unsafe extern "C" fn pselect(
253 nfds: c_int,
254 readfds: *mut fd_set,
255 writefds: *mut fd_set,
256 exceptfds: *mut fd_set,
257 timeout: *const timespec,
258 sigmask: *const sigset_t,
259) -> c_int {
260 let mut micro_timeout = if timeout.is_null() {
261 None
262 } else {
263 unsafe {
264 Some(timeval {
265 tv_sec: (*timeout).tv_sec,
266 tv_usec: ((*timeout).tv_nsec / 1000) as suseconds_t,
267 })
268 }
269 };
270 trace_expr!(
271 unsafe {
272 select_epoll(
273 nfds,
274 if readfds.is_null() {
275 None
276 } else {
277 Some(&mut *readfds)
278 },
279 if writefds.is_null() {
280 None
281 } else {
282 Some(&mut *writefds)
283 },
284 if exceptfds.is_null() {
285 None
286 } else {
287 Some(&mut *exceptfds)
288 },
289 micro_timeout.as_mut(),
290 sigmask,
291 )
292 },
293 "pselect({}, {:p}, {:p}, {:p}, {:p}, {:p})",
294 nfds,
295 readfds,
296 writefds,
297 exceptfds,
298 timeout,
299 sigmask,
300 )
301}