rustix/event/
select.rs
1#![allow(unsafe_code)]
7
8#[cfg(any(linux_like, target_os = "wasi"))]
9use crate::backend::c;
10use crate::fd::RawFd;
11use crate::{backend, io};
12#[cfg(any(windows, target_os = "wasi"))]
13use core::mem::{align_of, size_of};
14#[cfg(any(windows, target_os = "wasi"))]
15use core::slice;
16
17pub use crate::timespec::{Nsecs, Secs, Timespec};
18
19#[cfg(target_os = "wasi")]
23#[repr(C)]
24struct FD_SET {
25 fd_count: usize,
27 fd_array: [i32; c::FD_SETSIZE],
29}
30
31#[cfg(windows)]
32use windows_sys::Win32::Networking::WinSock::FD_SET;
33
34#[cfg(any(
36 windows,
37 all(
38 target_pointer_width = "64",
39 any(target_os = "freebsd", target_os = "dragonfly")
40 )
41))]
42#[repr(transparent)]
43#[derive(Copy, Clone, Default)]
44pub struct FdSetElement(pub(crate) u64);
45
46#[cfg(linux_like)]
48#[repr(transparent)]
49#[derive(Copy, Clone, Default)]
50pub struct FdSetElement(pub(crate) c::c_ulong);
51
52#[cfg(not(any(
54 linux_like,
55 windows,
56 target_os = "wasi",
57 all(
58 target_pointer_width = "64",
59 any(target_os = "freebsd", target_os = "dragonfly")
60 )
61)))]
62#[repr(transparent)]
63#[derive(Copy, Clone, Default)]
64pub struct FdSetElement(pub(crate) u32);
65
66#[cfg(target_os = "wasi")]
68#[repr(transparent)]
69#[derive(Copy, Clone, Default)]
70pub struct FdSetElement(pub(crate) usize);
71
72pub unsafe fn select(
122 nfds: i32,
123 readfds: Option<&mut [FdSetElement]>,
124 writefds: Option<&mut [FdSetElement]>,
125 exceptfds: Option<&mut [FdSetElement]>,
126 timeout: Option<&Timespec>,
127) -> io::Result<i32> {
128 backend::event::syscalls::select(nfds, readfds, writefds, exceptfds, timeout)
129}
130
131#[cfg(not(any(windows, target_os = "wasi")))]
132const BITS: usize = core::mem::size_of::<FdSetElement>() * 8;
133
134#[doc(alias = "FD_SET")]
136#[inline]
137pub fn fd_set_insert(fds: &mut [FdSetElement], fd: RawFd) {
138 #[cfg(not(any(windows, target_os = "wasi")))]
139 {
140 let fd = fd as usize;
141 fds[fd / BITS].0 |= 1 << (fd % BITS);
142 }
143
144 #[cfg(any(windows, target_os = "wasi"))]
145 {
146 let set = unsafe { &mut *fds.as_mut_ptr().cast::<FD_SET>() };
147 let fd_count = set.fd_count;
148 let fd_array = unsafe { slice::from_raw_parts(set.fd_array.as_ptr(), fd_count as usize) };
149
150 if !fd_array.iter().any(|p| *p as RawFd == fd) {
151 let fd_array = unsafe {
152 slice::from_raw_parts_mut(set.fd_array.as_mut_ptr(), fd_count as usize + 1)
153 };
154 set.fd_count = fd_count + 1;
155 fd_array[fd_count as usize] = fd as _;
156 }
157 }
158}
159
160#[doc(alias = "FD_CLR")]
162#[inline]
163pub fn fd_set_remove(fds: &mut [FdSetElement], fd: RawFd) {
164 #[cfg(not(any(windows, target_os = "wasi")))]
165 {
166 let fd = fd as usize;
167 fds[fd / BITS].0 &= !(1 << (fd % BITS));
168 }
169
170 #[cfg(any(windows, target_os = "wasi"))]
171 {
172 let set = unsafe { &mut *fds.as_mut_ptr().cast::<FD_SET>() };
173 let fd_count = set.fd_count;
174 let fd_array = unsafe { slice::from_raw_parts(set.fd_array.as_ptr(), fd_count as usize) };
175
176 if let Some(pos) = fd_array.iter().position(|p| *p as RawFd == fd) {
177 set.fd_count = fd_count - 1;
178 set.fd_array[pos] = *set.fd_array.last().unwrap();
179 }
180 }
181}
182
183#[inline]
186pub fn fd_set_bound(fds: &[FdSetElement]) -> RawFd {
187 #[cfg(not(any(windows, target_os = "wasi")))]
188 {
189 if let Some(position) = fds.iter().rposition(|element| element.0 != 0) {
190 let element = fds[position].0;
191 (position * BITS + (BITS - element.leading_zeros() as usize)) as RawFd
192 } else {
193 0
194 }
195 }
196
197 #[cfg(any(windows, target_os = "wasi"))]
198 {
199 let set = unsafe { &*fds.as_ptr().cast::<FD_SET>() };
200 let fd_count = set.fd_count;
201 let fd_array = unsafe { slice::from_raw_parts(set.fd_array.as_ptr(), fd_count as usize) };
202 let mut max = 0;
203 for fd in fd_array {
204 if *fd >= max {
205 max = *fd + 1;
206 }
207 }
208 max as RawFd
209 }
210}
211
212#[inline]
215pub fn fd_set_num_elements(set_count: usize, nfds: RawFd) -> usize {
216 #[cfg(any(windows, target_os = "wasi"))]
217 {
218 let _ = nfds;
219
220 fd_set_num_elements_for_fd_array(set_count)
221 }
222
223 #[cfg(not(any(windows, target_os = "wasi")))]
224 {
225 let _ = set_count;
226
227 fd_set_num_elements_for_bitvector(nfds)
228 }
229}
230
231#[cfg(any(windows, target_os = "wasi"))]
234#[inline]
235pub(crate) fn fd_set_num_elements_for_fd_array(set_count: usize) -> usize {
236 div_ceil(
239 align_of::<FD_SET>() + set_count * size_of::<RawFd>(),
240 size_of::<FdSetElement>(),
241 )
242}
243
244#[cfg(not(any(windows, target_os = "wasi")))]
247#[inline]
248pub(crate) fn fd_set_num_elements_for_bitvector(nfds: RawFd) -> usize {
249 let nfds = nfds as usize;
251 div_ceil(nfds, BITS)
252}
253
254fn div_ceil(lhs: usize, rhs: usize) -> usize {
255 let d = lhs / rhs;
256 let r = lhs % rhs;
257 if r > 0 {
258 d + 1
259 } else {
260 d
261 }
262}
263
264#[doc(alias = "FD_ISSET")]
266#[cfg(not(any(windows, target_os = "wasi")))]
267pub struct FdSetIter<'a> {
268 current: RawFd,
269 fds: &'a [FdSetElement],
270}
271
272#[doc(alias = "FD_ISSET")]
274#[cfg(any(windows, target_os = "wasi"))]
275pub struct FdSetIter<'a> {
276 current: usize,
277 fds: &'a [FdSetElement],
278}
279
280impl<'a> FdSetIter<'a> {
281 pub fn new(fds: &'a [FdSetElement]) -> Self {
283 Self { current: 0, fds }
284 }
285}
286
287#[cfg(not(any(windows, target_os = "wasi")))]
288impl<'a> Iterator for FdSetIter<'a> {
289 type Item = RawFd;
290
291 fn next(&mut self) -> Option<Self::Item> {
292 if let Some(element) = self.fds.get(self.current as usize / BITS) {
293 let shifted = element.0 >> ((self.current as usize % BITS) as u32);
295 if shifted != 0 {
296 let fd = self.current + shifted.trailing_zeros() as RawFd;
297 self.current = fd + 1;
298 return Some(fd);
299 }
300
301 if let Some(index) = self.fds[(self.current as usize / BITS) + 1..]
303 .iter()
304 .position(|element| element.0 != 0)
305 {
306 let index = index + (self.current as usize / BITS) + 1;
307 let element = self.fds[index].0;
308 let fd = (index * BITS) as RawFd + element.trailing_zeros() as RawFd;
309 self.current = fd + 1;
310 return Some(fd);
311 }
312 }
313 None
314 }
315}
316
317#[cfg(any(windows, target_os = "wasi"))]
318impl<'a> Iterator for FdSetIter<'a> {
319 type Item = RawFd;
320
321 fn next(&mut self) -> Option<Self::Item> {
322 let current = self.current;
323
324 let set = unsafe { &*self.fds.as_ptr().cast::<FD_SET>() };
325 let fd_count = set.fd_count;
326 let fd_array = unsafe { slice::from_raw_parts(set.fd_array.as_ptr(), fd_count as usize) };
327
328 if current == fd_count as usize {
329 return None;
330 }
331 let fd = fd_array[current as usize];
332 self.current = current + 1;
333 Some(fd as RawFd)
334 }
335}
336
337#[cfg(test)]
338mod test {
339 use super::*;
340 use core::mem::{align_of, size_of};
341
342 #[test]
343 #[cfg(any(windows, target_os = "wasi"))]
344 fn layouts() {
345 assert_eq!(align_of::<FdSetElement>(), align_of::<FD_SET>());
347
348 assert_eq!(
351 fd_set_num_elements_for_fd_array(
352 memoffset::span_of!(FD_SET, fd_array).len() / size_of::<RawFd>()
353 ) * size_of::<FdSetElement>(),
354 size_of::<FD_SET>()
355 );
356 }
357
358 #[test]
359 #[cfg(any(bsd, linux_kernel))]
360 fn layouts() {
361 use crate::backend::c;
362
363 assert_eq!(align_of::<FdSetElement>(), align_of::<c::fd_set>());
365
366 assert_eq!(
369 fd_set_num_elements_for_bitvector(c::FD_SETSIZE as RawFd) * size_of::<FdSetElement>(),
370 size_of::<c::fd_set>()
371 );
372 }
373}