From 492903ba6c89b3f042cd9336b321020b23b98f5f Mon Sep 17 00:00:00 2001 From: Andreas Fuchs Date: Sat, 28 Apr 2018 21:08:39 +0200 Subject: [PATCH] select: add pselect syscall This is a straight port of @abbradar's work in #276, with two (somewhat weak) tests and a bit of documentation. --- CHANGELOG.md | 2 ++ src/sys/select.rs | 77 +++++++++++++++++++++++++++++++++++++++-- test/sys/mod.rs | 1 + test/sys/test_select.rs | 55 +++++++++++++++++++++++++++++ 4 files changed, 133 insertions(+), 2 deletions(-) create mode 100644 test/sys/test_select.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index e146c48395..da1618d99b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] ### Added +- Added `pselect` + ([#894](https://github.com/nix-rust/nix/pull/894)) - Exposed `preadv` and `pwritev` on the BSDs. ([#883](https://github.com/nix-rust/nix/pull/883)) - Added `mlockall` and `munlockall` diff --git a/src/sys/select.rs b/src/sys/select.rs index 33d3e63855..0b6d2c406d 100644 --- a/src/sys/select.rs +++ b/src/sys/select.rs @@ -1,10 +1,11 @@ use std::mem; use std::os::unix::io::RawFd; -use std::ptr::null_mut; +use std::ptr::{null, null_mut}; use libc::{self, c_int}; use Result; use errno::Errno; -use sys::time::TimeVal; +use sys::signal::SigSet; +use sys::time::{TimeSpec, TimeVal}; pub use libc::FD_SETSIZE; @@ -131,6 +132,78 @@ where Errno::result(res) } +/// 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)](http://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, N, R, W, E, T, S>(nfds: N, + readfds: R, + writefds: W, + errorfds: E, + timeout: T, + sigmask: S) -> Result +where + N: Into>, + R: Into>, + W: Into>, + E: Into>, + T: Into>, + S: Into>, +{ + 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().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) +} + + #[cfg(test)] mod tests { use super::*; diff --git a/test/sys/mod.rs b/test/sys/mod.rs index 31cf73b1d9..1b3e67aa9f 100644 --- a/test/sys/mod.rs +++ b/test/sys/mod.rs @@ -15,6 +15,7 @@ mod test_aio; mod test_signalfd; mod test_socket; mod test_sockopt; +mod test_select; mod test_termios; mod test_ioctl; mod test_wait; diff --git a/test/sys/test_select.rs b/test/sys/test_select.rs new file mode 100644 index 0000000000..19d12fba54 --- /dev/null +++ b/test/sys/test_select.rs @@ -0,0 +1,55 @@ +use nix::sys::select::*; +use nix::unistd::{pipe, write}; +use nix::sys::signal::SigSet; +use nix::sys::time::{TimeSpec, TimeValLike}; +use std::os::unix::io::RawFd; + +#[test] +pub fn test_pselect() { + let _mtx = ::SIGNAL_MTX + .lock() + .expect("Mutex got poisoned by another test"); + + let (r1, w1) = pipe().unwrap(); + write(w1, b"hi!").unwrap(); + let (r2, _w2) = pipe().unwrap(); + + let mut fd_set = FdSet::new(); + fd_set.insert(r1); + fd_set.insert(r2); + + let timeout = TimeSpec::seconds(10); + let sigmask = SigSet::empty(); + assert_eq!( + 1, + pselect(None, &mut fd_set, None, None, &timeout, &sigmask).unwrap() + ); + assert!(fd_set.contains(r1)); + assert!(!fd_set.contains(r2)); +} + +#[test] +pub fn test_pselect_nfds2() { + let (r1, w1) = pipe().unwrap(); + write(w1, b"hi!").unwrap(); + let (r2, _w2) = pipe().unwrap(); + + let mut fd_set = FdSet::new(); + fd_set.insert(r1); + fd_set.insert(r2); + + let timeout = TimeSpec::seconds(10); + assert_eq!( + 1, + pselect( + ::std::cmp::max(r1, r2) + 1, + &mut fd_set, + None, + None, + &timeout, + None + ).unwrap() + ); + assert!(fd_set.contains(r1)); + assert!(!fd_set.contains(r2)); +}