@@ -53,6 +53,42 @@ impl FdSet {
53
53
* bits = 0
54
54
}
55
55
}
56
+
57
+ /// Finds the highest file descriptor in the set.
58
+ ///
59
+ /// Returns `None` if the set is empty.
60
+ ///
61
+ /// This can be used to calculate the `nfds` parameter of the [`select`] function.
62
+ ///
63
+ /// # Example
64
+ ///
65
+ /// ```
66
+ /// # extern crate nix;
67
+ /// # use nix::sys::select::FdSet;
68
+ /// # fn main() {
69
+ /// let mut set = FdSet::new();
70
+ /// set.insert(4);
71
+ /// set.insert(9);
72
+ /// assert_eq!(set.highest(), Some(9));
73
+ /// # }
74
+ /// ```
75
+ ///
76
+ /// [`select`]: fn.select.html
77
+ pub fn highest ( & self ) -> Option < RawFd > {
78
+ for ( i, & block) in self . bits . iter ( ) . enumerate ( ) . rev ( ) {
79
+ if block != 0 {
80
+ // Highest bit is located at `BITS - 1 - n.leading_zeros()`. Examples:
81
+ // 0b00000001
82
+ // 7 leading zeros, result should be 0 (bit at index 0 is 1)
83
+ // 0b001xxxxx
84
+ // 2 leading zeros, result should be 5 (bit at index 5 is 1) - x may be 0 or 1
85
+
86
+ return Some ( ( i * BITS + BITS - 1 - block. leading_zeros ( ) as usize ) as RawFd ) ;
87
+ }
88
+ }
89
+
90
+ None
91
+ }
56
92
}
57
93
58
94
mod ffi {
@@ -68,11 +104,52 @@ mod ffi {
68
104
}
69
105
}
70
106
71
- pub fn select ( nfds : c_int ,
72
- readfds : Option < & mut FdSet > ,
73
- writefds : Option < & mut FdSet > ,
74
- errorfds : Option < & mut FdSet > ,
75
- timeout : Option < & mut TimeVal > ) -> Result < c_int > {
107
+ /// Monitors file descriptors for readiness (see [select(2)]).
108
+ ///
109
+ /// Returns the total number of ready file descriptors in all sets. The sets are changed so that all
110
+ /// file descriptors that are ready for the given operation are set.
111
+ ///
112
+ /// When this function returns, `timeout` has an implementation-defined value.
113
+ ///
114
+ /// # Parameters
115
+ ///
116
+ /// * `nfds`: The highest file descriptor set in any of the passed `FdSet`s, plus 1. If `None`, this
117
+ /// is calculated automatically by calling [`FdSet::highest`] on all descriptor sets and adding 1
118
+ /// to the maximum of that.
119
+ /// * `readfds`: File descriptors to check for being ready to read.
120
+ /// * `writefds`: File descriptors to check for being ready to write.
121
+ /// * `errorfds`: File descriptors to check for pending error conditions.
122
+ /// * `timeout`: Maximum time to wait for descriptors to become ready (`None` to block
123
+ /// indefinitely).
124
+ ///
125
+ /// [select(2)]: http://man7.org/linux/man-pages/man2/select.2.html
126
+ /// [`FdSet::highest`]: struct.FdSet.html#method.highest
127
+ pub fn select < ' a , N , R , W , E , T > ( nfds : N ,
128
+ readfds : R ,
129
+ writefds : W ,
130
+ errorfds : E ,
131
+ timeout : T ) -> Result < c_int >
132
+ where
133
+ N : Into < Option < c_int > > ,
134
+ R : Into < Option < & ' a mut FdSet > > ,
135
+ W : Into < Option < & ' a mut FdSet > > ,
136
+ E : Into < Option < & ' a mut FdSet > > ,
137
+ T : Into < Option < & ' a mut TimeVal > > ,
138
+ {
139
+ let readfds = readfds. into ( ) ;
140
+ let writefds = writefds. into ( ) ;
141
+ let errorfds = errorfds. into ( ) ;
142
+ let timeout = timeout. into ( ) ;
143
+
144
+ let nfds = nfds. into ( ) . unwrap_or_else ( || {
145
+ readfds. iter ( )
146
+ . chain ( writefds. iter ( ) )
147
+ . chain ( errorfds. iter ( ) )
148
+ . map ( |set| set. highest ( ) . unwrap_or ( -1 ) )
149
+ . max ( )
150
+ . unwrap_or ( -1 ) + 1
151
+ } ) ;
152
+
76
153
let readfds = readfds. map ( |set| set as * mut FdSet ) . unwrap_or ( null_mut ( ) ) ;
77
154
let writefds = writefds. map ( |set| set as * mut FdSet ) . unwrap_or ( null_mut ( ) ) ;
78
155
let errorfds = errorfds. map ( |set| set as * mut FdSet ) . unwrap_or ( null_mut ( ) ) ;
@@ -85,3 +162,141 @@ pub fn select(nfds: c_int,
85
162
86
163
Errno :: result ( res)
87
164
}
165
+
166
+ #[ cfg( test) ]
167
+ mod tests {
168
+ use super :: * ;
169
+ use sys:: time:: { TimeVal , TimeValLike } ;
170
+ use unistd:: { write, pipe} ;
171
+
172
+ #[ test]
173
+ fn fdset_insert ( ) {
174
+ let mut fd_set = FdSet :: new ( ) ;
175
+
176
+ for i in 0 ..FD_SETSIZE {
177
+ assert ! ( !fd_set. contains( i) ) ;
178
+ }
179
+
180
+ fd_set. insert ( 7 ) ;
181
+
182
+ assert ! ( fd_set. contains( 7 ) ) ;
183
+ }
184
+
185
+ #[ test]
186
+ fn fdset_remove ( ) {
187
+ let mut fd_set = FdSet :: new ( ) ;
188
+
189
+ for i in 0 ..FD_SETSIZE {
190
+ assert ! ( !fd_set. contains( i) ) ;
191
+ }
192
+
193
+ fd_set. insert ( 7 ) ;
194
+ fd_set. remove ( 7 ) ;
195
+
196
+ for i in 0 ..FD_SETSIZE {
197
+ assert ! ( !fd_set. contains( i) ) ;
198
+ }
199
+ }
200
+
201
+ #[ test]
202
+ fn fdset_clear ( ) {
203
+ let mut fd_set = FdSet :: new ( ) ;
204
+ fd_set. insert ( 1 ) ;
205
+ fd_set. insert ( FD_SETSIZE / 2 ) ;
206
+ fd_set. insert ( FD_SETSIZE - 1 ) ;
207
+
208
+ fd_set. clear ( ) ;
209
+
210
+ for i in 0 ..FD_SETSIZE {
211
+ assert ! ( !fd_set. contains( i) ) ;
212
+ }
213
+ }
214
+
215
+ #[ test]
216
+ fn fdset_highest ( ) {
217
+ let mut set = FdSet :: new ( ) ;
218
+ assert_eq ! ( set. highest( ) , None ) ;
219
+ set. insert ( 0 ) ;
220
+ assert_eq ! ( set. highest( ) , Some ( 0 ) ) ;
221
+ set. insert ( 90 ) ;
222
+ assert_eq ! ( set. highest( ) , Some ( 90 ) ) ;
223
+ set. remove ( 0 ) ;
224
+ assert_eq ! ( set. highest( ) , Some ( 90 ) ) ;
225
+ set. remove ( 90 ) ;
226
+ assert_eq ! ( set. highest( ) , None ) ;
227
+
228
+ set. insert ( 4 ) ;
229
+ set. insert ( 5 ) ;
230
+ set. insert ( 7 ) ;
231
+ assert_eq ! ( set. highest( ) , Some ( 7 ) ) ;
232
+ }
233
+
234
+ // powerpc-unknown-linux-gnu currently fails on the first `assert_eq` because
235
+ // `select()` returns a 0 instead of a 1. Since this test has only been run on
236
+ // qemu, it's unclear if this is a OS or qemu bug. Just disable it on that arch
237
+ // for now.
238
+ // FIXME: Fix tests for powerpc and mips
239
+ // FIXME: Add a link to an upstream qemu bug if there is one
240
+ #[ test]
241
+ #[ cfg_attr( any( target_arch = "powerpc" , target_arch = "mips" ) , ignore) ]
242
+ fn test_select ( ) {
243
+ let ( r1, w1) = pipe ( ) . unwrap ( ) ;
244
+ write ( w1, b"hi!" ) . unwrap ( ) ;
245
+ let ( r2, _w2) = pipe ( ) . unwrap ( ) ;
246
+
247
+ let mut fd_set = FdSet :: new ( ) ;
248
+ fd_set. insert ( r1) ;
249
+ fd_set. insert ( r2) ;
250
+
251
+ let mut timeout = TimeVal :: seconds ( 10 ) ;
252
+ assert_eq ! ( 1 , select( None ,
253
+ & mut fd_set,
254
+ None ,
255
+ None ,
256
+ & mut timeout) . unwrap( ) ) ;
257
+ assert ! ( fd_set. contains( r1) ) ;
258
+ assert ! ( !fd_set. contains( r2) ) ;
259
+ }
260
+
261
+ #[ test]
262
+ #[ cfg_attr( any( target_arch = "powerpc" , target_arch = "mips" ) , ignore) ]
263
+ fn test_select_nfds ( ) {
264
+ let ( r1, w1) = pipe ( ) . unwrap ( ) ;
265
+ write ( w1, b"hi!" ) . unwrap ( ) ;
266
+ let ( r2, _w2) = pipe ( ) . unwrap ( ) ;
267
+
268
+ let mut fd_set = FdSet :: new ( ) ;
269
+ fd_set. insert ( r1) ;
270
+ fd_set. insert ( r2) ;
271
+
272
+ let mut timeout = TimeVal :: seconds ( 10 ) ;
273
+ assert_eq ! ( 1 , select( Some ( fd_set. highest( ) . unwrap( ) + 1 ) ,
274
+ & mut fd_set,
275
+ None ,
276
+ None ,
277
+ & mut timeout) . unwrap( ) ) ;
278
+ assert ! ( fd_set. contains( r1) ) ;
279
+ assert ! ( !fd_set. contains( r2) ) ;
280
+ }
281
+
282
+ #[ test]
283
+ #[ cfg_attr( any( target_arch = "powerpc" , target_arch = "mips" ) , ignore) ]
284
+ fn test_select_nfds2 ( ) {
285
+ let ( r1, w1) = pipe ( ) . unwrap ( ) ;
286
+ write ( w1, b"hi!" ) . unwrap ( ) ;
287
+ let ( r2, _w2) = pipe ( ) . unwrap ( ) ;
288
+
289
+ let mut fd_set = FdSet :: new ( ) ;
290
+ fd_set. insert ( r1) ;
291
+ fd_set. insert ( r2) ;
292
+
293
+ let mut timeout = TimeVal :: seconds ( 10 ) ;
294
+ assert_eq ! ( 1 , select( :: std:: cmp:: max( r1, r2) + 1 ,
295
+ & mut fd_set,
296
+ None ,
297
+ None ,
298
+ & mut timeout) . unwrap( ) ) ;
299
+ assert ! ( fd_set. contains( r1) ) ;
300
+ assert ! ( !fd_set. contains( r2) ) ;
301
+ }
302
+ }
0 commit comments