occlum/src/libos/src/net/io_multiplexing/select.rs

143 lines
3.6 KiB
Rust

use super::*;
/// Forward to host `poll`
/// (sgx_libc doesn't have `select`)
pub fn do_select(
nfds: usize,
readfds: &mut libc::fd_set,
writefds: &mut libc::fd_set,
exceptfds: &mut libc::fd_set,
timeout: Option<libc::timeval>,
) -> Result<usize> {
debug!("select: nfds: {}", nfds);
// convert libos fd to Linux fd
let mut host_to_libos_fd = [0; libc::FD_SETSIZE];
let mut polls = Vec::<libc::pollfd>::new();
let current_ref = process::get_current();
let mut proc = current_ref.lock().unwrap();
let file_table = proc.get_files().lock().unwrap();
for fd in 0..nfds {
let fd_ref = file_table.get(fd as FileDesc)?;
let (r, w, e) = (
readfds.is_set(fd),
writefds.is_set(fd),
exceptfds.is_set(fd),
);
if !(r || w || e) {
continue;
}
if let Ok(socket) = fd_ref.as_unix_socket() {
warn!("select unix socket is unimplemented, spin for read");
readfds.clear();
writefds.clear();
exceptfds.clear();
// FIXME: spin poll until can read (hack for php)
while r && socket.poll()?.0 == false {
spin_loop_hint();
}
let (rr, ww, ee) = socket.poll()?;
if r && rr {
readfds.set(fd);
}
if w && ww {
writefds.set(fd);
}
if e && ee {
writefds.set(fd);
}
return Ok(1);
}
let host_fd = if let Ok(socket) = fd_ref.as_socket() {
socket.fd()
} else if let Ok(eventfd) = fd_ref.as_event() {
eventfd.get_host_fd()
} else {
return_errno!(EBADF, "unsupported file type");
};
host_to_libos_fd[host_fd as usize] = fd;
let mut events = 0;
if r {
events |= libc::POLLIN;
}
if w {
events |= libc::POLLOUT;
}
if e {
events |= libc::POLLERR;
}
polls.push(libc::pollfd {
fd: host_fd as c_int,
events,
revents: 0,
});
}
// Unlock the current process and its file table as early as possible
drop(file_table);
drop(proc);
let timeout = match timeout {
None => -1,
Some(tv) => (tv.tv_sec * 1000 + tv.tv_usec / 1000) as i32,
};
let ret = try_libc!(libc::ocall::poll(
polls.as_mut_ptr(),
polls.len() as u64,
timeout
));
// convert fd back and write fdset
readfds.clear();
writefds.clear();
exceptfds.clear();
for poll in polls.iter() {
let fd = host_to_libos_fd[poll.fd as usize];
if poll.revents & libc::POLLIN != 0 {
readfds.set(fd);
}
if poll.revents & libc::POLLOUT != 0 {
writefds.set(fd);
}
if poll.revents & libc::POLLERR != 0 {
exceptfds.set(fd);
}
}
Ok(ret as usize)
}
/// Safe methods for `libc::fd_set`
trait FdSetExt {
fn set(&mut self, fd: usize);
fn clear(&mut self);
fn is_set(&mut self, fd: usize) -> bool;
}
impl FdSetExt for libc::fd_set {
fn set(&mut self, fd: usize) {
assert!(fd < libc::FD_SETSIZE);
unsafe {
libc::FD_SET(fd as c_int, self);
}
}
fn clear(&mut self) {
unsafe {
libc::FD_ZERO(self);
}
}
fn is_set(&mut self, fd: usize) -> bool {
assert!(fd < libc::FD_SETSIZE);
unsafe { libc::FD_ISSET(fd as c_int, self) }
}
}