1 use crate::{Interest, Token};
2 use log::error;
3 use std::mem::MaybeUninit;
4 use std::ops::{Deref, DerefMut};
5 use std::os::unix::io::{AsRawFd, RawFd};
6 #[cfg(debug_assertions)]
7 use std::sync::atomic::{AtomicUsize, Ordering};
8 use std::time::Duration;
9 use std::{cmp, io, ptr, slice};
10 
11 /// Unique id for use as `SelectorId`.
12 #[cfg(debug_assertions)]
13 static NEXT_ID: AtomicUsize = AtomicUsize::new(1);
14 
15 // Type of the `nchanges` and `nevents` parameters in the `kevent` function.
16 #[cfg(not(target_os = "netbsd"))]
17 type Count = libc::c_int;
18 #[cfg(target_os = "netbsd")]
19 type Count = libc::size_t;
20 
21 // Type of the `filter` field in the `kevent` structure.
22 #[cfg(any(target_os = "freebsd", target_os = "openbsd", target_os = "dragonfly"))]
23 type Filter = libc::c_short;
24 #[cfg(any(target_os = "macos", target_os = "ios"))]
25 type Filter = i16;
26 #[cfg(target_os = "netbsd")]
27 type Filter = u32;
28 
29 // Type of the `flags` field in the `kevent` structure.
30 #[cfg(any(target_os = "freebsd", target_os = "openbsd", target_os = "dragonfly"))]
31 type Flags = libc::c_ushort;
32 #[cfg(any(target_os = "macos", target_os = "ios"))]
33 type Flags = u16;
34 #[cfg(target_os = "netbsd")]
35 type Flags = u32;
36 
37 // Type of the `data` field in the `kevent` structure.
38 #[cfg(any(
39     target_os = "dragonfly",
40     target_os = "freebsd",
41     target_os = "ios",
42     target_os = "macos"
43 ))]
44 type Data = libc::intptr_t;
45 #[cfg(any(target_os = "netbsd", target_os = "openbsd"))]
46 type Data = i64;
47 
48 // Type of the `udata` field in the `kevent` structure.
49 #[cfg(not(target_os = "netbsd"))]
50 type UData = *mut libc::c_void;
51 #[cfg(target_os = "netbsd")]
52 type UData = libc::intptr_t;
53 
54 macro_rules! kevent {
55     ($id: expr, $filter: expr, $flags: expr, $data: expr) => {
56         libc::kevent {
57             ident: $id as libc::uintptr_t,
58             filter: $filter as Filter,
59             flags: $flags,
60             fflags: 0,
61             data: 0,
62             udata: $data as UData,
63         }
64     };
65 }
66 
67 #[derive(Debug)]
68 pub struct Selector {
69     #[cfg(debug_assertions)]
70     id: usize,
71     kq: RawFd,
72 }
73 
74 impl Selector {
new() -> io::Result<Selector>75     pub fn new() -> io::Result<Selector> {
76         syscall!(kqueue())
77             .and_then(|kq| syscall!(fcntl(kq, libc::F_SETFD, libc::FD_CLOEXEC)).map(|_| kq))
78             .map(|kq| Selector {
79                 #[cfg(debug_assertions)]
80                 id: NEXT_ID.fetch_add(1, Ordering::Relaxed),
81                 kq,
82             })
83     }
84 
try_clone(&self) -> io::Result<Selector>85     pub fn try_clone(&self) -> io::Result<Selector> {
86         syscall!(dup(self.kq)).map(|kq| Selector {
87             // It's the same selector, so we use the same id.
88             #[cfg(debug_assertions)]
89             id: self.id,
90             kq,
91         })
92     }
93 
select(&self, events: &mut Events, timeout: Option<Duration>) -> io::Result<()>94     pub fn select(&self, events: &mut Events, timeout: Option<Duration>) -> io::Result<()> {
95         let timeout = timeout.map(|to| libc::timespec {
96             tv_sec: cmp::min(to.as_secs(), libc::time_t::max_value() as u64) as libc::time_t,
97             // `Duration::subsec_nanos` is guaranteed to be less than one
98             // billion (the number of nanoseconds in a second), making the
99             // cast to i32 safe. The cast itself is needed for platforms
100             // where C's long is only 32 bits.
101             tv_nsec: libc::c_long::from(to.subsec_nanos() as i32),
102         });
103         let timeout = timeout
104             .as_ref()
105             .map(|s| s as *const _)
106             .unwrap_or(ptr::null_mut());
107 
108         events.clear();
109         syscall!(kevent(
110             self.kq,
111             ptr::null(),
112             0,
113             events.as_mut_ptr(),
114             events.capacity() as Count,
115             timeout,
116         ))
117         .map(|n_events| {
118             // This is safe because `kevent` ensures that `n_events` are
119             // assigned.
120             unsafe { events.set_len(n_events as usize) };
121         })
122     }
123 
register(&self, fd: RawFd, token: Token, interests: Interest) -> io::Result<()>124     pub fn register(&self, fd: RawFd, token: Token, interests: Interest) -> io::Result<()> {
125         let flags = libc::EV_CLEAR | libc::EV_RECEIPT | libc::EV_ADD;
126         // At most we need two changes, but maybe we only need 1.
127         let mut changes: [MaybeUninit<libc::kevent>; 2] =
128             [MaybeUninit::uninit(), MaybeUninit::uninit()];
129         let mut n_changes = 0;
130 
131         if interests.is_writable() {
132             let kevent = kevent!(fd, libc::EVFILT_WRITE, flags, token.0);
133             changes[n_changes] = MaybeUninit::new(kevent);
134             n_changes += 1;
135         }
136 
137         if interests.is_readable() {
138             let kevent = kevent!(fd, libc::EVFILT_READ, flags, token.0);
139             changes[n_changes] = MaybeUninit::new(kevent);
140             n_changes += 1;
141         }
142 
143         // Older versions of macOS (OS X 10.11 and 10.10 have been witnessed)
144         // can return EPIPE when registering a pipe file descriptor where the
145         // other end has already disappeared. For example code that creates a
146         // pipe, closes a file descriptor, and then registers the other end will
147         // see an EPIPE returned from `register`.
148         //
149         // It also turns out that kevent will still report events on the file
150         // descriptor, telling us that it's readable/hup at least after we've
151         // done this registration. As a result we just ignore `EPIPE` here
152         // instead of propagating it.
153         //
154         // More info can be found at tokio-rs/mio#582.
155         let changes = unsafe {
156             // This is safe because we ensure that at least `n_changes` are in
157             // the array.
158             slice::from_raw_parts_mut(changes[0].as_mut_ptr(), n_changes)
159         };
160         kevent_register(self.kq, changes, &[libc::EPIPE as Data])
161     }
162 
reregister(&self, fd: RawFd, token: Token, interests: Interest) -> io::Result<()>163     pub fn reregister(&self, fd: RawFd, token: Token, interests: Interest) -> io::Result<()> {
164         let flags = libc::EV_CLEAR | libc::EV_RECEIPT;
165         let write_flags = if interests.is_writable() {
166             flags | libc::EV_ADD
167         } else {
168             flags | libc::EV_DELETE
169         };
170         let read_flags = if interests.is_readable() {
171             flags | libc::EV_ADD
172         } else {
173             flags | libc::EV_DELETE
174         };
175 
176         let mut changes: [libc::kevent; 2] = [
177             kevent!(fd, libc::EVFILT_WRITE, write_flags, token.0),
178             kevent!(fd, libc::EVFILT_READ, read_flags, token.0),
179         ];
180 
181         // Since there is no way to check with which interests the fd was
182         // registered we modify both readable and write, adding it when required
183         // and removing it otherwise, ignoring the ENOENT error when it comes
184         // up. The ENOENT error informs us that a filter we're trying to remove
185         // wasn't there in first place, but we don't really care since our goal
186         // is accomplished.
187         //
188         // For the explanation of ignoring `EPIPE` see `register`.
189         kevent_register(
190             self.kq,
191             &mut changes,
192             &[libc::ENOENT as Data, libc::EPIPE as Data],
193         )
194     }
195 
deregister(&self, fd: RawFd) -> io::Result<()>196     pub fn deregister(&self, fd: RawFd) -> io::Result<()> {
197         let flags = libc::EV_DELETE | libc::EV_RECEIPT;
198         let mut changes: [libc::kevent; 2] = [
199             kevent!(fd, libc::EVFILT_WRITE, flags, 0),
200             kevent!(fd, libc::EVFILT_READ, flags, 0),
201         ];
202 
203         // Since there is no way to check with which interests the fd was
204         // registered we remove both filters (readable and writeable) and ignore
205         // the ENOENT error when it comes up. The ENOENT error informs us that
206         // the filter wasn't there in first place, but we don't really care
207         // about that since our goal is to remove it.
208         kevent_register(self.kq, &mut changes, &[libc::ENOENT as Data])
209     }
210 
211     // Used by `Waker`.
212     #[cfg(any(target_os = "freebsd", target_os = "ios", target_os = "macos", target_os = "dragonfly"))]
setup_waker(&self, token: Token) -> io::Result<()>213     pub fn setup_waker(&self, token: Token) -> io::Result<()> {
214         // First attempt to accept user space notifications.
215         let mut kevent = kevent!(
216             0,
217             libc::EVFILT_USER,
218             libc::EV_ADD | libc::EV_CLEAR | libc::EV_RECEIPT,
219             token.0
220         );
221 
222         syscall!(kevent(self.kq, &kevent, 1, &mut kevent, 1, ptr::null())).and_then(|_| {
223             if (kevent.flags & libc::EV_ERROR) != 0 && kevent.data != 0 {
224                 Err(io::Error::from_raw_os_error(kevent.data as i32))
225             } else {
226                 Ok(())
227             }
228         })
229     }
230 
231     // Used by `Waker`.
232     #[cfg(any(target_os = "freebsd", target_os = "ios", target_os = "macos", target_os = "dragonfly"))]
wake(&self, token: Token) -> io::Result<()>233     pub fn wake(&self, token: Token) -> io::Result<()> {
234         let mut kevent = kevent!(
235             0,
236             libc::EVFILT_USER,
237             libc::EV_ADD | libc::EV_RECEIPT,
238             token.0
239         );
240         kevent.fflags = libc::NOTE_TRIGGER;
241 
242         syscall!(kevent(self.kq, &kevent, 1, &mut kevent, 1, ptr::null())).and_then(|_| {
243             if (kevent.flags & libc::EV_ERROR) != 0 && kevent.data != 0 {
244                 Err(io::Error::from_raw_os_error(kevent.data as i32))
245             } else {
246                 Ok(())
247             }
248         })
249     }
250 }
251 
252 /// Register `changes` with `kq`ueue.
kevent_register( kq: RawFd, changes: &mut [libc::kevent], ignored_errors: &[Data], ) -> io::Result<()>253 fn kevent_register(
254     kq: RawFd,
255     changes: &mut [libc::kevent],
256     ignored_errors: &[Data],
257 ) -> io::Result<()> {
258     syscall!(kevent(
259         kq,
260         changes.as_ptr(),
261         changes.len() as Count,
262         changes.as_mut_ptr(),
263         changes.len() as Count,
264         ptr::null(),
265     ))
266     .map(|_| ())
267     .or_else(|err| {
268         // According to the manual page of FreeBSD: "When kevent() call fails
269         // with EINTR error, all changes in the changelist have been applied",
270         // so we can safely ignore it.
271         if err.raw_os_error() == Some(libc::EINTR) {
272             Ok(())
273         } else {
274             Err(err)
275         }
276     })
277     .and_then(|()| check_errors(&changes, ignored_errors))
278 }
279 
280 /// Check all events for possible errors, it returns the first error found.
check_errors(events: &[libc::kevent], ignored_errors: &[Data]) -> io::Result<()>281 fn check_errors(events: &[libc::kevent], ignored_errors: &[Data]) -> io::Result<()> {
282     for event in events {
283         // We can't use references to packed structures (in checking the ignored
284         // errors), so we need copy the data out before use.
285         let data = event.data;
286         // Check for the error flag, the actual error will be in the `data`
287         // field.
288         if (event.flags & libc::EV_ERROR != 0) && data != 0 && !ignored_errors.contains(&data) {
289             return Err(io::Error::from_raw_os_error(data as i32));
290         }
291     }
292     Ok(())
293 }
294 
295 cfg_net! {
296     #[cfg(debug_assertions)]
297     impl Selector {
298         pub fn id(&self) -> usize {
299             self.id
300         }
301     }
302 }
303 
304 impl AsRawFd for Selector {
as_raw_fd(&self) -> RawFd305     fn as_raw_fd(&self) -> RawFd {
306         self.kq
307     }
308 }
309 
310 impl Drop for Selector {
drop(&mut self)311     fn drop(&mut self) {
312         if let Err(err) = syscall!(close(self.kq)) {
313             error!("error closing kqueue: {}", err);
314         }
315     }
316 }
317 
318 pub type Event = libc::kevent;
319 pub struct Events(Vec<libc::kevent>);
320 
321 impl Events {
with_capacity(capacity: usize) -> Events322     pub fn with_capacity(capacity: usize) -> Events {
323         Events(Vec::with_capacity(capacity))
324     }
325 }
326 
327 impl Deref for Events {
328     type Target = Vec<libc::kevent>;
329 
deref(&self) -> &Self::Target330     fn deref(&self) -> &Self::Target {
331         &self.0
332     }
333 }
334 
335 impl DerefMut for Events {
deref_mut(&mut self) -> &mut Self::Target336     fn deref_mut(&mut self) -> &mut Self::Target {
337         &mut self.0
338     }
339 }
340 
341 // `Events` cannot derive `Send` or `Sync` because of the
342 // `udata: *mut ::c_void` field in `libc::kevent`. However, `Events`'s public
343 // API treats the `udata` field as a `uintptr_t` which is `Send`. `Sync` is
344 // safe because with a `events: &Events` value, the only access to the `udata`
345 // field is through `fn token(event: &Event)` which cannot mutate the field.
346 unsafe impl Send for Events {}
347 unsafe impl Sync for Events {}
348 
349 pub mod event {
350     use std::fmt;
351 
352     use crate::sys::Event;
353     use crate::Token;
354 
355     use super::{Filter, Flags};
356 
token(event: &Event) -> Token357     pub fn token(event: &Event) -> Token {
358         Token(event.udata as usize)
359     }
360 
is_readable(event: &Event) -> bool361     pub fn is_readable(event: &Event) -> bool {
362         event.filter == libc::EVFILT_READ || {
363             #[cfg(any(target_os = "freebsd", target_os = "ios", target_os = "macos", target_os = "dragonfly"))]
364             // Used by the `Awakener`. On platforms that use `eventfd` or a unix
365             // pipe it will emit a readable event so we'll fake that here as
366             // well.
367             {
368                 event.filter == libc::EVFILT_USER
369             }
370             #[cfg(not(any(target_os = "freebsd", target_os = "ios", target_os = "macos", target_os = "dragonfly")))]
371             {
372                 false
373             }
374         }
375     }
376 
is_writable(event: &Event) -> bool377     pub fn is_writable(event: &Event) -> bool {
378         event.filter == libc::EVFILT_WRITE
379     }
380 
is_error(event: &Event) -> bool381     pub fn is_error(event: &Event) -> bool {
382         (event.flags & libc::EV_ERROR) != 0 ||
383             // When the read end of the socket is closed, EV_EOF is set on
384             // flags, and fflags contains the error if there is one.
385             (event.flags & libc::EV_EOF) != 0 && event.fflags != 0
386     }
387 
is_read_closed(event: &Event) -> bool388     pub fn is_read_closed(event: &Event) -> bool {
389         event.filter == libc::EVFILT_READ && event.flags & libc::EV_EOF != 0
390     }
391 
is_write_closed(event: &Event) -> bool392     pub fn is_write_closed(event: &Event) -> bool {
393         event.filter == libc::EVFILT_WRITE && event.flags & libc::EV_EOF != 0
394     }
395 
is_priority(_: &Event) -> bool396     pub fn is_priority(_: &Event) -> bool {
397         // kqueue doesn't have priority indicators.
398         false
399     }
400 
401     #[allow(unused_variables)] // `event` is not used on some platforms.
is_aio(event: &Event) -> bool402     pub fn is_aio(event: &Event) -> bool {
403         #[cfg(any(
404             target_os = "dragonfly",
405             target_os = "freebsd",
406             target_os = "ios",
407             target_os = "macos"
408         ))]
409         {
410             event.filter == libc::EVFILT_AIO
411         }
412         #[cfg(not(any(
413             target_os = "dragonfly",
414             target_os = "freebsd",
415             target_os = "ios",
416             target_os = "macos"
417         )))]
418         {
419             false
420         }
421     }
422 
423     #[allow(unused_variables)] // `event` is only used on FreeBSD.
is_lio(event: &Event) -> bool424     pub fn is_lio(event: &Event) -> bool {
425         #[cfg(target_os = "freebsd")]
426         {
427             event.filter == libc::EVFILT_LIO
428         }
429         #[cfg(not(target_os = "freebsd"))]
430         {
431             false
432         }
433     }
434 
debug_details(f: &mut fmt::Formatter<'_>, event: &Event) -> fmt::Result435     pub fn debug_details(f: &mut fmt::Formatter<'_>, event: &Event) -> fmt::Result {
436         debug_detail!(
437             FilterDetails(Filter),
438             PartialEq::eq,
439             libc::EVFILT_READ,
440             libc::EVFILT_WRITE,
441             libc::EVFILT_AIO,
442             libc::EVFILT_VNODE,
443             libc::EVFILT_PROC,
444             libc::EVFILT_SIGNAL,
445             libc::EVFILT_TIMER,
446             #[cfg(target_os = "freebsd")]
447             libc::EVFILT_PROCDESC,
448             #[cfg(any(
449                 target_os = "freebsd",
450                 target_os = "dragonfly",
451                 target_os = "ios",
452                 target_os = "macos"
453             ))]
454             libc::EVFILT_FS,
455             #[cfg(target_os = "freebsd")]
456             libc::EVFILT_LIO,
457             #[cfg(any(
458                 target_os = "freebsd",
459                 target_os = "dragonfly",
460                 target_os = "ios",
461                 target_os = "macos"
462             ))]
463             libc::EVFILT_USER,
464             #[cfg(target_os = "freebsd")]
465             libc::EVFILT_SENDFILE,
466             #[cfg(target_os = "freebsd")]
467             libc::EVFILT_EMPTY,
468             #[cfg(target_os = "dragonfly")]
469             libc::EVFILT_EXCEPT,
470             #[cfg(any(target_os = "ios", target_os = "macos"))]
471             libc::EVFILT_MACHPORT,
472             #[cfg(any(target_os = "ios", target_os = "macos"))]
473             libc::EVFILT_VM,
474         );
475 
476         #[allow(clippy::trivially_copy_pass_by_ref)]
477         fn check_flag(got: &Flags, want: &Flags) -> bool {
478             (got & want) != 0
479         }
480         debug_detail!(
481             FlagsDetails(Flags),
482             check_flag,
483             libc::EV_ADD,
484             libc::EV_DELETE,
485             libc::EV_ENABLE,
486             libc::EV_DISABLE,
487             libc::EV_ONESHOT,
488             libc::EV_CLEAR,
489             libc::EV_RECEIPT,
490             libc::EV_DISPATCH,
491             #[cfg(target_os = "freebsd")]
492             libc::EV_DROP,
493             libc::EV_FLAG1,
494             libc::EV_ERROR,
495             libc::EV_EOF,
496             libc::EV_SYSFLAGS,
497             #[cfg(any(target_os = "ios", target_os = "macos"))]
498             libc::EV_FLAG0,
499             #[cfg(any(target_os = "ios", target_os = "macos"))]
500             libc::EV_POLL,
501             #[cfg(any(target_os = "ios", target_os = "macos"))]
502             libc::EV_OOBAND,
503             #[cfg(target_os = "dragonfly")]
504             libc::EV_NODATA,
505         );
506 
507         #[allow(clippy::trivially_copy_pass_by_ref)]
508         fn check_fflag(got: &u32, want: &u32) -> bool {
509             (got & want) != 0
510         }
511         debug_detail!(
512             FflagsDetails(u32),
513             check_fflag,
514             #[cfg(any(
515                 target_os = "dragonfly",
516                 target_os = "freebsd",
517                 target_os = "ios",
518                 target_os = "macos"
519             ))]
520             libc::NOTE_TRIGGER,
521             #[cfg(any(
522                 target_os = "dragonfly",
523                 target_os = "freebsd",
524                 target_os = "ios",
525                 target_os = "macos"
526             ))]
527             libc::NOTE_FFNOP,
528             #[cfg(any(
529                 target_os = "dragonfly",
530                 target_os = "freebsd",
531                 target_os = "ios",
532                 target_os = "macos"
533             ))]
534             libc::NOTE_FFAND,
535             #[cfg(any(
536                 target_os = "dragonfly",
537                 target_os = "freebsd",
538                 target_os = "ios",
539                 target_os = "macos"
540             ))]
541             libc::NOTE_FFOR,
542             #[cfg(any(
543                 target_os = "dragonfly",
544                 target_os = "freebsd",
545                 target_os = "ios",
546                 target_os = "macos"
547             ))]
548             libc::NOTE_FFCOPY,
549             #[cfg(any(
550                 target_os = "dragonfly",
551                 target_os = "freebsd",
552                 target_os = "ios",
553                 target_os = "macos"
554             ))]
555             libc::NOTE_FFCTRLMASK,
556             #[cfg(any(
557                 target_os = "dragonfly",
558                 target_os = "freebsd",
559                 target_os = "ios",
560                 target_os = "macos"
561             ))]
562             libc::NOTE_FFLAGSMASK,
563             libc::NOTE_LOWAT,
564             libc::NOTE_DELETE,
565             libc::NOTE_WRITE,
566             #[cfg(target_os = "dragonfly")]
567             libc::NOTE_OOB,
568             #[cfg(target_os = "openbsd")]
569             libc::NOTE_EOF,
570             #[cfg(any(target_os = "ios", target_os = "macos"))]
571             libc::NOTE_EXTEND,
572             libc::NOTE_ATTRIB,
573             libc::NOTE_LINK,
574             libc::NOTE_RENAME,
575             libc::NOTE_REVOKE,
576             #[cfg(any(target_os = "ios", target_os = "macos"))]
577             libc::NOTE_NONE,
578             #[cfg(any(target_os = "openbsd"))]
579             libc::NOTE_TRUNCATE,
580             libc::NOTE_EXIT,
581             libc::NOTE_FORK,
582             libc::NOTE_EXEC,
583             #[cfg(any(target_os = "ios", target_os = "macos"))]
584             libc::NOTE_SIGNAL,
585             #[cfg(any(target_os = "ios", target_os = "macos"))]
586             libc::NOTE_EXITSTATUS,
587             #[cfg(any(target_os = "ios", target_os = "macos"))]
588             libc::NOTE_EXIT_DETAIL,
589             libc::NOTE_PDATAMASK,
590             libc::NOTE_PCTRLMASK,
591             #[cfg(any(
592                 target_os = "dragonfly",
593                 target_os = "freebsd",
594                 target_os = "netbsd",
595                 target_os = "openbsd"
596             ))]
597             libc::NOTE_TRACK,
598             #[cfg(any(
599                 target_os = "dragonfly",
600                 target_os = "freebsd",
601                 target_os = "netbsd",
602                 target_os = "openbsd"
603             ))]
604             libc::NOTE_TRACKERR,
605             #[cfg(any(
606                 target_os = "dragonfly",
607                 target_os = "freebsd",
608                 target_os = "netbsd",
609                 target_os = "openbsd"
610             ))]
611             libc::NOTE_CHILD,
612             #[cfg(any(target_os = "ios", target_os = "macos"))]
613             libc::NOTE_EXIT_DETAIL_MASK,
614             #[cfg(any(target_os = "ios", target_os = "macos"))]
615             libc::NOTE_EXIT_DECRYPTFAIL,
616             #[cfg(any(target_os = "ios", target_os = "macos"))]
617             libc::NOTE_EXIT_MEMORY,
618             #[cfg(any(target_os = "ios", target_os = "macos"))]
619             libc::NOTE_EXIT_CSERROR,
620             #[cfg(any(target_os = "ios", target_os = "macos"))]
621             libc::NOTE_VM_PRESSURE,
622             #[cfg(any(target_os = "ios", target_os = "macos"))]
623             libc::NOTE_VM_PRESSURE_TERMINATE,
624             #[cfg(any(target_os = "ios", target_os = "macos"))]
625             libc::NOTE_VM_PRESSURE_SUDDEN_TERMINATE,
626             #[cfg(any(target_os = "ios", target_os = "macos"))]
627             libc::NOTE_VM_ERROR,
628             #[cfg(any(target_os = "freebsd", target_os = "ios", target_os = "macos"))]
629             libc::NOTE_SECONDS,
630             #[cfg(any(target_os = "freebsd"))]
631             libc::NOTE_MSECONDS,
632             #[cfg(any(target_os = "freebsd", target_os = "ios", target_os = "macos"))]
633             libc::NOTE_USECONDS,
634             #[cfg(any(target_os = "freebsd", target_os = "ios", target_os = "macos"))]
635             libc::NOTE_NSECONDS,
636             #[cfg(any(target_os = "ios", target_os = "macos"))]
637             #[cfg(any(target_os = "freebsd", target_os = "ios", target_os = "macos"))]
638             libc::NOTE_ABSOLUTE,
639             #[cfg(any(target_os = "ios", target_os = "macos"))]
640             libc::NOTE_LEEWAY,
641             #[cfg(any(target_os = "ios", target_os = "macos"))]
642             libc::NOTE_CRITICAL,
643         );
644 
645         // Can't reference fields in packed structures.
646         let ident = event.ident;
647         let data = event.data;
648         let udata = event.udata;
649         f.debug_struct("kevent")
650             .field("ident", &ident)
651             .field("filter", &FilterDetails(event.filter))
652             .field("flags", &FlagsDetails(event.flags))
653             .field("fflags", &FflagsDetails(event.fflags))
654             .field("data", &data)
655             .field("udata", &udata)
656             .finish()
657     }
658 }
659 
660 #[test]
does_not_register_rw()661 fn does_not_register_rw() {
662     use crate::unix::SourceFd;
663     use crate::{Poll, Token};
664 
665     let kq = unsafe { libc::kqueue() };
666     let mut kqf = SourceFd(&kq);
667     let poll = Poll::new().unwrap();
668 
669     // Registering kqueue fd will fail if write is requested (On anything but
670     // some versions of macOS).
671     poll.registry()
672         .register(&mut kqf, Token(1234), Interest::READABLE)
673         .unwrap();
674 }
675