//! `poll.h` implementation. //! //! See . use core::{mem, ptr, slice}; use crate::{ error::Errno, fs::File, header::{ bits_sigset_t::sigset_t, errno::{EBADF, EINTR}, sys_epoll::{ EPOLL_CLOEXEC, EPOLL_CTL_ADD, EPOLLERR, EPOLLHUP, EPOLLIN, EPOLLNVAL, EPOLLOUT, EPOLLPRI, EPOLLRDBAND, EPOLLRDNORM, EPOLLWRBAND, EPOLLWRNORM, epoll_data, epoll_event, }, time::timespec, }, platform::{ ERRNO, PalEpoll, Sys, types::{c_int, c_short, c_ulong}, }, }; /// Data other than high-priority data may be read without blocking. pub const POLLIN: c_short = 0x001; /// High-priority data may be read without blocking. pub const POLLPRI: c_short = 0x002; /// Normal data may be written without blocking. pub const POLLOUT: c_short = 0x004; /// An error has occurred (revents only). pub const POLLERR: c_short = 0x008; /// Device has been disconnected (revents only). pub const POLLHUP: c_short = 0x010; /// Invalid fd member (revents only). pub const POLLNVAL: c_short = 0x020; /// Normal data may be read without blocking. pub const POLLRDNORM: c_short = 0x040; /// Priority data may be read without blocking. pub const POLLRDBAND: c_short = 0x080; /// Equivalent to POLLOUT. pub const POLLWRNORM: c_short = 0x100; /// Priority data may be written. pub const POLLWRBAND: c_short = 0x200; /// An unsigned integer type used for the number of file descriptors. #[allow(non_camel_case_types)] pub type nfds_t = c_ulong; /// See . /// /// A structure storing a file descriptor along with input and output flags for poll operations. #[allow(non_camel_case_types)] #[repr(C)] pub struct pollfd { /// The following descriptor being polled. pub fd: c_int, /// The input event flags. pub events: c_short, /// The output event flags. pub revents: c_short, } #[allow(clippy::needless_update)] // epoll_event _pad field on redox pub unsafe fn poll_epoll(fds: &mut [pollfd], timeout: c_int, sigmask: *const sigset_t) -> c_int { let event_map = [ (POLLIN, EPOLLIN), (POLLPRI, EPOLLPRI), (POLLOUT, EPOLLOUT), (POLLERR, EPOLLERR), (POLLHUP, EPOLLHUP), (POLLNVAL, EPOLLNVAL), (POLLRDNORM, EPOLLRDNORM), (POLLWRNORM, EPOLLWRNORM), (POLLRDBAND, EPOLLRDBAND), (POLLWRBAND, EPOLLWRBAND), ]; let ep = { let epfd = match Sys::epoll_create1(EPOLL_CLOEXEC) { Ok(epfd) => epfd, Err(Errno(err)) => { ERRNO.set(err); return -1; } }; File::new(epfd) }; let mut closed = 0; for (i, fd) in fds.iter_mut().enumerate() { let pfd = fd; pfd.revents = 0; // Ignore the entry with negative fd if pfd.fd < 0 { continue; } #[cfg(target_os = "redox")] let mut event = epoll_event { events: 0, data: epoll_data { u64: i as u64 }, ..Default::default() }; #[cfg(target_os = "linux")] let mut event = epoll_event { events: 0, data: epoll_data { u64: i as u64 }, }; for (p, ep) in event_map.iter() { if pfd.events & p > 0 { event.events |= ep; } } match unsafe { Sys::epoll_ctl(*ep, EPOLL_CTL_ADD, pfd.fd, &raw mut event) } { Ok(()) => {} Err(Errno(EBADF)) => { pfd.revents |= POLLNVAL; closed += 1; } Err(Errno(err)) => { ERRNO.set(err); return -1; } } } // Early exit if there are fds, and all are closed (revents = POLLNVAL) if closed > 0 && closed == fds.len() { return closed as i32; } let mut events: [epoll_event; 32] = unsafe { mem::zeroed() }; match unsafe { Sys::epoll_pwait( *ep, events.as_mut_ptr(), events.len() as c_int, timeout, sigmask, ) } { Ok(res) => { for event in events.iter().take(res) { let pi = unsafe { event.data.u64 as usize }; // TODO: Error status when fd does not match? if let Some(pfd) = fds.get_mut(pi) { for (p, ep) in event_map.iter() { if event.events & ep > 0 { pfd.revents |= p; } } } } } Err(Errno(err)) => { if err == EINTR && closed > 0 { // some fds are closed by signal } else { ERRNO.set(err); return -1; } } } let mut count = 0; for pfd in fds.iter() { if pfd.revents > 0 { count += 1; } } count } /// See . /// /// Provides applications with a mechanism for multiplexing input/output /// over a set of file descriptors. /// /// - The `timeout` parameter represents milliseconds. /// - A `timeout` of `-1` is equivalent to passing a null pointer for `tmo_p` to `ppoll`. /// - `poll` should behave equivalent to `ppoll` with a null pointer for `sigmask`. /// /// Note: Uses epoll internally. #[unsafe(no_mangle)] pub unsafe extern "C" fn poll(fds: *mut pollfd, nfds: nfds_t, timeout: c_int) -> c_int { trace_expr!( unsafe { poll_epoll( slice::from_raw_parts_mut(fds, nfds as usize), timeout, ptr::null_mut(), ) }, "poll({:p}, {}, {})", fds, nfds, timeout, ) } /// See . /// /// Provides applications with a mechanism for multiplexing input/output /// over a set of file descriptors. /// /// - The `tmo_p` parameter is the timeout as represented by a `timespec` struct. /// - Passing a null pointer for timeout is equivalent to `-1` for `timeout` to `poll`. /// /// Note: Uses epoll internally. #[unsafe(no_mangle)] pub unsafe extern "C" fn ppoll( fds: *mut pollfd, nfds: nfds_t, tmo_p: *const timespec, sigmask: *const sigset_t, ) -> c_int { let timeout = if tmo_p.is_null() { -1 } else { let tmo = unsafe { &*tmo_p }; if tmo.tv_sec > (c_int::MAX / 1000).into() { c_int::MAX } else { ((tmo.tv_sec as c_int) * 1000) + ((tmo.tv_nsec as c_int) / 1000000) } }; trace_expr!( unsafe { poll_epoll( slice::from_raw_parts_mut(fds, nfds as usize), timeout, sigmask, ) }, "ppoll({:p}, {}, {:p}, {:p})", fds, nfds, tmo_p, sigmask ) }