nix/sys/
select.rs

1//! Portably monitor a group of file descriptors for readiness.
2use std::convert::TryFrom;
3use std::iter::FusedIterator;
4use std::mem;
5use std::ops::Range;
6use std::os::unix::io::RawFd;
7use std::ptr::{null, null_mut};
8use libc::{self, c_int};
9use crate::Result;
10use crate::errno::Errno;
11use crate::sys::time::{TimeSpec, TimeVal};
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>(nfds: N,
177    readfds: R,
178    writefds: W,
179    errorfds: E,
180                                 timeout: T) -> Result<c_int>
181where
182    N: Into<Option<c_int>>,
183    R: Into<Option<&'a mut FdSet>>,
184    W: Into<Option<&'a mut FdSet>>,
185    E: Into<Option<&'a mut FdSet>>,
186    T: Into<Option<&'a mut TimeVal>>,
187{
188    let mut readfds = readfds.into();
189    let mut writefds = writefds.into();
190    let mut errorfds = errorfds.into();
191    let timeout = timeout.into();
192
193    let nfds = nfds.into().unwrap_or_else(|| {
194        readfds.iter_mut()
195            .chain(writefds.iter_mut())
196            .chain(errorfds.iter_mut())
197            .map(|set| set.highest().unwrap_or(-1))
198            .max()
199            .unwrap_or(-1) + 1
200    });
201
202    let readfds = readfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
203    let writefds = writefds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
204    let errorfds = errorfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
205    let timeout = timeout.map(|tv| tv as *mut _ as *mut libc::timeval)
206        .unwrap_or(null_mut());
207
208    let res = unsafe {
209        libc::select(nfds, readfds, writefds, errorfds, timeout)
210    };
211
212    Errno::result(res)
213}
214
215feature! {
216#![feature = "signal"]
217
218use crate::sys::signal::SigSet;
219
220/// Monitors file descriptors for readiness with an altered signal mask.
221///
222/// Returns the total number of ready file descriptors in all sets. The sets are changed so that all
223/// file descriptors that are ready for the given operation are set.
224///
225/// When this function returns, the original signal mask is restored.
226///
227/// Unlike [`select`](#fn.select), `pselect` does not mutate the `timeout` value.
228///
229/// # Parameters
230///
231/// * `nfds`: The highest file descriptor set in any of the passed `FdSet`s, plus 1. If `None`, this
232///   is calculated automatically by calling [`FdSet::highest`] on all descriptor sets and adding 1
233///   to the maximum of that.
234/// * `readfds`: File descriptors to check for read readiness
235/// * `writefds`: File descriptors to check for write readiness
236/// * `errorfds`: File descriptors to check for pending error conditions.
237/// * `timeout`: Maximum time to wait for descriptors to become ready (`None` to block
238///   indefinitely).
239/// * `sigmask`: Signal mask to activate while waiting for file descriptors to turn
240///    ready (`None` to set no alternative signal mask).
241///
242/// # References
243///
244/// [pselect(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pselect.html)
245///
246/// [The new pselect() system call](https://lwn.net/Articles/176911/)
247///
248/// [`FdSet::highest`]: struct.FdSet.html#method.highest
249pub fn pselect<'a, N, R, W, E, T, S>(nfds: N,
250    readfds: R,
251    writefds: W,
252    errorfds: E,
253    timeout: T,
254                                     sigmask: S) -> Result<c_int>
255where
256    N: Into<Option<c_int>>,
257    R: Into<Option<&'a mut FdSet>>,
258    W: Into<Option<&'a mut FdSet>>,
259    E: Into<Option<&'a mut FdSet>>,
260    T: Into<Option<&'a TimeSpec>>,
261    S: Into<Option<&'a SigSet>>,
262{
263    let mut readfds = readfds.into();
264    let mut writefds = writefds.into();
265    let mut errorfds = errorfds.into();
266    let sigmask = sigmask.into();
267    let timeout = timeout.into();
268
269    let nfds = nfds.into().unwrap_or_else(|| {
270        readfds.iter_mut()
271            .chain(writefds.iter_mut())
272            .chain(errorfds.iter_mut())
273            .map(|set| set.highest().unwrap_or(-1))
274            .max()
275            .unwrap_or(-1) + 1
276    });
277
278    let readfds = readfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
279    let writefds = writefds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
280    let errorfds = errorfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
281    let timeout = timeout.map(|ts| ts.as_ref() as *const libc::timespec).unwrap_or(null());
282    let sigmask = sigmask.map(|sm| sm.as_ref() as *const libc::sigset_t).unwrap_or(null());
283
284    let res = unsafe {
285        libc::pselect(nfds, readfds, writefds, errorfds, timeout, sigmask)
286    };
287
288    Errno::result(res)
289}
290}
291
292#[cfg(test)]
293mod tests {
294    use super::*;
295    use std::os::unix::io::RawFd;
296    use crate::sys::time::{TimeVal, TimeValLike};
297    use crate::unistd::{write, pipe};
298
299    #[test]
300    fn fdset_insert() {
301        let mut fd_set = FdSet::new();
302
303        for i in 0..FD_SETSIZE {
304            assert!(!fd_set.contains(i as RawFd));
305        }
306
307        fd_set.insert(7);
308
309        assert!(fd_set.contains(7));
310    }
311
312    #[test]
313    fn fdset_remove() {
314        let mut fd_set = FdSet::new();
315
316        for i in 0..FD_SETSIZE {
317            assert!(!fd_set.contains(i as RawFd));
318        }
319
320        fd_set.insert(7);
321        fd_set.remove(7);
322
323        for i in 0..FD_SETSIZE {
324            assert!(!fd_set.contains(i as RawFd));
325        }
326    }
327
328    #[test]
329    fn fdset_clear() {
330        let mut fd_set = FdSet::new();
331        fd_set.insert(1);
332        fd_set.insert((FD_SETSIZE / 2) as RawFd);
333        fd_set.insert((FD_SETSIZE - 1) as RawFd);
334
335        fd_set.clear();
336
337        for i in 0..FD_SETSIZE {
338            assert!(!fd_set.contains(i as RawFd));
339        }
340    }
341
342    #[test]
343    fn fdset_highest() {
344        let mut set = FdSet::new();
345        assert_eq!(set.highest(), None);
346        set.insert(0);
347        assert_eq!(set.highest(), Some(0));
348        set.insert(90);
349        assert_eq!(set.highest(), Some(90));
350        set.remove(0);
351        assert_eq!(set.highest(), Some(90));
352        set.remove(90);
353        assert_eq!(set.highest(), None);
354
355        set.insert(4);
356        set.insert(5);
357        set.insert(7);
358        assert_eq!(set.highest(), Some(7));
359    }
360
361    #[test]
362    fn fdset_fds() {
363        let mut set = FdSet::new();
364        assert_eq!(set.fds(None).collect::<Vec<_>>(), vec![]);
365        set.insert(0);
366        assert_eq!(set.fds(None).collect::<Vec<_>>(), vec![0]);
367        set.insert(90);
368        assert_eq!(set.fds(None).collect::<Vec<_>>(), vec![0, 90]);
369
370        // highest limit
371        assert_eq!(set.fds(Some(89)).collect::<Vec<_>>(), vec![0]);
372        assert_eq!(set.fds(Some(90)).collect::<Vec<_>>(), vec![0, 90]);
373    }
374
375    #[test]
376    fn test_select() {
377        let (r1, w1) = pipe().unwrap();
378        write(w1, b"hi!").unwrap();
379        let (r2, _w2) = pipe().unwrap();
380
381        let mut fd_set = FdSet::new();
382        fd_set.insert(r1);
383        fd_set.insert(r2);
384
385        let mut timeout = TimeVal::seconds(10);
386        assert_eq!(1, select(None,
387                             &mut fd_set,
388                             None,
389                             None,
390                             &mut timeout).unwrap());
391        assert!(fd_set.contains(r1));
392        assert!(!fd_set.contains(r2));
393    }
394
395    #[test]
396    fn test_select_nfds() {
397        let (r1, w1) = pipe().unwrap();
398        write(w1, b"hi!").unwrap();
399        let (r2, _w2) = pipe().unwrap();
400
401        let mut fd_set = FdSet::new();
402        fd_set.insert(r1);
403        fd_set.insert(r2);
404
405        let mut timeout = TimeVal::seconds(10);
406        assert_eq!(1, select(Some(fd_set.highest().unwrap() + 1),
407                &mut fd_set,
408                None,
409                None,
410                             &mut timeout).unwrap());
411        assert!(fd_set.contains(r1));
412        assert!(!fd_set.contains(r2));
413    }
414
415    #[test]
416    fn test_select_nfds2() {
417        let (r1, w1) = pipe().unwrap();
418        write(w1, b"hi!").unwrap();
419        let (r2, _w2) = pipe().unwrap();
420
421        let mut fd_set = FdSet::new();
422        fd_set.insert(r1);
423        fd_set.insert(r2);
424
425        let mut timeout = TimeVal::seconds(10);
426        assert_eq!(1, select(::std::cmp::max(r1, r2) + 1,
427                &mut fd_set,
428                None,
429                None,
430                             &mut timeout).unwrap());
431        assert!(fd_set.contains(r1));
432        assert!(!fd_set.contains(r2));
433    }
434}