summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Fuchs <asf@boinkor.net>2018-04-28 21:08:39 +0200
committerAndreas Fuchs <asf@boinkor.net>2018-04-28 23:54:54 +0200
commit492903ba6c89b3f042cd9336b321020b23b98f5f (patch)
treef3306f88bcd69295e7618b83affda42f62c95ba4
parentc2fb79e2fd6ecc15589f532459db06a506696efa (diff)
downloadnix-492903ba6c89b3f042cd9336b321020b23b98f5f.zip
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.
-rw-r--r--CHANGELOG.md2
-rw-r--r--src/sys/select.rs77
-rw-r--r--test/sys/mod.rs1
-rw-r--r--test/sys/test_select.rs55
4 files changed, 133 insertions, 2 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e146c483..da1618d9 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 33d3e638..0b6d2c40 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<c_int>
+where
+ N: Into<Option<c_int>>,
+ R: Into<Option<&'a mut FdSet>>,
+ W: Into<Option<&'a mut FdSet>>,
+ E: Into<Option<&'a mut FdSet>>,
+ 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().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 31cf73b1..1b3e67aa 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 00000000..19d12fba
--- /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));
+}