2023-01-14 22:56:24 +00:00
|
|
|
use libc::c_int;
|
2023-08-06 12:56:30 +00:00
|
|
|
use std::os::unix::prelude::*;
|
2025-01-05 00:22:38 +00:00
|
|
|
use std::time::Duration;
|
|
|
|
|
|
|
|
pub enum Timeout {
|
|
|
|
Duration(Duration),
|
|
|
|
Forever,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Timeout {
|
|
|
|
pub const ZERO: Timeout = Timeout::Duration(Duration::ZERO);
|
|
|
|
|
|
|
|
/// Convert from usecs to poll-friendly msecs.
|
|
|
|
#[allow(unused)]
|
|
|
|
fn as_poll_msecs(&self) -> c_int {
|
|
|
|
match self {
|
|
|
|
// Negative values mean wait forever in poll-speak.
|
|
|
|
Timeout::Forever => -1 as c_int,
|
|
|
|
Timeout::Duration(duration) => {
|
|
|
|
assert!(
|
|
|
|
duration.as_millis() < c_int::MAX as _,
|
|
|
|
"Timeout too long but not forever!"
|
|
|
|
);
|
|
|
|
duration.as_millis() as c_int
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-01-14 22:56:24 +00:00
|
|
|
|
2023-02-18 01:21:44 +00:00
|
|
|
/// Returns `true` if the fd is or becomes readable within the given timeout.
|
|
|
|
/// This returns `false` if the waiting is interrupted by a signal.
|
2025-01-05 00:22:38 +00:00
|
|
|
pub fn is_fd_readable(fd: i32, timeout: Timeout) -> bool {
|
|
|
|
FdReadableSet::is_fd_readable(fd, timeout)
|
2023-01-14 22:56:24 +00:00
|
|
|
}
|
|
|
|
|
2023-02-18 01:21:44 +00:00
|
|
|
/// Returns whether an fd is readable.
|
2023-01-14 22:56:24 +00:00
|
|
|
pub fn poll_fd_readable(fd: i32) -> bool {
|
2024-06-30 01:06:02 +00:00
|
|
|
FdReadableSet::poll_fd_readable(fd)
|
2023-01-14 22:56:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// A modest wrapper around select() or poll().
|
|
|
|
/// This allows accumulating a set of fds and then seeing if they are readable.
|
|
|
|
/// This only handles readability.
|
|
|
|
/// Apple's `man poll`: "The poll() system call currently does not support devices."
|
|
|
|
#[cfg(target_os = "macos")]
|
2024-06-30 01:06:02 +00:00
|
|
|
pub struct FdReadableSet {
|
2023-01-14 22:56:24 +00:00
|
|
|
// The underlying fdset and nfds value to pass to select().
|
|
|
|
fdset_: libc::fd_set,
|
|
|
|
nfds_: c_int,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(target_os = "macos")]
|
2024-06-30 01:06:02 +00:00
|
|
|
impl FdReadableSet {
|
2023-01-14 22:56:24 +00:00
|
|
|
/// Construct an empty set.
|
2024-06-30 01:06:02 +00:00
|
|
|
pub fn new() -> FdReadableSet {
|
|
|
|
FdReadableSet {
|
2023-01-14 22:56:24 +00:00
|
|
|
fdset_: unsafe { std::mem::zeroed() },
|
|
|
|
nfds_: 0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Reset back to an empty set.
|
|
|
|
pub fn clear(&mut self) {
|
|
|
|
self.nfds_ = 0;
|
|
|
|
unsafe {
|
|
|
|
libc::FD_ZERO(&mut self.fdset_);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Add an fd to the set. The fd is ignored if negative (for convenience).
|
|
|
|
pub fn add(&mut self, fd: RawFd) {
|
|
|
|
if fd >= (libc::FD_SETSIZE as RawFd) {
|
|
|
|
//FLOGF(error, "fd %d too large for select()", fd);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if fd >= 0 {
|
|
|
|
unsafe { libc::FD_SET(fd, &mut self.fdset_) };
|
|
|
|
self.nfds_ = std::cmp::max(self.nfds_, fd + 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-18 01:21:44 +00:00
|
|
|
/// Returns `true` if the given `fd` is marked as set, in our set. Returns `false` if `fd` is
|
|
|
|
/// negative.
|
2023-01-14 22:56:24 +00:00
|
|
|
pub fn test(&self, fd: RawFd) -> bool {
|
|
|
|
fd >= 0 && unsafe { libc::FD_ISSET(fd, &self.fdset_) }
|
|
|
|
}
|
|
|
|
|
2024-12-21 18:41:41 +00:00
|
|
|
/// Call `select()`. Note this destructively modifies the set. Returns the result of
|
|
|
|
/// `select()`.
|
2025-01-05 00:22:38 +00:00
|
|
|
pub fn check_readable(&mut self, timeout: Timeout) -> c_int {
|
2023-01-14 22:56:24 +00:00
|
|
|
let null = std::ptr::null_mut();
|
2024-12-29 13:56:45 +00:00
|
|
|
let mut tvs;
|
2025-01-05 00:22:38 +00:00
|
|
|
let timeout = match timeout {
|
|
|
|
Timeout::Forever => std::ptr::null_mut(),
|
|
|
|
Timeout::Duration(duration) => {
|
|
|
|
tvs = libc::timeval {
|
|
|
|
tv_sec: duration.as_secs() as libc::time_t,
|
|
|
|
tv_usec: duration.subsec_micros() as libc::suseconds_t,
|
|
|
|
};
|
|
|
|
&mut tvs
|
|
|
|
}
|
2024-12-29 13:56:45 +00:00
|
|
|
};
|
|
|
|
unsafe {
|
|
|
|
return libc::select(self.nfds_, &mut self.fdset_, null, null, timeout);
|
2023-01-14 22:56:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Check if a single fd is readable, with a given timeout.
|
2023-02-18 01:21:44 +00:00
|
|
|
/// Returns `true` if readable, `false` otherwise.
|
2025-01-05 00:22:38 +00:00
|
|
|
pub fn is_fd_readable(fd: RawFd, timeout: Timeout) -> bool {
|
2023-01-14 22:56:24 +00:00
|
|
|
if fd < 0 {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
let mut s = Self::new();
|
|
|
|
s.add(fd);
|
2025-01-05 00:22:38 +00:00
|
|
|
let res = s.check_readable(timeout);
|
2023-01-14 22:56:24 +00:00
|
|
|
return res > 0 && s.test(fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Check if a single fd is readable, without blocking.
|
2023-02-18 01:21:44 +00:00
|
|
|
/// Returns `true` if readable, `false` if not.
|
2023-01-14 22:56:24 +00:00
|
|
|
pub fn poll_fd_readable(fd: RawFd) -> bool {
|
2025-01-05 00:22:38 +00:00
|
|
|
return Self::is_fd_readable(fd, Timeout::ZERO);
|
2023-01-14 22:56:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(target_os = "macos"))]
|
2024-06-30 01:06:02 +00:00
|
|
|
pub struct FdReadableSet {
|
2023-01-14 22:56:24 +00:00
|
|
|
pollfds_: Vec<libc::pollfd>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(target_os = "macos"))]
|
2024-06-30 01:06:02 +00:00
|
|
|
impl FdReadableSet {
|
2023-01-14 22:56:24 +00:00
|
|
|
/// Construct an empty set.
|
2024-06-30 01:06:02 +00:00
|
|
|
pub fn new() -> FdReadableSet {
|
|
|
|
FdReadableSet {
|
2023-01-14 22:56:24 +00:00
|
|
|
pollfds_: Vec::new(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Reset back to an empty set.
|
|
|
|
pub fn clear(&mut self) {
|
|
|
|
self.pollfds_.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn pollfd_get_fd(pollfd: &libc::pollfd) -> RawFd {
|
|
|
|
pollfd.fd
|
|
|
|
}
|
|
|
|
|
2023-02-18 01:21:44 +00:00
|
|
|
/// Add an fd to the set. The fd is ignored if negative (for convenience). The fd is also
|
|
|
|
/// ignored if it's already in the set.
|
2023-01-14 22:56:24 +00:00
|
|
|
pub fn add(&mut self, fd: RawFd) {
|
2023-02-18 01:21:44 +00:00
|
|
|
if fd < 0 {
|
|
|
|
return;
|
2023-01-14 22:56:24 +00:00
|
|
|
}
|
2023-02-18 01:21:44 +00:00
|
|
|
let pos = match self.pollfds_.binary_search_by_key(&fd, Self::pollfd_get_fd) {
|
|
|
|
Ok(_) => return,
|
|
|
|
Err(pos) => pos,
|
|
|
|
};
|
|
|
|
|
|
|
|
self.pollfds_.insert(
|
|
|
|
pos,
|
|
|
|
libc::pollfd {
|
|
|
|
fd,
|
|
|
|
events: libc::POLLIN,
|
|
|
|
revents: 0,
|
|
|
|
},
|
|
|
|
);
|
2023-01-14 22:56:24 +00:00
|
|
|
}
|
|
|
|
|
2023-02-18 01:21:44 +00:00
|
|
|
/// Returns `true` if the given `fd` has input available to read or has been HUP'd.
|
|
|
|
/// Returns `false` if `fd` is negative or was not found in the set.
|
2023-01-14 22:56:24 +00:00
|
|
|
pub fn test(&self, fd: RawFd) -> bool {
|
|
|
|
// If a pipe is widowed with no data, Linux sets POLLHUP but not POLLIN, so test for both.
|
|
|
|
if let Ok(pos) = self.pollfds_.binary_search_by_key(&fd, Self::pollfd_get_fd) {
|
|
|
|
let pollfd = &self.pollfds_[pos];
|
|
|
|
debug_assert_eq!(pollfd.fd, fd);
|
|
|
|
return pollfd.revents & (libc::POLLIN | libc::POLLHUP) != 0;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2025-01-05 00:22:38 +00:00
|
|
|
fn do_poll(fds: &mut [libc::pollfd], timeout: Timeout) -> c_int {
|
2023-01-14 22:56:24 +00:00
|
|
|
let count = fds.len();
|
|
|
|
assert!(count <= libc::nfds_t::MAX as usize, "count too big");
|
|
|
|
return unsafe {
|
|
|
|
libc::poll(
|
|
|
|
fds.as_mut_ptr(),
|
|
|
|
count as libc::nfds_t,
|
2025-01-05 00:22:38 +00:00
|
|
|
timeout.as_poll_msecs(),
|
2023-01-14 22:56:24 +00:00
|
|
|
)
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2024-12-21 18:41:41 +00:00
|
|
|
/// Call poll(). Note this destructively modifies the set. Return the result of poll().
|
2025-01-05 00:22:38 +00:00
|
|
|
pub fn check_readable(&mut self, timeout: Timeout) -> c_int {
|
2023-01-14 22:56:24 +00:00
|
|
|
if self.pollfds_.is_empty() {
|
|
|
|
return 0;
|
|
|
|
}
|
2025-01-05 00:22:38 +00:00
|
|
|
return Self::do_poll(&mut self.pollfds_, timeout);
|
2023-01-14 22:56:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Check if a single fd is readable, with a given timeout.
|
2024-05-06 19:58:10 +00:00
|
|
|
/// Return true if `fd` is our set and is readable, `false` otherwise.
|
2025-01-05 00:22:38 +00:00
|
|
|
pub fn is_fd_readable(fd: RawFd, timeout: Timeout) -> bool {
|
2023-01-14 22:56:24 +00:00
|
|
|
if fd < 0 {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
let mut pfd = libc::pollfd {
|
|
|
|
fd,
|
|
|
|
events: libc::POLLIN,
|
|
|
|
revents: 0,
|
|
|
|
};
|
2025-01-05 00:22:38 +00:00
|
|
|
let ret = Self::do_poll(std::slice::from_mut(&mut pfd), timeout);
|
2023-01-14 22:56:24 +00:00
|
|
|
return ret > 0 && (pfd.revents & libc::POLLIN) != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Check if a single fd is readable, without blocking.
|
2024-05-06 19:58:10 +00:00
|
|
|
/// Return true if readable, false if not.
|
2023-01-14 22:56:24 +00:00
|
|
|
pub fn poll_fd_readable(fd: RawFd) -> bool {
|
2025-01-05 00:22:38 +00:00
|
|
|
return Self::is_fd_readable(fd, Timeout::ZERO);
|
2023-01-14 22:56:24 +00:00
|
|
|
}
|
|
|
|
}
|