Coverage Report

Created: 2024-04-26 06:25

/rust/registry/src/index.crates.io-6f17d22bba15001f/nix-0.26.2/src/sys/select.rs
Line
Count
Source (jump to first uncovered line)
1
//! Portably monitor a group of file descriptors for readiness.
2
use crate::errno::Errno;
3
use crate::sys::time::{TimeSpec, TimeVal};
4
use crate::Result;
5
use libc::{self, c_int};
6
use std::convert::TryFrom;
7
use std::iter::FusedIterator;
8
use std::mem;
9
use std::ops::Range;
10
use std::os::unix::io::RawFd;
11
use std::ptr::{null, null_mut};
12
13
pub use libc::FD_SETSIZE;
14
15
/// Contains a set of file descriptors used by [`select`]
16
#[repr(transparent)]
17
0
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
18
pub struct FdSet(libc::fd_set);
19
20
0
fn assert_fd_valid(fd: RawFd) {
21
0
    assert!(
22
0
        usize::try_from(fd).map_or(false, |fd| fd < FD_SETSIZE),
23
0
        "fd must be in the range 0..FD_SETSIZE",
24
0
    );
25
0
}
26
27
impl FdSet {
28
    /// Create an empty `FdSet`
29
0
    pub fn new() -> FdSet {
30
0
        let mut fdset = mem::MaybeUninit::uninit();
31
0
        unsafe {
32
0
            libc::FD_ZERO(fdset.as_mut_ptr());
33
0
            FdSet(fdset.assume_init())
34
0
        }
35
0
    }
36
37
    /// Add a file descriptor to an `FdSet`
38
0
    pub fn insert(&mut self, fd: RawFd) {
39
0
        assert_fd_valid(fd);
40
0
        unsafe { libc::FD_SET(fd, &mut self.0) };
41
0
    }
42
43
    /// Remove a file descriptor from an `FdSet`
44
0
    pub fn remove(&mut self, fd: RawFd) {
45
0
        assert_fd_valid(fd);
46
0
        unsafe { libc::FD_CLR(fd, &mut self.0) };
47
0
    }
48
49
    /// Test an `FdSet` for the presence of a certain file descriptor.
50
0
    pub fn contains(&self, fd: RawFd) -> bool {
51
0
        assert_fd_valid(fd);
52
0
        unsafe { libc::FD_ISSET(fd, &self.0) }
53
0
    }
54
55
    /// Remove all file descriptors from this `FdSet`.
56
0
    pub fn clear(&mut self) {
57
0
        unsafe { libc::FD_ZERO(&mut self.0) };
58
0
    }
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
0
    pub fn highest(&self) -> Option<RawFd> {
78
0
        self.fds(None).next_back()
79
0
    }
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
0
    pub fn fds(&self, highest: Option<RawFd>) -> Fds {
100
0
        Fds {
101
0
            set: self,
102
0
            range: 0..highest.map(|h| h as usize + 1).unwrap_or(FD_SETSIZE),
103
0
        }
104
0
    }
105
}
106
107
impl Default for FdSet {
108
0
    fn default() -> Self {
109
0
        Self::new()
110
0
    }
111
}
112
113
/// Iterator over `FdSet`.
114
0
#[derive(Debug)]
115
pub struct Fds<'a> {
116
    set: &'a FdSet,
117
    range: Range<usize>,
118
}
119
120
impl<'a> Iterator for Fds<'a> {
121
    type Item = RawFd;
122
123
0
    fn next(&mut self) -> Option<RawFd> {
124
0
        for i in &mut self.range {
125
0
            if self.set.contains(i as RawFd) {
126
0
                return Some(i as RawFd);
127
0
            }
128
        }
129
0
        None
130
0
    }
131
132
    #[inline]
133
0
    fn size_hint(&self) -> (usize, Option<usize>) {
134
0
        let (_, upper) = self.range.size_hint();
135
0
        (0, upper)
136
0
    }
137
}
138
139
impl<'a> DoubleEndedIterator for Fds<'a> {
140
    #[inline]
141
0
    fn next_back(&mut self) -> Option<RawFd> {
142
0
        while let Some(i) = self.range.next_back() {
143
0
            if self.set.contains(i as RawFd) {
144
0
                return Some(i as RawFd);
145
0
            }
146
        }
147
0
        None
148
0
    }
149
}
150
151
impl<'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
176
0
pub fn select<'a, N, R, W, E, T>(
177
0
    nfds: N,
