nix/sys/
select.rs

1//! Portably monitor a group of file descriptors for readiness.
2use crate::errno::Errno;
3use crate::sys::time::{TimeSpec, TimeVal};
4use crate::Result;
5use libc::{self, c_int};
6use std::convert::TryFrom;
7use std::iter::FusedIterator;
8use std::mem;
9use std::ops::Range;
10use std::os::unix::io::RawFd;
11use std::ptr::{null, null_mut};
12
13pub use libc::FD_SETSIZE;
14
15/// Contains a set of file descriptors used by [`select`]
16#[repr(transparent)]
17#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
18pub struct FdSet(libc::fd_set);
19
20fn assert_fd_valid(fd: RawFd) {
21    assert!(
22        usize::try_from(fd).map_or(false, |fd| fd < FD_SETSIZE),
23        "fd must be in the range 0..FD_SETSIZE",
24    );
25}
26
27impl FdSet {
28    /// Create an empty `FdSet`
29    pub fn new() -> FdSet {
30        let mut fdset = mem::MaybeUninit::uninit();
31        unsafe {
32            libc::FD_ZERO(fdset.as_mut_ptr());
33            FdSet(fdset.assume_init())
34        }
35    }
36
37    /// Add a file descriptor to an `FdSet`
38    pub fn insert(&mut self, fd: RawFd) {
39        assert_fd_valid(fd);
40        unsafe { libc::FD_SET(fd, &mut self.0) };
41    }
42
43    /// Remove a file descriptor from an `FdSet`
44    pub fn remove(&mut self, fd: RawFd) {
45        assert_fd_valid(fd);
46        unsafe { libc::FD_CLR(fd, &mut self.0) };
47    }
48
49    /// Test an `FdSet` for the presence of a certain file descriptor.
50    pub fn contains(&self, fd: RawFd) -> bool {
51        assert_fd_valid(fd);
52        unsafe { libc::FD_ISSET(fd, &self.0) }
53    }
54
55    /// Remove all file descriptors from this `FdSet`.
56    pub fn clear(&mut self) {
57        unsafe { libc::FD_ZERO(&mut self.0) };
58    }
59
60    /// Finds the highest file descriptor in the set.
61    ///
62    /// Returns `None` if the set is empty.
63    ///
64    /// This can be used to calculate the `nfds` parameter of the [`select`] function.
65    ///
66    /// # Example
67    ///
68    /// ```
69    /// # use nix::sys::select::FdSet;
70    /// let mut set = FdSet::new();
71    /// set.insert(4);
72    /// set.insert(9);
73    /// assert_eq!(set.highest(), Some(9));
74    /// ```
75    ///
76    /// [`select`]: fn.select.html
77    pub fn highest(&self) -> Option<RawFd> {
78        self.fds(None).next_back()
79    }
80
81    /// Returns an iterator over the file descriptors in the set.
82    ///
83    /// For performance, it takes an optional higher bound: the iterator will
84    /// not return any elements of the set greater than the given file
85    /// descriptor.
86    ///
87    /// # Examples
88    ///
89    /// ```
90    /// # use nix::sys::select::FdSet;
91    /// # use std::os::unix::io::RawFd;
92    /// let mut set = FdSet::new();
93    /// set.insert(4);
94    /// set.insert(9);
95    /// let fds: Vec<RawFd> = set.fds(None).collect();
96    /// assert_eq!(fds, vec![4, 9]);
97    /// ```
98    #[inline]
99    pub fn fds(&self, highest: Option<RawFd>) -> Fds {
100        Fds {
101            set: self,
102            range: 0..highest.map(|h| h as usize + 1).unwrap_or(FD_SETSIZE),
103        }
104    }
105}
106
107impl Default for FdSet {
108    fn default() -> Self {
109        Self::new()
110    }
111}
112
113/// Iterator over `FdSet`.
114#[derive(Debug)]
115pub struct Fds<'a> {
116    set: &'a FdSet,
117    range: Range<usize>,
118}
119
120impl<'a> Iterator for Fds<'a> {
121    type Item = RawFd;
122
123    fn next(&mut self) -> Option<RawFd> {
124        for i in &mut self.range {
125            if self.set.contains(i as RawFd) {
126                return Some(i as RawFd);
127            }
128        }
129        None
130    }
131
132    #[inline]
133    fn size_hint(&self) -> (usize, Option<usize>) {
134        let (_, upper) = self.range.size_hint();
135        (0, upper)
136    }
137}
138
139impl<'a> DoubleEndedIterator for Fds<'a> {
140    #[inline]
141    fn next_back(&mut self) -> Option<RawFd> {
142        while let Some(i) = self.range.next_back() {
143            if self.set.contains(i as RawFd) {
144                return Some(i as RawFd);
145            }
146        }
147        None
148    }
149}
150
151impl<'a> FusedIterator for Fds<'a> {}
152
153/// Monitors file descriptors for readiness
154///
155/// Returns the total number of ready file descriptors in all sets. The sets are changed so that all
156/// file descriptors that are ready for the given operation are set.
157///
158/// When this function returns, `timeout` has an implementation-defined value.
159///
160/// # Parameters
161///
162/// * `nfds`: The highest file descriptor set in any of the passed `FdSet`s, plus 1. If `None`, this
163///   is calculated automatically by calling [`FdSet::highest`] on all descriptor sets and adding 1
164///   to the maximum of that.
165/// * `readfds`: File descriptors to check for being ready to read.
166/// * `writefds`: File descriptors to check for being ready to write.
167/// * `errorfds`: File descriptors to check for pending error conditions.
168/// * `timeout`: Maximum time to wait for descriptors to become ready (`None` to block
169///   indefinitely).
170///
171/// # References
172///
173/// [select(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/select.html)
174///
175/// [`FdSet::highest`]: struct.FdSet.html#method.highest
176pub fn select<'a, N, R, W, E, T>(
177    nfds: N,
178    readfds: R,
179    writefds: W,
180    errorfds: E,
181    timeout: T,
182) -> Result<c_int>
183where
184    N: Into<Option<c_int>>,
185    R: Into<Option<&'a mut FdSet>>,
186    W: Into<Option<&'a mut FdSet>>,
187    E: Into<Option<&'a mut FdSet>>,
188    T: Into<Option<&'a mut TimeVal>>,
189{
190    let mut readfds = readfds.into();
191    let mut writefds = writefds.into();
192    let mut errorfds = errorfds.into();
193    let timeout = timeout.into();
194
195    let nfds = nfds.into().unwrap_or_else(|| {
196        readfds
197            .iter_mut()
198            .chain(writefds.iter_mut())
199            .chain(errorfds.iter_mut())
200            .map(|set| set.highest().unwrap_or(-1))
201            .max()
202            .unwrap_or(-1)
203            + 1
204    });
205
206    let readfds = readfds
207        .map(|set| set as *mut _ as *mut libc::fd_set)
208        .unwrap_or(null_mut());
209    let writefds = writefds
210        .map(|set| set as *mut _ as *mut libc::fd_set)
211        .unwrap_or(null_mut());
212    let errorfds = errorfds
213        .map(|set| set as *mut _ as *mut libc::fd_set)
214        .unwrap_or(null_mut());
215    let timeout = timeout
216        .map(|tv| tv as *mut _ as *mut libc::timeval)
217        .unwrap_or(null_mut());
218
219    let res =
220        unsafe { libc::select(nfds, readfds, writefds, errorfds, timeout) };
221
222    Errno::result(res)
223}
224
225feature! {
226#![feature = "signal"]
227
228use crate::sys::signal::SigSet;
229
230/// Monitors file descriptors for readiness with an altered signal mask.
231///
232/// Returns the total number of ready file descriptors in all sets. The sets are changed so that all
233/// file descriptors that are ready for the given operation are set.
234///
235/// When this function returns, the original signal mask is restored.
236///
237/// Unlike [`select`](#fn.select), `pselect` does not mutate the `timeout` value.
238///
239/// # Parameters
240///
241/// * `nfds`: The highest file descriptor set in any of the passed `FdSet`s, plus 1. If `None`, this
242///   is calculated automatically by calling [`FdSet::highest`] on all descriptor sets and adding 1
243///   to the maximum of that.
244/// * `readfds`: File descriptors to check for read readiness
245/// * `writefds`: File descriptors to check for write readiness
246/// * `errorfds`: File descriptors to check for pending error conditions.
247/// * `timeout`: Maximum time to wait for descriptors to become ready (`None` to block
248///   indefinitely).
249/// * `sigmask`: Signal mask to activate while waiting for file descriptors to turn
250///    ready (`None` to set no alternative signal mask).
251///
252/// # References
253///
254/// [pselect(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pselect.html)
255///
256/// [The new pselect() system call](https://lwn.net/Articles/176911/)
257///
258/// [`FdSet::highest`]: struct.FdSet.html#method.highest
259pub fn pselect<'a, N, R, W, E, T, S>(nfds: N,
260    readfds: R,
261    writefds: W,
262    errorfds: E,
263    timeout: T,
264                                     sigmask: S) -> Result<c_int>
265where
266    N: Into<Option<c_int>>,
267    R: Into<Option<&'a mut FdSet>>,
268    W: Into<Option<&'a mut FdSet>>,
269    E: Into<Option<&'a mut FdSet>>,
270    T: Into<Option<&'a TimeSpec>>,
271    S: Into<Option<&'a SigSet>>,
272{
273    let mut readfds = readfds.into();
274    let mut writefds = writefds.into();
275    let mut errorfds = errorfds.into();
276    let sigmask = sigmask.into();
277    let timeout = timeout.into();
278
279    let nfds = nfds.into().unwrap_or_else(|| {
280        readfds.iter_mut()
281            .chain(writefds.iter_mut())
282            .chain(errorfds.iter_mut())
283            .map(|set| set.highest().unwrap_or(-1))
284            .max()
285            .unwrap_or(-1) + 1
286    });
287
288    let readfds = readfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
289    let writefds = writefds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
290    let errorfds = errorfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
291    let timeout = timeout.map(|ts| ts.as_ref() as *const libc::timespec).unwrap_or(null());
292    let sigmask = sigmask.map(|sm| sm.as_ref() as *const libc::sigset_t).unwrap_or(null());
293
294    let res = unsafe {
295        libc::pselect(nfds, readfds, writefds, errorfds, timeout, sigmask)
296    };
297
298    Errno::result(res)
299}
300}
301
302#[cfg(test)]
303mod tests {
304    use super::*;
305    use crate::sys::time::{TimeVal, TimeValLike};
306    use crate::unistd::{pipe, write};
307    use std::os::unix::io::RawFd;
308
309    #[test]
310    fn fdset_insert() {
311        let mut fd_set = FdSet::new();
312
313        for i in 0..FD_SETSIZE {
314            assert!(!fd_set.contains(i as RawFd));
315        }
316
317        fd_set.insert(7);
318
319        assert!(fd_set.contains(7));
320    }
321
322    #[test]
323    fn fdset_remove() {
324        let mut fd_set = FdSet::new();
325
326        for i in 0..FD_SETSIZE {
327            assert!(!fd_set.contains(i as RawFd));
328        }
329
330        fd_set.insert(7);
331        fd_set.remove(7);
332
333        for i in 0..FD_SETSIZE {
334            assert!(!fd_set.contains(i as RawFd));
335        }
336    }
337
338    #[test]
339    fn fdset_clear() {
340        let mut fd_set = FdSet::new();
341        fd_set.insert(1);
342        fd_set.insert((FD_SETSIZE / 2) as RawFd);
343        fd_set.insert((FD_SETSIZE - 1) as RawFd);
344
345        fd_set.clear();
346
347        for i in 0..FD_SETSIZE {
348            assert!(!fd_set.contains(i as RawFd));
349        }
350    }
351
352    #[test]
353    fn fdset_highest() {
354        let mut set = FdSet::new();
355        assert_eq!(set.highest(), None);
356        set.insert(0);
357        assert_eq!(set.highest(), Some(0));
358        set.insert(90);
359        assert_eq!(set.highest(), Some(90));
360        set.remove(0);
361        assert_eq!(set.highest(), Some(90));
362        set.remove(90);
363        assert_eq!(set.highest(), None);
364
365        set.insert(4);
366        set.insert(5);
367        set.insert(7);
368        assert_eq!(set.highest(), Some(7));
369    }
370
371    #[test]
372    fn fdset_fds() {
373        let mut set = FdSet::new();
374        assert_eq!(set.fds(None).collect::<Vec<_>>(), vec![]);
375        set.insert(0);
376        assert_eq!(set.fds(None).collect::<Vec<_>>(), vec![0]);
377        set.insert(90);
378        assert_eq!(set.fds(None).collect::<Vec<_>>(), vec![0, 90]);
379
380        // highest limit
381        assert_eq!(set.fds(Some(89)).collect::<Vec<_>>(), vec![0]);
382        assert_eq!(set.fds(Some(90)).collect::<Vec<_>>(), vec![0, 90]);
383    }
384
385    #[test]
386    fn test_select() {
387        let (r1, w1) = pipe().unwrap();
388        write(w1, b"hi!").unwrap();
389        let (r2, _w2) = pipe().unwrap();
390
391        let mut fd_set = FdSet::new();
392        fd_set.insert(r1);
393        fd_set.insert(r2);
394
395        let mut timeout = TimeVal::seconds(10);
396        assert_eq!(
397            1,
398            select(None, &mut fd_set, None, None, &mut timeout).unwrap()
399        );
400        assert!(fd_set.contains(r1));
401        assert!(!fd_set.contains(r2));
402    }
403
404    #[test]
405    fn test_select_nfds() {
406        let (r1, w1) = pipe().unwrap();
407        write(w1, b"hi!").unwrap();
408        let (r2, _w2) = pipe().unwrap();
409
410        let mut fd_set = FdSet::new();
411        fd_set.insert(r1);
412        fd_set.insert(r2);
413
414        let mut timeout = TimeVal::seconds(10);
415        assert_eq!(
416            1,
417            select(
418                Some(fd_set.highest().unwrap() + 1),
419                &mut fd_set,
420                None,
421                None,
422                &mut timeout
423            )
424            .unwrap()
425        );
426        assert!(fd_set.contains(r1));
427        assert!(!fd_set.contains(r2));
428    }
429
430    #[test]
431    fn test_select_nfds2() {
432        let (r1, w1) = pipe().unwrap();
433        write(w1, b"hi!").unwrap();
434        let (r2, _w2) = pipe().unwrap();
435
436        let mut fd_set = FdSet::new();
437        fd_set.insert(r1);
438        fd_set.insert(r2);
439
440        let mut timeout = TimeVal::seconds(10);
441        assert_eq!(
442            1,
443            select(
444                ::std::cmp::max(r1, r2) + 1,
445                &mut fd_set,
446                None,
447                None,
448                &mut timeout
449            )
450            .unwrap()
451        );
452        assert!(fd_set.contains(r1));
453        assert!(!fd_set.contains(r2));
454    }
455}