Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add pselect syscall #894

Merged
merged 1 commit into from
Apr 28, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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));
}