Skip to content

Commit

Permalink
select: add pselect syscall
Browse files Browse the repository at this point in the history
This is a straight port of @abbradar's work in #276, with
two (somewhat weak) tests and a bit of documentation.
  • Loading branch information
antifuchs committed Apr 28, 2018
1 parent c2fb79e commit 492903b
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 2 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
77 changes: 75 additions & 2 deletions src/sys/select.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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::*;
Expand Down
1 change: 1 addition & 0 deletions test/sys/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
55 changes: 55 additions & 0 deletions test/sys/test_select.rs
Original file line number Diff line number Diff line change
@@ -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));
}

0 comments on commit 492903b

Please sign in to comment.