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