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 select::FdSet::fds() method #1207

Merged
merged 8 commits into from
Apr 19, 2020
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 @@ -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.
zombiezen marked this conversation as resolved.
Show resolved Hide resolved
([#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
Original file line number Diff line number Diff line change
@@ -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