diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f76bfaa24..fab232a635 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,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)) diff --git a/src/sys/select.rs b/src/sys/select.rs index 32569acc70..a46985bc4c 100644 --- a/src/sys/select.rs +++ b/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}; @@ -59,14 +61,33 @@ impl FdSet { /// /// [`select`]: fn.select.html pub fn highest(&mut self) -> Option { - 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 = set.fds(None).collect(); + /// assert_eq!(fds, vec![4, 9]); + /// ``` + #[inline] + pub fn fds(&mut self, highest: Option) -> Fds { + Fds { + set: self, + range: 0..highest.map(|h| h as usize + 1).unwrap_or(FD_SETSIZE), + } } } @@ -76,6 +97,46 @@ impl Default for FdSet { } } +/// Iterator over `FdSet`. +#[derive(Debug)] +pub struct Fds<'a> { + set: &'a mut FdSet, + range: Range, +} + +impl<'a> Iterator for Fds<'a> { + type Item = RawFd; + + fn next(&mut self) -> Option { + 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) { + let (_, upper) = self.range.size_hint(); + (0, upper) + } +} + +impl<'a> DoubleEndedIterator for Fds<'a> { + #[inline] + fn next_back(&mut self) -> Option { + 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 @@ -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 where N: Into>, @@ -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) @@ -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 where N: Into>, @@ -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![]); + set.insert(0); + assert_eq!(set.fds(None).collect::>(), vec![0]); + set.insert(90); + assert_eq!(set.fds(None).collect::>(), vec![0, 90]); + + // highest limit + assert_eq!(set.fds(Some(89)).collect::>(), vec![0]); + assert_eq!(set.fds(Some(90)).collect::>(), vec![0, 90]); + } + #[test] fn test_select() { let (r1, w1) = pipe().unwrap(); @@ -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)); @@ -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));