Skip to content

Commit

Permalink
Merge #1207
Browse files Browse the repository at this point in the history
1207: Add select::FdSet::fds() method r=asomers a=zombiezen

To be more consistent with most Rust APIs and enable cloning of the iterator, I made `FdSet::contains` operate on an immutable borrow instead of a mutable one by copying the set. If this is not desirable, I can roll that back from this PR and focus purely on the `fds()` method.

Co-authored-by: Ross Light <ross@zombiezen.com>
  • Loading branch information
bors[bot] and zombiezen committed Apr 19, 2020
2 parents fdf828b + e8c7961 commit f94e882
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 21 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -17,6 +17,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
identity for filesystem checks per-thread.
(#[1163](https://github.com/nix-rust/nix/pull/1163))
- Derived `Ord`, `PartialOrd` for `unistd::Pid` (#[1189](https://github.com/nix-rust/nix/pull/1189))
- Added `select::FdSet::fds` method to iterate over file descriptors in a set.
([#1207](https://github.com/nix-rust/nix/pull/1207))

### Changed
- Changed `fallocate` return type from `c_int` to `()` (#[1201](https://github.com/nix-rust/nix/pull/1201))
Expand Down
117 changes: 96 additions & 21 deletions src/sys/select.rs
@@ -1,4 +1,6 @@
use std::iter::FusedIterator;
use std::mem;
use std::ops::Range;
use std::os::unix::io::RawFd;
use std::ptr::{null, null_mut};
use libc::{self, c_int};
Expand Down Expand Up @@ -59,14 +61,33 @@ impl FdSet {
///
/// [`select`]: fn.select.html
pub fn highest(&mut self) -> Option<RawFd> {
for i in (0..FD_SETSIZE).rev() {
let i = i as RawFd;
if unsafe { libc::FD_ISSET(i, self as *mut _ as *mut libc::fd_set) } {
return Some(i)
}
}
self.fds(None).next_back()
}

None
/// Returns an iterator over the file descriptors in the set.
///
/// For performance, it takes an optional higher bound: the iterator will
/// not return any elements of the set greater than the given file
/// descriptor.
///
/// # Examples
///
/// ```
/// # extern crate nix;
/// # use nix::sys::select::FdSet;
/// # use std::os::unix::io::RawFd;
/// let mut set = FdSet::new();
/// set.insert(4);
/// set.insert(9);
/// let fds: Vec<RawFd> = set.fds(None).collect();
/// assert_eq!(fds, vec![4, 9]);
/// ```
#[inline]
pub fn fds(&mut self, highest: Option<RawFd>) -> Fds {
Fds {
set: self,
range: 0..highest.map(|h| h as usize + 1).unwrap_or(FD_SETSIZE),
}
}
}

Expand All @@ -76,6 +97,46 @@ impl Default for FdSet {
}
}

/// Iterator over `FdSet`.
#[derive(Debug)]
pub struct Fds<'a> {
set: &'a mut FdSet,
range: Range<usize>,
}

impl<'a> Iterator for Fds<'a> {
type Item = RawFd;

fn next(&mut self) -> Option<RawFd> {
while let Some(i) = self.range.next() {
if self.set.contains(i as RawFd) {
return Some(i as RawFd);
}
}
None
}

#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let (_, upper) = self.range.size_hint();
(0, upper)
}
}

impl<'a> DoubleEndedIterator for Fds<'a> {
#[inline]
fn next_back(&mut self) -> Option<RawFd> {
while let Some(i) = self.range.next_back() {
if self.set.contains(i as RawFd) {
return Some(i as RawFd);
}
}
None
}
}

impl<'a> FusedIterator for Fds<'a> {}

/// Monitors file descriptors for readiness
///
/// Returns the total number of ready file descriptors in all sets. The sets are changed so that all
Expand All @@ -100,9 +161,9 @@ impl Default for FdSet {
///
/// [`FdSet::highest`]: struct.FdSet.html#method.highest
pub fn select<'a, N, R, W, E, T>(nfds: N,
readfds: R,
writefds: W,
errorfds: E,
readfds: R,
writefds: W,
errorfds: E,
timeout: T) -> Result<c_int>
where
N: Into<Option<c_int>>,
Expand All @@ -129,7 +190,7 @@ where
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(|tv| tv as *mut _ as *mut libc::timeval)
.unwrap_or(null_mut());
.unwrap_or(null_mut());

let res = unsafe {
libc::select(nfds, readfds, writefds, errorfds, timeout)
Expand Down Expand Up @@ -168,10 +229,10 @@ where
///
/// [`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,
readfds: R,
writefds: W,
errorfds: E,
timeout: T,
sigmask: S) -> Result<c_int>
where
N: Into<Option<c_int>>,
Expand Down Expand Up @@ -279,6 +340,20 @@ mod tests {
assert_eq!(set.highest(), Some(7));
}

#[test]
fn fdset_fds() {
let mut set = FdSet::new();
assert_eq!(set.fds(None).collect::<Vec<_>>(), vec![]);
set.insert(0);
assert_eq!(set.fds(None).collect::<Vec<_>>(), vec![0]);
set.insert(90);
assert_eq!(set.fds(None).collect::<Vec<_>>(), vec![0, 90]);

// highest limit
assert_eq!(set.fds(Some(89)).collect::<Vec<_>>(), vec![0]);
assert_eq!(set.fds(Some(90)).collect::<Vec<_>>(), vec![0, 90]);
}

#[test]
fn test_select() {
let (r1, w1) = pipe().unwrap();
Expand Down Expand Up @@ -311,9 +386,9 @@ mod tests {

let mut timeout = TimeVal::seconds(10);
assert_eq!(1, select(Some(fd_set.highest().unwrap() + 1),
&mut fd_set,
None,
None,
&mut fd_set,
None,
None,
&mut timeout).unwrap());
assert!(fd_set.contains(r1));
assert!(!fd_set.contains(r2));
Expand All @@ -331,9 +406,9 @@ mod tests {

let mut timeout = TimeVal::seconds(10);
assert_eq!(1, select(::std::cmp::max(r1, r2) + 1,
&mut fd_set,
None,
None,
&mut fd_set,
None,
None,
&mut timeout).unwrap());
assert!(fd_set.contains(r1));
assert!(!fd_set.contains(r2));
Expand Down

0 comments on commit f94e882

Please sign in to comment.