//! Portably monitor a group of file descriptors for readiness.
use crate::errno::Errno;
use crate::sys::time::{TimeSpec, TimeVal};
use crate::Result;
use libc::{
self, c_int};
use std::convert::TryFrom;
use std::iter::FusedIterator;
use std::mem;
use std::ops::Range;
use std::os::unix::io::{AsRawFd, BorrowedFd, RawFd};
use std::ptr::{null, null_mut};
pub use libc::FD_SETSIZE;
/// Contains a set of file descriptors used by [`select`]
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct FdSet<
'fd> {
set: libc::fd_set,
_fd: std::marker::PhantomData<BorrowedFd<
'fd>>,
}
fn assert_fd_valid(fd: RawFd) {
assert!(
usize::try_from(fd).map_or(
false, |fd| fd < FD_SETSIZE),
"fd must be in the range 0..FD_SETSIZE",
);
}
impl<
'fd> FdSet<'fd> {
/// Create an empty `FdSet`
pub fn new() -> FdSet<
'fd> {
let mut fdset = mem::MaybeUninit::uninit();
unsafe {
libc::FD_ZERO(fdset.as_mut_ptr());
Self {
set: fdset.assume_init(),
_fd: std::marker::PhantomData,
}
}
}
/// Add a file descriptor to an `FdSet`
pub fn insert(&
mut self, fd: BorrowedFd<
'fd>) {
assert_fd_valid(fd.as_raw_fd());
unsafe { libc::FD_SET(fd.as_raw_fd(), &
mut self.set) };
}
/// Remove a file descriptor from an `FdSet`
pub fn remove(&
mut self, fd: BorrowedFd<
'fd>) {
assert_fd_valid(fd.as_raw_fd());
unsafe { libc::FD_CLR(fd.as_raw_fd(), &
mut self.set) };
}
/// Test an `FdSet` for the presence of a certain file descriptor.
pub fn contains(&
self, fd: BorrowedFd<
'fd>) -> bool {
assert_fd_valid(fd.as_raw_fd());
unsafe { libc::FD_ISSET(fd.as_raw_fd(), &
self.set) }
}
/// Remove all file descriptors from this `FdSet`.
pub fn clear(&
mut self) {
unsafe { libc::FD_ZERO(&
mut self.set) };
}
/// Finds the highest file descriptor in the set.
///
/// Returns `None` if the set is empty.
///
/// This can be used to calculate the `nfds` parameter of the [`select`] function.
///
/// # Example
///
/// ```
/// # use std::os::unix::io::{AsRawFd, BorrowedFd};
/// # use nix::sys::select::FdSet;
/// let fd_four = unsafe {BorrowedFd::borrow_raw(4)};
/// let fd_nine = unsafe {BorrowedFd::borrow_raw(9)};
/// let mut set = FdSet::new();
/// set.insert(fd_four);
/// set.insert(fd_nine);
/// assert_eq!(set.highest().map(|borrowed_fd|borrowed_fd.as_raw_fd()), Some(9));
/// ```
///
/// [`select`]: fn.select.html
pub fn highest(&
self) -> Option<BorrowedFd<
'_>> {
self.fds(None).next_back()
}
/// Returns an iterator over the file descriptors in the set.
///
/// For performance, it takes an optional higher bound: the iterator will
/// not return any elements of the set greater than the given file
/// descriptor.
///
/// # Examples
///
/// ```
/// # use nix::sys::select::FdSet;
/// # use std::os::unix::io::{AsRawFd, BorrowedFd, RawFd};
/// let mut set = FdSet::new();
/// let fd_four = unsafe {BorrowedFd::borrow_raw(4)};
/// let fd_nine = unsafe {BorrowedFd::borrow_raw(9)};
/// set.insert(fd_four);
/// set.insert(fd_nine);
/// let fds: Vec<RawFd> = set.fds(None).map(|borrowed_fd|borrowed_fd.as_raw_fd()).collect();
/// assert_eq!(fds, vec![4, 9]);
/// ```
#[inline]
pub fn fds(&
self, highest: Option<RawFd>) -> Fds {
Fds {
set:
self,
range:
0..highest.map(|h| h
as usize +
1).unwrap_or(FD_SETSIZE),
}
}
}
impl<
'fd> Default for FdSet<'fd> {
fn default() ->
Self {
Self::new()
}
}
/// Iterator over `FdSet`.
#[derive(Debug)]
pub struct Fds<
'a, 'fd> {
set: &
'a FdSet<'fd>,
range: Range<usize>,
}
impl<
'a, 'fd> Iterator
for Fds<
'a, 'fd> {
type Item = BorrowedFd<
'fd>;
fn next(&
mut self) -> Option<
Self::Item> {
for i
in &
mut self.range {
let borrowed_i =
unsafe { BorrowedFd::borrow_raw(i
as RawFd) };
if self.set.contains(borrowed_i) {
return Some(borrowed_i);
}
}
None
}
#[inline]
fn size_hint(&
self) -> (usize, Option<usize>) {
let (_, upper) =
self.range.size_hint();
(
0, upper)
}
}
impl<
'a, 'fd> DoubleEndedIterator
for Fds<
'a, 'fd> {
#[inline]
fn next_back(&
mut self) -> Option<BorrowedFd<
'fd>> {
while let Some(i) =
self.range.next_back() {
let borrowed_i =
unsafe { BorrowedFd::borrow_raw(i
as RawFd) };
if self.set.contains(borrowed_i) {
return Some(borrowed_i);
}
}
None
}
}
impl<
'a, 'fd> FusedIterator
for Fds<
'a, 'fd> {}
/// Monitors file descriptors for readiness
///
/// Returns the total number of ready file descriptors in all sets. The sets are changed so that all
/// file descriptors that are ready for the given operation are set.
///
/// When this function returns, `timeout` has an implementation-defined value.
///
/// # Parameters
///
/// * `nfds`: The highest file descriptor set in any of the passed `FdSet`s, plus 1. If `None`, this
/// is calculated automatically by calling [`FdSet::highest`] on all descriptor sets and adding 1
/// to the maximum of that.
/// * `readfds`: File descriptors to check for being ready to read.
/// * `writefds`: File descriptors to check for being ready to write.
/// * `errorfds`: File descriptors to check for pending error conditions.
/// * `timeout`: Maximum time to wait for descriptors to become ready (`None` to block
/// indefinitely).
///
/// # References
///
/// [select(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/select.html)
///
/// [`FdSet::highest`]: struct.FdSet.html#method.highest
pub fn select<
'a, 'fd, N, R, W, E, T>(
nfds: N,
readfds: R,
writefds: W,
errorfds: E,
timeout: T,
) -> Result<c_int>
where
'fd: 'a,
N: Into<Option<c_int>>,
R: Into<Option<&
'a mut FdSet<'fd>>>,
W: Into<Option<&
'a mut FdSet<'fd>>>,
E: Into<Option<&
'a mut FdSet<'fd>>>,
T: Into<Option<&
'a mut TimeVal>>,
{
let mut readfds = readfds.into();
let mut writefds = writefds.into();
let mut errorfds = errorfds.into();
let timeout = timeout.into();
let nfds = nfds.into().unwrap_or_else(|| {
readfds
.iter_mut()
.chain(writefds.iter_mut())
.chain(errorfds.iter_mut())
.map(|set| {
set.highest()
.map(|borrowed_fd| borrowed_fd.as_raw_fd())
.unwrap_or(-
1)
})
.max()
.unwrap_or(-
1)
+
1
});
let readfds = readfds
.map(|set| set
as *
mut _
as *
mut libc::fd_set)
.unwrap_or(null_mut());
let writefds = writefds
.map(|set| set
as *
mut _
as *
mut libc::fd_set)
.unwrap_or(null_mut());
let errorfds = errorfds
.map(|set| set
as *
mut _
as *
mut libc::fd_set)
.unwrap_or(null_mut());
let timeout = timeout
.map(|tv| tv
as *
mut _
as *
mut libc::timeval)
.unwrap_or(null_mut());
let res =
unsafe { libc::select(nfds, readfds, writefds, errorfds, timeout) };
Errno::result(res)
}
feature! {
#![feature =
"signal"]
use crate::sys::signal::SigSet;
/// Monitors file descriptors for readiness with an altered signal mask.
///
/// Returns the total number of ready file descriptors in all sets. The sets are changed so that all
/// file descriptors that are ready for the given operation are set.
///
/// When this function returns, the original signal mask is restored.
///
/// Unlike [`select`](#fn.select), `pselect` does not mutate the `timeout` value.
///
/// # Parameters
///
/// * `nfds`: The highest file descriptor set in any of the passed `FdSet`s, plus 1. If `None`, this
/// is calculated automatically by calling [`FdSet::highest`] on all descriptor sets and adding 1
/// to the maximum of that.
/// * `readfds`: File descriptors to check for read readiness
/// * `writefds`: File descriptors to check for write readiness
/// * `errorfds`: File descriptors to check for pending error conditions.
/// * `timeout`: Maximum time to wait for descriptors to become ready (`None` to block
/// indefinitely).
/// * `sigmask`: Signal mask to activate while waiting for file descriptors to turn
/// ready (`None` to set no alternative signal mask).
///
/// # References
///
/// [pselect(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pselect.html)
///
/// [The new pselect() system call](https://lwn.net/Articles/176911/)
///
/// [`FdSet::highest`]: struct.FdSet.html#method.highest
pub fn pselect<
'a, 'fd, N, R, W, E, T, S>(nfds: N,
readfds: R,
writefds: W,
errorfds: E,
timeout: T,
sigmask: S) -> Result<c_int>
where
'fd: 'a,
N: Into<Option<c_int>>,
R: Into<Option<&
'a mut FdSet<'fd>>>,
W: Into<Option<&
'a mut FdSet<'fd>>>,
E: Into<Option<&
'a mut FdSet<'fd>>>,
T: Into<Option<&
'a TimeSpec>>,
S: Into<Option<&
'a SigSet>>,
{
let mut readfds = readfds.into();
let mut writefds = writefds.into();
let mut errorfds = errorfds.into();
let sigmask = sigmask.into();
let timeout = timeout.into();
let nfds = nfds.into().unwrap_or_else(|| {
readfds.iter_mut()
.chain(writefds.iter_mut())
.chain(errorfds.iter_mut())
.map(|set| set.highest().map(|borrowed_fd|borrowed_fd.as_raw_fd()).unwrap_or(-
1))
.max()
.unwrap_or(-
1) +
1
});
let readfds = readfds.map(|set| set
as *
mut _
as *
mut libc::fd_set).unwrap_or(null_mut());
let writefds = writefds.map(|set| set
as *
mut _
as *
mut libc::fd_set).unwrap_or(null_mut()
);
let errorfds = errorfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
let timeout = timeout.map(|ts| ts.as_ref() as *const libc::timespec).unwrap_or(null());
let sigmask = sigmask.map(|sm| sm.as_ref() as *const libc::sigset_t).unwrap_or(null());
let res = unsafe {
libc::pselect(nfds, readfds, writefds, errorfds, timeout, sigmask)
};
Errno::result(res)
}
}