1 use std::mem;
2 use std::os::unix::io::RawFd;
3 use std::ptr::{null, null_mut};
4 use libc::{self, c_int};
5 use Result;
6 use errno::Errno;
7 use sys::signal::SigSet;
8 use sys::time::{TimeSpec, TimeVal};
9
10 pub use libc::FD_SETSIZE;
11
12 // FIXME: Change to repr(transparent) once it's stable
13 #[repr(C)]
14 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
15 pub struct FdSet(libc::fd_set);
16
17 impl FdSet {
new() -> FdSet18 pub fn new() -> FdSet {
19 let mut fdset = unsafe { mem::uninitialized() };
20 unsafe { libc::FD_ZERO(&mut fdset) };
21 FdSet(fdset)
22 }
23
insert(&mut self, fd: RawFd)24 pub fn insert(&mut self, fd: RawFd) {
25 unsafe { libc::FD_SET(fd, &mut self.0) };
26 }
27
remove(&mut self, fd: RawFd)28 pub fn remove(&mut self, fd: RawFd) {
29 unsafe { libc::FD_CLR(fd, &mut self.0) };
30 }
31
contains(&mut self, fd: RawFd) -> bool32 pub fn contains(&mut self, fd: RawFd) -> bool {
33 unsafe { libc::FD_ISSET(fd, &mut self.0) }
34 }
35
clear(&mut self)36 pub fn clear(&mut self) {
37 unsafe { libc::FD_ZERO(&mut self.0) };
38 }
39
40 /// Finds the highest file descriptor in the set.
41 ///
42 /// Returns `None` if the set is empty.
43 ///
44 /// This can be used to calculate the `nfds` parameter of the [`select`] function.
45 ///
46 /// # Example
47 ///
48 /// ```
49 /// # extern crate nix;
50 /// # use nix::sys::select::FdSet;
51 /// # fn main() {
52 /// let mut set = FdSet::new();
53 /// set.insert(4);
54 /// set.insert(9);
55 /// assert_eq!(set.highest(), Some(9));
56 /// # }
57 /// ```
58 ///
59 /// [`select`]: fn.select.html
highest(&mut self) -> Option<RawFd>60 pub fn highest(&mut self) -> Option<RawFd> {
61 for i in (0..FD_SETSIZE).rev() {
62 let i = i as RawFd;
63 if unsafe { libc::FD_ISSET(i, self as *mut _ as *mut libc::fd_set) } {
64 return Some(i)
65 }
66 }
67
68 None
69 }
70 }
71
72 /// Monitors file descriptors for readiness
73 ///
74 /// Returns the total number of ready file descriptors in all sets. The sets are changed so that all
75 /// file descriptors that are ready for the given operation are set.
76 ///
77 /// When this function returns, `timeout` has an implementation-defined value.
78 ///
79 /// # Parameters
80 ///
81 /// * `nfds`: The highest file descriptor set in any of the passed `FdSet`s, plus 1. If `None`, this
82 /// is calculated automatically by calling [`FdSet::highest`] on all descriptor sets and adding 1
83 /// to the maximum of that.
84 /// * `readfds`: File descriptors to check for being ready to read.
85 /// * `writefds`: File descriptors to check for being ready to write.
86 /// * `errorfds`: File descriptors to check for pending error conditions.
87 /// * `timeout`: Maximum time to wait for descriptors to become ready (`None` to block
88 /// indefinitely).
89 ///
90 /// # References
91 ///
92 /// [select(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/select.html)
93 ///
94 /// [`FdSet::highest`]: struct.FdSet.html#method.highest
select<'a, N, R, W, E, T>(nfds: N, readfds: R, writefds: W, errorfds: E, timeout: T) -> Result<c_int> where N: Into<Option<c_int>>, R: Into<Option<&'a mut FdSet>>, W: Into<Option<&'a mut FdSet>>, E: Into<Option<&'a mut FdSet>>, T: Into<Option<&'a mut TimeVal>>,95 pub fn select<'a, N, R, W, E, T>(nfds: N,
96 readfds: R,
97 writefds: W,
98 errorfds: E,
99 timeout: T) -> Result<c_int>
100 where
101 N: Into<Option<c_int>>,
102 R: Into<Option<&'a mut FdSet>>,
103 W: Into<Option<&'a mut FdSet>>,
104 E: Into<Option<&'a mut FdSet>>,
105 T: Into<Option<&'a mut TimeVal>>,
106 {
107 let mut readfds = readfds.into();
108 let mut writefds = writefds.into();
109 let mut errorfds = errorfds.into();
110 let timeout = timeout.into();
111
112 let nfds = nfds.into().unwrap_or_else(|| {
113 readfds.iter_mut()
114 .chain(writefds.iter_mut())
115 .chain(errorfds.iter_mut())
116 .map(|set| set.highest().unwrap_or(-1))
117 .max()
118 .unwrap_or(-1) + 1
119 });
120
121 let readfds = readfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
122 let writefds = writefds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
123 let errorfds = errorfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
124 let timeout = timeout.map(|tv| tv as *mut _ as *mut libc::timeval)
125 .unwrap_or(null_mut());
126
127 let res = unsafe {
128 libc::select(nfds, readfds, writefds, errorfds, timeout)
129 };
130
131 Errno::result(res)
132 }
133
134 /// Monitors file descriptors for readiness with an altered signal mask.
135 ///
136 /// Returns the total number of ready file descriptors in all sets. The sets are changed so that all
137 /// file descriptors that are ready for the given operation are set.
138 ///
139 /// When this function returns, the original signal mask is restored.
140 ///
141 /// Unlike [`select`](#fn.select), `pselect` does not mutate the `timeout` value.
142 ///
143 /// # Parameters
144 ///
145 /// * `nfds`: The highest file descriptor set in any of the passed `FdSet`s, plus 1. If `None`, this
146 /// is calculated automatically by calling [`FdSet::highest`] on all descriptor sets and adding 1
147 /// to the maximum of that.
148 /// * `readfds`: File descriptors to check for read readiness
149 /// * `writefds`: File descriptors to check for write readiness
150 /// * `errorfds`: File descriptors to check for pending error conditions.
151 /// * `timeout`: Maximum time to wait for descriptors to become ready (`None` to block
152 /// indefinitely).
153 /// * `sigmask`: Signal mask to activate while waiting for file descriptors to turn
154 /// ready (`None` to set no alternative signal mask).
155 ///
156 /// # References
157 ///
158 /// [pselect(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/pselect.html)
159 ///
160 /// [The new pselect() system call](https://lwn.net/Articles/176911/)
161 ///
162 /// [`FdSet::highest`]: struct.FdSet.html#method.highest
pselect<'a, N, R, W, E, T, S>(nfds: N, readfds: R, writefds: W, errorfds: E, timeout: T, sigmask: S) -> Result<c_int> where N: Into<Option<c_int>>, R: Into<Option<&'a mut FdSet>>, W: Into<Option<&'a mut FdSet>>, E: Into<Option<&'a mut FdSet>>, T: Into<Option<&'a TimeSpec>>, S: Into<Option<&'a SigSet>>,163 pub fn pselect<'a, N, R, W, E, T, S>(nfds: N,
164 readfds: R,
165 writefds: W,
166 errorfds: E,
167 timeout: T,
168 sigmask: S) -> Result<c_int>
169 where
170 N: Into<Option<c_int>>,
171 R: Into<Option<&'a mut FdSet>>,
172 W: Into<Option<&'a mut FdSet>>,
173 E: Into<Option<&'a mut FdSet>>,
174 T: Into<Option<&'a TimeSpec>>,
175 S: Into<Option<&'a SigSet>>,
176 {
177 let mut readfds = readfds.into();
178 let mut writefds = writefds.into();
179 let mut errorfds = errorfds.into();
180 let sigmask = sigmask.into();
181 let timeout = timeout.into();
182
183 let nfds = nfds.into().unwrap_or_else(|| {
184 readfds.iter_mut()
185 .chain(writefds.iter_mut())
186 .chain(errorfds.iter_mut())
187 .map(|set| set.highest().unwrap_or(-1))
188 .max()
189 .unwrap_or(-1) + 1
190 });
191
192 let readfds = readfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
193 let writefds = writefds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
194 let errorfds = errorfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
195 let timeout = timeout.map(|ts| ts.as_ref() as *const libc::timespec).unwrap_or(null());
196 let sigmask = sigmask.map(|sm| sm.as_ref() as *const libc::sigset_t).unwrap_or(null());
197
198 let res = unsafe {
199 libc::pselect(nfds, readfds, writefds, errorfds, timeout, sigmask)
200 };
201
202 Errno::result(res)
203 }
204
205
206 #[cfg(test)]
207 mod tests {
208 use super::*;
209 use std::os::unix::io::RawFd;
210 use sys::time::{TimeVal, TimeValLike};
211 use unistd::{write, pipe};
212
213 #[test]
fdset_insert()214 fn fdset_insert() {
215 let mut fd_set = FdSet::new();
216
217 for i in 0..FD_SETSIZE {
218 assert!(!fd_set.contains(i as RawFd));
219 }
220
221 fd_set.insert(7);
222
223 assert!(fd_set.contains(7));
224 }
225
226 #[test]
fdset_remove()227 fn fdset_remove() {
228 let mut fd_set = FdSet::new();
229
230 for i in 0..FD_SETSIZE {
231 assert!(!fd_set.contains(i as RawFd));
232 }
233
234 fd_set.insert(7);
235 fd_set.remove(7);
236
237 for i in 0..FD_SETSIZE {
238 assert!(!fd_set.contains(i as RawFd));
239 }
240 }
241
242 #[test]
fdset_clear()243 fn fdset_clear() {
244 let mut fd_set = FdSet::new();
245 fd_set.insert(1);
246 fd_set.insert((FD_SETSIZE / 2) as RawFd);
247 fd_set.insert((FD_SETSIZE - 1) as RawFd);
248
249 fd_set.clear();
250
251 for i in 0..FD_SETSIZE {
252 assert!(!fd_set.contains(i as RawFd));
253 }
254 }
255
256 #[test]
fdset_highest()257 fn fdset_highest() {
258 let mut set = FdSet::new();
259 assert_eq!(set.highest(), None);
260 set.insert(0);
261 assert_eq!(set.highest(), Some(0));
262 set.insert(90);
263 assert_eq!(set.highest(), Some(90));
264 set.remove(0);
265 assert_eq!(set.highest(), Some(90));
266 set.remove(90);
267 assert_eq!(set.highest(), None);
268
269 set.insert(4);
270 set.insert(5);
271 set.insert(7);
272 assert_eq!(set.highest(), Some(7));
273 }
274
275 #[test]
test_select()276 fn test_select() {
277 let (r1, w1) = pipe().unwrap();
278 write(w1, b"hi!").unwrap();
279 let (r2, _w2) = pipe().unwrap();
280
281 let mut fd_set = FdSet::new();
282 fd_set.insert(r1);
283 fd_set.insert(r2);
284
285 let mut timeout = TimeVal::seconds(10);
286 assert_eq!(1, select(None,
287 &mut fd_set,
288 None,
289 None,
290 &mut timeout).unwrap());
291 assert!(fd_set.contains(r1));
292 assert!(!fd_set.contains(r2));
293 }
294
295 #[test]
test_select_nfds()296 fn test_select_nfds() {
297 let (r1, w1) = pipe().unwrap();
298 write(w1, b"hi!").unwrap();
299 let (r2, _w2) = pipe().unwrap();
300
301 let mut fd_set = FdSet::new();
302 fd_set.insert(r1);
303 fd_set.insert(r2);
304
305 let mut timeout = TimeVal::seconds(10);
306 assert_eq!(1, select(Some(fd_set.highest().unwrap() + 1),
307 &mut fd_set,
308 None,
309 None,
310 &mut timeout).unwrap());
311 assert!(fd_set.contains(r1));
312 assert!(!fd_set.contains(r2));
313 }
314
315 #[test]
test_select_nfds2()316 fn test_select_nfds2() {
317 let (r1, w1) = pipe().unwrap();
318 write(w1, b"hi!").unwrap();
319 let (r2, _w2) = pipe().unwrap();
320
321 let mut fd_set = FdSet::new();
322 fd_set.insert(r1);
323 fd_set.insert(r2);
324
325 let mut timeout = TimeVal::seconds(10);
326 assert_eq!(1, select(::std::cmp::max(r1, r2) + 1,
327 &mut fd_set,
328 None,
329 None,
330 &mut timeout).unwrap());
331 assert!(fd_set.contains(r1));
332 assert!(!fd_set.contains(r2));
333 }
334 }
335