/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 | | } |