xref: /relibc/src/header/sys_select/mod.rs (revision 7d27737c3ffa5bbdf5d00592bb95abf1f9703aee)
1 //! sys/select.h implementation
2 
3 use core::mem;
4 
5 use cbitset::BitSet;
6 
7 use crate::{
8     fs::File,
9     header::{
10         errno,
11         sys_epoll::{
12             epoll_create1, epoll_ctl, epoll_data, epoll_event, epoll_wait, EPOLLERR, EPOLLIN,
13             EPOLLOUT, EPOLL_CLOEXEC, EPOLL_CTL_ADD,
14         },
15         sys_time::timeval,
16     },
17     platform::{self, types::*},
18 };
19 
20 // fd_set is also defined in C because cbindgen is incompatible with mem::size_of booo
21 
22 pub const FD_SETSIZE: usize = 1024;
23 type bitset = BitSet<[u64; FD_SETSIZE / (8 * mem::size_of::<u64>())]>;
24 
25 #[repr(C)]
26 pub struct fd_set {
27     pub fds_bits: bitset,
28 }
29 
30 pub fn select_epoll(
31     nfds: c_int,
32     readfds: Option<&mut fd_set>,
33     writefds: Option<&mut fd_set>,
34     exceptfds: Option<&mut fd_set>,
35     timeout: Option<&mut timeval>,
36 ) -> c_int {
37     if nfds < 0 || nfds > FD_SETSIZE as i32 {
38         unsafe { platform::errno = errno::EINVAL };
39         return -1;
40     };
41 
42     let ep = {
43         let epfd = epoll_create1(EPOLL_CLOEXEC);
44         if epfd < 0 {
45             return -1;
46         }
47         File::new(epfd)
48     };
49 
50     let mut read_bitset: Option<&mut bitset> = readfds.map(|fd_set| &mut fd_set.fds_bits);
51     let mut write_bitset: Option<&mut bitset> = writefds.map(|fd_set| &mut fd_set.fds_bits);
52     let mut except_bitset: Option<&mut bitset> = exceptfds.map(|fd_set| &mut fd_set.fds_bits);
53 
54     // Keep track of the number of file descriptors that do not support epoll
55     let mut not_epoll = 0;
56     for fd in 0..nfds {
57         let mut events = 0;
58 
59         if let Some(ref fd_set) = read_bitset {
60             if fd_set.contains(fd as usize) {
61                 events |= EPOLLIN;
62             }
63         }
64 
65         if let Some(ref fd_set) = write_bitset {
66             if fd_set.contains(fd as usize) {
67                 events |= EPOLLOUT;
68             }
69         }
70 
71         if let Some(ref fd_set) = except_bitset {
72             if fd_set.contains(fd as usize) {
73                 events |= EPOLLERR;
74             }
75         }
76 
77         if events > 0 {
78             let mut event = epoll_event {
79                 events,
80                 data: epoll_data { fd },
81                 ..Default::default()
82             };
83             if epoll_ctl(*ep, EPOLL_CTL_ADD, fd, &mut event) < 0 {
84                 if unsafe { platform::errno == errno::EPERM } {
85                     not_epoll += 1;
86                 } else {
87                     return -1;
88                 }
89             } else {
90                 if let Some(ref mut fd_set) = read_bitset {
91                     if fd_set.contains(fd as usize) {
92                         fd_set.remove(fd as usize);
93                     }
94                 }
95 
96                 if let Some(ref mut fd_set) = write_bitset {
97                     if fd_set.contains(fd as usize) {
98                         fd_set.remove(fd as usize);
99                     }
100                 }
101 
102                 if let Some(ref mut fd_set) = except_bitset {
103                     if fd_set.contains(fd as usize) {
104                         fd_set.remove(fd as usize);
105                     }
106                 }
107             }
108         }
109     }
110 
111     let mut events: [epoll_event; 32] = unsafe { mem::zeroed() };
112     let epoll_timeout = if not_epoll > 0 {
113         // Do not wait if any non-epoll file descriptors were found
114         0
115     } else {
116         match timeout {
117             Some(timeout) => {
118                 //TODO: Check for overflow
119                 ((timeout.tv_sec as c_int) * 1000) + ((timeout.tv_usec as c_int) / 1000)
120             }
121             None => -1,
122         }
123     };
124     let res = epoll_wait(
125         *ep,
126         events.as_mut_ptr(),
127         events.len() as c_int,
128         epoll_timeout,
129     );
130     if res < 0 {
131         return -1;
132     }
133 
134     let mut count = not_epoll;
135     for event in events.iter().take(res as usize) {
136         let fd = unsafe { event.data.fd };
137         // TODO: Error status when fd does not match?
138         if fd >= 0 && fd < FD_SETSIZE as c_int {
139             if event.events & EPOLLIN > 0 {
140                 if let Some(ref mut fd_set) = read_bitset {
141                     fd_set.insert(fd as usize);
142                     count += 1;
143                 }
144             }
145             if event.events & EPOLLOUT > 0 {
146                 if let Some(ref mut fd_set) = write_bitset {
147                     fd_set.insert(fd as usize);
148                     count += 1;
149                 }
150             }
151             if event.events & EPOLLERR > 0 {
152                 if let Some(ref mut fd_set) = except_bitset {
153                     fd_set.insert(fd as usize);
154                     count += 1;
155                 }
156             }
157         }
158     }
159     count
160 }
161 
162 #[no_mangle]
163 pub unsafe extern "C" fn select(
164     nfds: c_int,
165     readfds: *mut fd_set,
166     writefds: *mut fd_set,
167     exceptfds: *mut fd_set,
168     timeout: *mut timeval,
169 ) -> c_int {
170     trace_expr!(
171         select_epoll(
172             nfds,
173             if readfds.is_null() {
174                 None
175             } else {
176                 Some(&mut *readfds)
177             },
178             if writefds.is_null() {
179                 None
180             } else {
181                 Some(&mut *writefds)
182             },
183             if exceptfds.is_null() {
184                 None
185             } else {
186                 Some(&mut *exceptfds)
187             },
188             if timeout.is_null() {
189                 None
190             } else {
191                 Some(&mut *timeout)
192             }
193         ),
194         "select({}, {:p}, {:p}, {:p}, {:p})",
195         nfds,
196         readfds,
197         writefds,
198         exceptfds,
199         timeout
200     )
201 }
202