178
0
    readfds: R,
179
0
    writefds: W,
180
0
    errorfds: E,
181
0
    timeout: T,
182
0
) -> Result<c_int>
183
0
where
184
0
    N: Into<Option<c_int>>,
185
0
    R: Into<Option<&'a mut FdSet>>,
186
0
    W: Into<Option<&'a mut FdSet>>,
187
0
    E: Into<Option<&'a mut FdSet>>,
188
0
    T: Into<Option<&'a mut TimeVal>>,
189
0
{
190
0
    let mut readfds = readfds.into();
191
0
    let mut writefds = writefds.into();
192
0
    let mut errorfds = errorfds.into();
193
0
    let timeout = timeout.into();
194
0
195
0
    let nfds = nfds.into().unwrap_or_else(|| {
196
0
        readfds
197
0
            .iter_mut()
198
0
            .chain(writefds.iter_mut())
199
0
            .chain(errorfds.iter_mut())
200
0
            .map(|set| set.highest().unwrap_or(-1))
201
0
            .max()
202
0
            .unwrap_or(-1)
203
0
            + 1
204
0
    });
205
0
206
0
    let readfds = readfds
207
0
        .map(|set| set as *mut _ as *mut libc::fd_set)
208
0
        .unwrap_or(null_mut());
209
0
    let writefds = writefds
210
0
        .map(|set| set as *mut _ as *mut libc::fd_set)
211
0
        .unwrap_or(null_mut());
212
0
    let errorfds = errorfds
213
0
        .map(|set| set as *mut _ as *mut libc::fd_set)
214
0
        .unwrap_or(null_mut());
215
0
    let timeout = timeout
216
0
        .map(|tv| tv as *mut _ as *mut libc::timeval)
217
0
        .unwrap_or(null_mut());
218
0
219
0
    let res =
220
0
        unsafe { libc::select(nfds, readfds, writefds, errorfds, timeout) };
221
0
222
0
    Errno::result(res)
223
0
}
224
225
feature! {
226
#![feature = "signal"]
227
228
use 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
259
0
pub fn pselect<'a, N, R, W, E, T, S>(nfds: N,
260
0
    readfds: R,
261
0
    writefds: W,
262
0
    errorfds: E,
263
0
    timeout: T,
264
0
                                     sigmask: S) -> Result<c_int>
265
0
where
266
0
    N: Into<Option<c_int>>,
267
0
    R: Into<Option<&'a mut FdSet>>,
268
0
    W: Into<Option<&'a mut FdSet>>,
269
0
    E: Into<Option<&'a mut FdSet>>,
270
0
    T: Into<Option<&'a TimeSpec>>,
271
0
    S: Into<Option<&'a SigSet>>,
272
0
{
273
0
    let mut readfds = readfds.into();
274
0
    let mut writefds = writefds.into();
275
0
    let mut errorfds = errorfds.into();
276
0
    let sigmask = sigmask.into();
277
0
    let timeout = timeout.into();
278
0
279
0
    let nfds = nfds.into().unwrap_or_else(|| {
280
0
        readfds.iter_mut()
281
0
            .chain(writefds.iter_mut())
282
0
            .chain(errorfds.iter_mut())
283
0
            .map(|set| set.highest().unwrap_or(-1))
284
0
            .max()
285
0
            .unwrap_or(-1) + 1
286
0
    });
287
0
288
0
    let readfds = readfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
289
0
    let writefds = writefds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
290
0
    let errorfds = errorfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
291
0
    let timeout = timeout.map(|ts| ts.as_ref() as *const libc::timespec).unwrap_or(null());
292
0
    let sigmask = sigmask.map(|sm| sm.as_ref() as *const libc::sigset_t).unwrap_or(null());
293
0
294
0
    let res = unsafe {
295
0
        libc::pselect(nfds, readfds, writefds, errorfds, timeout, sigmask)
296
0
    };
297
0
298
0
    Errno::result(res)
299
0
}
300
}
301
302
#[cfg(test)]
303
mod 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
}