Forked from
redox-os / relibc
1545 commits behind the upstream repository.
-
Jeremy Soller authoredJeremy Soller authored
mod.rs 6.05 KiB
//! sys/select.h implementation
use core::mem;
use fs::File;
use header::errno;
use header::sys_epoll::{
epoll_create1, EPOLL_CLOEXEC,
epoll_ctl, EPOLL_CTL_ADD,
epoll_wait,
EPOLLIN, EPOLLOUT, EPOLLERR,
epoll_data, epoll_event
};
use header::sys_time::timeval;
use platform;
use platform::types::*;
// fd_set is also defined in C because cbindgen is incompatible with mem::size_of booo
pub const FD_SETSIZE: usize = 1024;
#[repr(C)]
pub struct fd_set {
pub fds_bits: [c_ulong; FD_SETSIZE / (8 * mem::size_of::<c_ulong>())],
}
impl fd_set {
fn index(fd: c_int) -> usize {
(fd as usize) / (8 * mem::size_of::<c_ulong>())
}
fn bitmask(fd: c_int) -> c_ulong {
1 << ((fd as usize) & (8 * mem::size_of::<c_ulong>() - 1)) as c_ulong
}
fn zero(&mut self) {
for i in 0..self.fds_bits.len() {
self.fds_bits[i] = 0;
}
}
fn set(&mut self, fd: c_int) {
self.fds_bits[Self::index(fd)] |= Self::bitmask(fd);
}
fn clr(&mut self, fd: c_int) {
self.fds_bits[Self::index(fd)] &= !Self::bitmask(fd);
}
fn isset(&self, fd: c_int) -> bool {
self.fds_bits[Self::index(fd)] & Self::bitmask(fd) > 0
}
}
pub fn select_epoll(
nfds: c_int,
mut readfds: Option<&mut fd_set>,
mut writefds: Option<&mut fd_set>,
mut exceptfds: Option<&mut fd_set>,
timeout: Option<&mut timeval>
) -> c_int {
if nfds < 0 || nfds > FD_SETSIZE as i32 {
unsafe { platform::errno = errno::EINVAL };
return -1;
};
let ep = {
let epfd = epoll_create1(EPOLL_CLOEXEC);
if epfd < 0 {
return -1;
}
File::new(epfd)
};
// Keep track of the number of file descriptors that do not support epoll
let mut not_epoll = 0;
for fd in 0..nfds {
if let Some(ref mut fd_set) = readfds {
if fd_set.isset(fd) {
let mut event = epoll_event {
events: EPOLLIN,
data: epoll_data {
fd: fd,
},
};
if epoll_ctl(*ep, EPOLL_CTL_ADD, fd, &mut event) < 0 {
if unsafe { platform::errno == errno::EPERM } {
not_epoll += 1;
} else {
return -1;
}
} else {
fd_set.clr(fd);
}
}
}
if let Some(ref mut fd_set) = writefds {
if fd_set.isset(fd) {
let mut event = epoll_event {
events: EPOLLOUT,
data: epoll_data {
fd: fd,
},
};
if epoll_ctl(*ep, EPOLL_CTL_ADD, fd, &mut event) < 0 {
if unsafe { platform::errno == errno::EPERM } {
not_epoll += 1;
} else {
return -1;
}
} else {
fd_set.clr(fd);
}
}
}
if let Some(ref mut fd_set) = exceptfds {
if fd_set.isset(fd) {
let mut event = epoll_event {
events: EPOLLERR,
data: epoll_data {
fd: fd,
},
};
if epoll_ctl(*ep, EPOLL_CTL_ADD, fd, &mut event) < 0 {
if unsafe { platform::errno == errno::EPERM } {
not_epoll += 1;
} else {
return -1;
}
} else {
fd_set.clr(fd);
}
}
}
}
let mut events: [epoll_event; 32] = unsafe { mem::zeroed() };
let epoll_timeout = if not_epoll > 0 {
// Do not wait if any non-epoll file descriptors were found
0
} else {
match timeout {
Some(timeout) => {
//TODO: Check for overflow
((timeout.tv_sec as c_int) * 1000) +
((timeout.tv_usec as c_int) / 1000)
},
None => -1
}
};
let res = epoll_wait(
*ep,
events.as_mut_ptr(),
events.len() as c_int,
epoll_timeout
);
if res < 0 {
return -1;
}
let mut count = not_epoll;
for i in 0..res as usize {
let event = &events[i];
let fd = unsafe { event.data.fd };
// TODO: Error status when fd does not match?
if fd >= 0 && fd <= FD_SETSIZE as c_int {
if event.events & EPOLLIN > 0 {
if let Some(ref mut fd_set) = readfds {
fd_set.set(fd);
count += 1;
}
}
if event.events & EPOLLOUT > 0 {
if let Some(ref mut fd_set) = writefds {
fd_set.set(fd);
count += 1;
}
}
if event.events & EPOLLERR > 0 {
if let Some(ref mut fd_set) = exceptfds {
fd_set.set(fd);
count += 1;
}
}
}
}
count
}
#[no_mangle]
pub unsafe extern "C" fn select(
nfds: c_int,
readfds: *mut fd_set,
writefds: *mut fd_set,
exceptfds: *mut fd_set,
timeout: *mut timeval,
) -> c_int {
trace_expr!(
select_epoll(
nfds,
if readfds.is_null() {
None
} else {
Some(&mut *readfds)
},
if writefds.is_null() {
None
} else {
Some(&mut *writefds)
},
if exceptfds.is_null() {
None
} else {
Some(&mut *exceptfds)
},
if timeout.is_null() {
None
} else {
Some(&mut *timeout)
}
),
"select({}, {:p}, {:p}, {:p}, {:p})",
nfds,
readfds,
writefds,
exceptfds,
timeout
)
}