Skip to content

Commit 1b80024

Browse files
bors[bot]antifuchs
andcommitted
Merge #894
894: Add pselect syscall r=asomers a=antifuchs I saw that #276 was closed, and now I need `pselect`, so here it is! I copied the function body from @abbradar's work, updated the type signatures, added two tests and added a doc comment. Hope this works! Co-authored-by: Andreas Fuchs <[email protected]>
2 parents c2fb79e + 492903b commit 1b80024

File tree

4 files changed

+133
-2
lines changed

4 files changed

+133
-2
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
66
## [Unreleased]
77

88
### Added
9+
- Added `pselect`
10+
([#894](https://github.com/nix-rust/nix/pull/894))
911
- Exposed `preadv` and `pwritev` on the BSDs.
1012
([#883](https://github.com/nix-rust/nix/pull/883))
1113
- Added `mlockall` and `munlockall`

src/sys/select.rs

+75-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
use std::mem;
22
use std::os::unix::io::RawFd;
3-
use std::ptr::null_mut;
3+
use std::ptr::{null, null_mut};
44
use libc::{self, c_int};
55
use Result;
66
use errno::Errno;
7-
use sys::time::TimeVal;
7+
use sys::signal::SigSet;
8+
use sys::time::{TimeSpec, TimeVal};
89

910
pub use libc::FD_SETSIZE;
1011

@@ -131,6 +132,78 @@ where
131132
Errno::result(res)
132133
}
133134

135+
/// Monitors file descriptors for readiness with an altered signal mask.
136+
///
137+
/// Returns the total number of ready file descriptors in all sets. The sets are changed so that all
138+
/// file descriptors that are ready for the given operation are set.
139+
///
140+
/// When this function returns, the original signal mask is restored.
141+
///
142+
/// Unlike [`select`](#fn.select), `pselect` does not mutate the `timeout` value.
143+
///
144+
/// # Parameters
145+
///
146+
/// * `nfds`: The highest file descriptor set in any of the passed `FdSet`s, plus 1. If `None`, this
147+
/// is calculated automatically by calling [`FdSet::highest`] on all descriptor sets and adding 1
148+
/// to the maximum of that.
149+
/// * `readfds`: File descriptors to check for read readiness
150+
/// * `writefds`: File descriptors to check for write readiness
151+
/// * `errorfds`: File descriptors to check for pending error conditions.
152+
/// * `timeout`: Maximum time to wait for descriptors to become ready (`None` to block
153+
/// indefinitely).
154+
/// * `sigmask`: Signal mask to activate while waiting for file descriptors to turn
155+
/// ready (`None` to set no alternative signal mask).
156+
///
157+
/// # References
158+
///
159+
/// [pselect(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/pselect.html)
160+
///
161+
/// [The new pselect() system call](https://lwn.net/Articles/176911/)
162+
///
163+
/// [`FdSet::highest`]: struct.FdSet.html#method.highest
164+
pub fn pselect<'a, N, R, W, E, T, S>(nfds: N,
165+
readfds: R,
166+
writefds: W,
167+
errorfds: E,
168+
timeout: T,
169+
sigmask: S) -> Result<c_int>
170+
where
171+
N: Into<Option<c_int>>,
172+
R: Into<Option<&'a mut FdSet>>,
173+
W: Into<Option<&'a mut FdSet>>,
174+
E: Into<Option<&'a mut FdSet>>,
175+
T: Into<Option<&'a TimeSpec>>,
176+
S: Into<Option<&'a SigSet>>,
177+
{
178+
let mut readfds = readfds.into();
179+
let mut writefds = writefds.into();
180+
let mut errorfds = errorfds.into();
181+
let sigmask = sigmask.into();
182+
let timeout = timeout.into();
183+
184+
let nfds = nfds.into().unwrap_or_else(|| {
185+
readfds.iter_mut()
186+
.chain(writefds.iter_mut())
187+
.chain(errorfds.iter_mut())
188+
.map(|set| set.highest().unwrap_or(-1))
189+
.max()
190+
.unwrap_or(-1) + 1
191+
});
192+
193+
let readfds = readfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
194+
let writefds = writefds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
195+
let errorfds = errorfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
196+
let timeout = timeout.map(|ts| ts.as_ref() as *const libc::timespec).unwrap_or(null());
197+
let sigmask = sigmask.map(|sm| sm.as_ref() as *const libc::sigset_t).unwrap_or(null());
198+
199+
let res = unsafe {
200+
libc::pselect(nfds, readfds, writefds, errorfds, timeout, sigmask)
201+
};
202+
203+
Errno::result(res)
204+
}
205+
206+
134207
#[cfg(test)]
135208
mod tests {
136209
use super::*;

test/sys/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ mod test_aio;
1515
mod test_signalfd;
1616
mod test_socket;
1717
mod test_sockopt;
18+
mod test_select;
1819
mod test_termios;
1920
mod test_ioctl;
2021
mod test_wait;

test/sys/test_select.rs

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
use nix::sys::select::*;
2+
use nix::unistd::{pipe, write};
3+
use nix::sys::signal::SigSet;
4+
use nix::sys::time::{TimeSpec, TimeValLike};
5+
use std::os::unix::io::RawFd;
6+
7+
#[test]
8+
pub fn test_pselect() {
9+
let _mtx = ::SIGNAL_MTX
10+
.lock()
11+
.expect("Mutex got poisoned by another test");
12+
13+
let (r1, w1) = pipe().unwrap();
14+
write(w1, b"hi!").unwrap();
15+
let (r2, _w2) = pipe().unwrap();
16+
17+
let mut fd_set = FdSet::new();
18+
fd_set.insert(r1);
19+
fd_set.insert(r2);
20+
21+
let timeout = TimeSpec::seconds(10);
22+
let sigmask = SigSet::empty();
23+
assert_eq!(
24+
1,
25+
pselect(None, &mut fd_set, None, None, &timeout, &sigmask).unwrap()
26+
);
27+
assert!(fd_set.contains(r1));
28+
assert!(!fd_set.contains(r2));
29+
}
30+
31+
#[test]
32+
pub fn test_pselect_nfds2() {
33+
let (r1, w1) = pipe().unwrap();
34+
write(w1, b"hi!").unwrap();
35+
let (r2, _w2) = pipe().unwrap();
36+
37+
let mut fd_set = FdSet::new();
38+
fd_set.insert(r1);
39+
fd_set.insert(r2);
40+
41+
let timeout = TimeSpec::seconds(10);
42+
assert_eq!(
43+
1,
44+
pselect(
45+
::std::cmp::max(r1, r2) + 1,
46+
&mut fd_set,
47+
None,
48+
None,
49+
&timeout,
50+
None
51+
).unwrap()
52+
);
53+
assert!(fd_set.contains(r1));
54+
assert!(!fd_set.contains(r2));
55+
}

0 commit comments

Comments
 (0)