summaryrefslogtreecommitdiffstats
path: root/third_party/rust/nix/src/sys/event.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/nix/src/sys/event.rs')
-rw-r--r--third_party/rust/nix/src/sys/event.rs374
1 files changed, 374 insertions, 0 deletions
diff --git a/third_party/rust/nix/src/sys/event.rs b/third_party/rust/nix/src/sys/event.rs
new file mode 100644
index 0000000000..d8ad628ea2
--- /dev/null
+++ b/third_party/rust/nix/src/sys/event.rs
@@ -0,0 +1,374 @@
+/* TOOD: Implement for other kqueue based systems
+ */
+
+use crate::{Errno, Result};
+#[cfg(not(target_os = "netbsd"))]
+use libc::{c_int, c_long, intptr_t, time_t, timespec, uintptr_t};
+#[cfg(target_os = "netbsd")]
+use libc::{c_long, intptr_t, size_t, time_t, timespec, uintptr_t};
+use std::convert::TryInto;
+use std::mem;
+use std::os::unix::io::RawFd;
+use std::ptr;
+
+// Redefine kevent in terms of programmer-friendly enums and bitfields.
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub struct KEvent {
+ kevent: libc::kevent,
+}
+
+#[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "openbsd"
+))]
+type type_of_udata = *mut libc::c_void;
+#[cfg(any(target_os = "netbsd"))]
+type type_of_udata = intptr_t;
+
+#[cfg(target_os = "netbsd")]
+type type_of_event_filter = u32;
+#[cfg(not(target_os = "netbsd"))]
+type type_of_event_filter = i16;
+libc_enum! {
+ #[cfg_attr(target_os = "netbsd", repr(u32))]
+ #[cfg_attr(not(target_os = "netbsd"), repr(i16))]
+ #[non_exhaustive]
+ pub enum EventFilter {
+ EVFILT_AIO,
+ /// Returns whenever there is no remaining data in the write buffer
+ #[cfg(target_os = "freebsd")]
+ EVFILT_EMPTY,
+ #[cfg(target_os = "dragonfly")]
+ EVFILT_EXCEPT,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos"))]
+ EVFILT_FS,
+ #[cfg(target_os = "freebsd")]
+ EVFILT_LIO,
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ EVFILT_MACHPORT,
+ EVFILT_PROC,
+ /// Returns events associated with the process referenced by a given
+ /// process descriptor, created by `pdfork()`. The events to monitor are:
+ ///
+ /// - NOTE_EXIT: the process has exited. The exit status will be stored in data.
+ #[cfg(target_os = "freebsd")]
+ EVFILT_PROCDESC,
+ EVFILT_READ,
+ /// Returns whenever an asynchronous `sendfile()` call completes.
+ #[cfg(target_os = "freebsd")]
+ EVFILT_SENDFILE,
+ EVFILT_SIGNAL,
+ EVFILT_TIMER,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos"))]
+ EVFILT_USER,
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ EVFILT_VM,
+ EVFILT_VNODE,
+ EVFILT_WRITE,
+ }
+ impl TryFrom<type_of_event_filter>
+}
+
+#[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "openbsd"
+))]
+pub type type_of_event_flag = u16;
+#[cfg(any(target_os = "netbsd"))]
+pub type type_of_event_flag = u32;
+libc_bitflags! {
+ pub struct EventFlag: type_of_event_flag {
+ EV_ADD;
+ EV_CLEAR;
+ EV_DELETE;
+ EV_DISABLE;
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd",
+ target_os = "ios", target_os = "macos",
+ target_os = "netbsd", target_os = "openbsd"))]
+ EV_DISPATCH;
+ #[cfg(target_os = "freebsd")]
+ EV_DROP;
+ EV_ENABLE;
+ EV_EOF;
+ EV_ERROR;
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ EV_FLAG0;
+ EV_FLAG1;
+ #[cfg(target_os = "dragonfly")]
+ EV_NODATA;
+ EV_ONESHOT;
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ EV_OOBAND;
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ EV_POLL;
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd",
+ target_os = "ios", target_os = "macos",
+ target_os = "netbsd", target_os = "openbsd"))]
+ EV_RECEIPT;
+ }
+}
+
+libc_bitflags!(
+ pub struct FilterFlag: u32 {
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ NOTE_ABSOLUTE;
+ NOTE_ATTRIB;
+ NOTE_CHILD;
+ NOTE_DELETE;
+ #[cfg(target_os = "openbsd")]
+ NOTE_EOF;
+ NOTE_EXEC;
+ NOTE_EXIT;
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ NOTE_EXITSTATUS;
+ NOTE_EXTEND;
+ #[cfg(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "freebsd",
+ target_os = "dragonfly"))]
+ NOTE_FFAND;
+ #[cfg(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "freebsd",
+ target_os = "dragonfly"))]
+ NOTE_FFCOPY;
+ #[cfg(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "freebsd",
+ target_os = "dragonfly"))]
+ NOTE_FFCTRLMASK;
+ #[cfg(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "freebsd",
+ target_os = "dragonfly"))]
+ NOTE_FFLAGSMASK;
+ #[cfg(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "freebsd",
+ target_os = "dragonfly"))]
+ NOTE_FFNOP;
+ #[cfg(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "freebsd",
+ target_os = "dragonfly"))]
+ NOTE_FFOR;
+ NOTE_FORK;
+ NOTE_LINK;
+ NOTE_LOWAT;
+ #[cfg(target_os = "freebsd")]
+ NOTE_MSECONDS;
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ NOTE_NONE;
+ #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))]
+ NOTE_NSECONDS;
+ #[cfg(target_os = "dragonfly")]
+ NOTE_OOB;
+ NOTE_PCTRLMASK;
+ NOTE_PDATAMASK;
+ NOTE_RENAME;
+ NOTE_REVOKE;
+ #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))]
+ NOTE_SECONDS;
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ NOTE_SIGNAL;
+ NOTE_TRACK;
+ NOTE_TRACKERR;
+ #[cfg(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "freebsd",
+ target_os = "dragonfly"))]
+ NOTE_TRIGGER;
+ #[cfg(target_os = "openbsd")]
+ NOTE_TRUNCATE;
+ #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))]
+ NOTE_USECONDS;
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ NOTE_VM_ERROR;
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ NOTE_VM_PRESSURE;
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ NOTE_VM_PRESSURE_SUDDEN_TERMINATE;
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ NOTE_VM_PRESSURE_TERMINATE;
+ NOTE_WRITE;
+ }
+);
+
+pub fn kqueue() -> Result<RawFd> {
+ let res = unsafe { libc::kqueue() };
+
+ Errno::result(res)
+}
+
+// KEvent can't derive Send because on some operating systems, udata is defined
+// as a void*. However, KEvent's public API always treats udata as an intptr_t,
+// which is safe to Send.
+unsafe impl Send for KEvent {}
+
+impl KEvent {
+ #[allow(clippy::needless_update)] // Not needless on all platforms.
+ pub fn new(
+ ident: uintptr_t,
+ filter: EventFilter,
+ flags: EventFlag,
+ fflags: FilterFlag,
+ data: intptr_t,
+ udata: intptr_t,
+ ) -> KEvent {
+ KEvent {
+ kevent: libc::kevent {
+ ident,
+ filter: filter as type_of_event_filter,
+ flags: flags.bits(),
+ fflags: fflags.bits(),
+ // data can be either i64 or intptr_t, depending on platform
+ data: data as _,
+ udata: udata as type_of_udata,
+ ..unsafe { mem::zeroed() }
+ },
+ }
+ }
+
+ pub fn ident(&self) -> uintptr_t {
+ self.kevent.ident
+ }
+
+ pub fn filter(&self) -> Result<EventFilter> {
+ self.kevent.filter.try_into()
+ }
+
+ pub fn flags(&self) -> EventFlag {
+ EventFlag::from_bits(self.kevent.flags).unwrap()
+ }
+
+ pub fn fflags(&self) -> FilterFlag {
+ FilterFlag::from_bits(self.kevent.fflags).unwrap()
+ }
+
+ pub fn data(&self) -> intptr_t {
+ self.kevent.data as intptr_t
+ }
+
+ pub fn udata(&self) -> intptr_t {
+ self.kevent.udata as intptr_t
+ }
+}
+
+pub fn kevent(
+ kq: RawFd,
+ changelist: &[KEvent],
+ eventlist: &mut [KEvent],
+ timeout_ms: usize,
+) -> Result<usize> {
+ // Convert ms to timespec
+ let timeout = timespec {
+ tv_sec: (timeout_ms / 1000) as time_t,
+ tv_nsec: ((timeout_ms % 1000) * 1_000_000) as c_long,
+ };
+
+ kevent_ts(kq, changelist, eventlist, Some(timeout))
+}
+
+#[cfg(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "openbsd"
+))]
+type type_of_nchanges = c_int;
+#[cfg(target_os = "netbsd")]
+type type_of_nchanges = size_t;
+
+pub fn kevent_ts(
+ kq: RawFd,
+ changelist: &[KEvent],
+ eventlist: &mut [KEvent],
+ timeout_opt: Option<timespec>,
+) -> Result<usize> {
+ let res = unsafe {
+ libc::kevent(
+ kq,
+ changelist.as_ptr() as *const libc::kevent,
+ changelist.len() as type_of_nchanges,
+ eventlist.as_mut_ptr() as *mut libc::kevent,
+ eventlist.len() as type_of_nchanges,
+ if let Some(ref timeout) = timeout_opt {
+ timeout as *const timespec
+ } else {
+ ptr::null()
+ },
+ )
+ };
+
+ Errno::result(res).map(|r| r as usize)
+}
+
+#[inline]
+pub fn ev_set(
+ ev: &mut KEvent,
+ ident: usize,
+ filter: EventFilter,
+ flags: EventFlag,
+ fflags: FilterFlag,
+ udata: intptr_t,
+) {
+ ev.kevent.ident = ident as uintptr_t;
+ ev.kevent.filter = filter as type_of_event_filter;
+ ev.kevent.flags = flags.bits();
+ ev.kevent.fflags = fflags.bits();
+ ev.kevent.data = 0;
+ ev.kevent.udata = udata as type_of_udata;
+}
+
+#[test]
+fn test_struct_kevent() {
+ use std::mem;
+
+ let udata: intptr_t = 12345;
+
+ let actual = KEvent::new(
+ 0xdead_beef,
+ EventFilter::EVFILT_READ,
+ EventFlag::EV_ONESHOT | EventFlag::EV_ADD,
+ FilterFlag::NOTE_CHILD | FilterFlag::NOTE_EXIT,
+ 0x1337,
+ udata,
+ );
+ assert_eq!(0xdead_beef, actual.ident());
+ let filter = actual.kevent.filter;
+ assert_eq!(libc::EVFILT_READ, filter);
+ assert_eq!(libc::EV_ONESHOT | libc::EV_ADD, actual.flags().bits());
+ assert_eq!(libc::NOTE_CHILD | libc::NOTE_EXIT, actual.fflags().bits());
+ assert_eq!(0x1337, actual.data());
+ assert_eq!(udata as type_of_udata, actual.udata() as type_of_udata);
+ assert_eq!(mem::size_of::<libc::kevent>(), mem::size_of::<KEvent>());
+}
+
+#[test]
+fn test_kevent_filter() {
+ let udata: intptr_t = 12345;
+
+ let actual = KEvent::new(
+ 0xdead_beef,
+ EventFilter::EVFILT_READ,
+ EventFlag::EV_ONESHOT | EventFlag::EV_ADD,
+ FilterFlag::NOTE_CHILD | FilterFlag::NOTE_EXIT,
+ 0x1337,
+ udata,
+ );
+ assert_eq!(EventFilter::EVFILT_READ, actual.filter().unwrap());
+}