use crate::buffer::Buffer;
use crate::fd::{AsFd, OwnedFd, RawFd};
use crate::pid::Pid;
use crate::signal::Signal;
use crate::timespec::Timespec;
use crate::{backend, io};
use backend::c::{self, intptr_t, kevent as kevent_t, uintptr_t};
use backend::event::syscalls;
use core::mem::zeroed;
use core::time::Duration;
#[repr(transparent)]
#[derive(Copy, Clone)]
pub struct Event {
inner: kevent_t,
}
impl Event {
#[allow(clippy::needless_update)]
pub fn new(filter: EventFilter, flags: EventFlags, udata: *mut c::c_void) -> Event {
let (ident, data, filter, fflags) = match filter {
EventFilter::Read(fd) => (fd as uintptr_t, 0, c::EVFILT_READ, 0),
EventFilter::Write(fd) => (fd as _, 0, c::EVFILT_WRITE, 0),
#[cfg(target_os = "freebsd")]
EventFilter::Empty(fd) => (fd as _, 0, c::EVFILT_EMPTY, 0),
EventFilter::Vnode { vnode, flags } => (vnode as _, 0, c::EVFILT_VNODE, flags.bits()),
EventFilter::Proc { pid, flags } => {
(Pid::as_raw(Some(pid)) as _, 0, c::EVFILT_PROC, flags.bits())
}
EventFilter::Signal { signal, times: _ } => {
(signal.as_raw() as _, 0, c::EVFILT_SIGNAL, 0)
}
EventFilter::Timer { ident, timer } => {
#[cfg(any(apple, target_os = "freebsd", target_os = "netbsd"))]
let (data, fflags) = match timer {
Some(timer) => {
if timer.subsec_millis() == 0 {
(timer.as_secs() as _, c::NOTE_SECONDS)
} else if timer.subsec_nanos() == 0 {
(timer.as_micros() as _, c::NOTE_USECONDS)
} else {
(timer.as_nanos() as _, c::NOTE_NSECONDS)
}
}
None => (intptr_t::MAX, c::NOTE_SECONDS),
};
#[cfg(any(target_os = "dragonfly", target_os = "openbsd"))]
let (data, fflags) = match timer {
Some(timer) => (timer.as_millis() as _, 0),
None => (intptr_t::MAX, 0),
};
(ident as _, data, c::EVFILT_TIMER, fflags)
}
#[cfg(any(apple, freebsdlike))]
EventFilter::User {
ident,
flags,
user_flags,
} => (ident as _, 0, c::EVFILT_USER, flags.bits() | user_flags.0),
EventFilter::Unknown => panic!("unknown filter"),
};
Event {
inner: kevent_t {
ident,
filter: filter as _,
flags: flags.bits() as _,
fflags,
data: {
data as _
},
udata: {
udata as _
},
..unsafe { zeroed() }
},
}
}
pub fn flags(&self) -> EventFlags {
EventFlags::from_bits_retain(self.inner.flags as _)
}
pub fn udata(&self) -> *mut c::c_void {
self.inner.udata as _
}
pub fn data(&self) -> i64 {
self.inner.data as _
}
pub fn filter(&self) -> EventFilter {
match self.inner.filter as _ {
c::EVFILT_READ => EventFilter::Read(self.inner.ident as _),
c::EVFILT_WRITE => EventFilter::Write(self.inner.ident as _),
#[cfg(target_os = "freebsd")]
c::EVFILT_EMPTY => EventFilter::Empty(self.inner.ident as _),
c::EVFILT_VNODE => EventFilter::Vnode {
vnode: self.inner.ident as _,
flags: VnodeEvents::from_bits_retain(self.inner.fflags),
},
c::EVFILT_PROC => EventFilter::Proc {
pid: Pid::from_raw(self.inner.ident as _).unwrap(),
flags: ProcessEvents::from_bits_retain(self.inner.fflags),
},
c::EVFILT_SIGNAL => EventFilter::Signal {
signal: unsafe { Signal::from_raw_unchecked(self.inner.ident as _) },
times: self.inner.data as _,
},
c::EVFILT_TIMER => EventFilter::Timer {
ident: self.inner.ident as _,
timer: {
let (data, fflags) = (self.inner.data, self.inner.fflags);
#[cfg(not(any(apple, target_os = "freebsd", target_os = "netbsd")))]
let _ = fflags;
#[cfg(any(apple, target_os = "freebsd", target_os = "netbsd"))]
match fflags as _ {
c::NOTE_SECONDS => Some(Duration::from_secs(data as _)),
c::NOTE_USECONDS => Some(Duration::from_micros(data as _)),
c::NOTE_NSECONDS => Some(Duration::from_nanos(data as _)),
_ => {
None
}
}
#[cfg(any(target_os = "dragonfly", target_os = "openbsd"))]
Some(Duration::from_millis(data as _))
},
},
#[cfg(any(apple, freebsdlike))]
c::EVFILT_USER => EventFilter::User {
ident: self.inner.ident as _,
flags: UserFlags::from_bits_retain(self.inner.fflags),
user_flags: UserDefinedFlags(self.inner.fflags & EVFILT_USER_FLAGS),
},
_ => EventFilter::Unknown,
}
}
}
#[cfg(any(apple, freebsdlike))]
const EVFILT_USER_FLAGS: u32 = 0x00ff_ffff;
#[repr(i16)]
#[non_exhaustive]
pub enum EventFilter {
Read(RawFd),
Write(RawFd),
#[cfg(target_os = "freebsd")]
Empty(RawFd),
Vnode {
vnode: RawFd,
flags: VnodeEvents,
},
Proc {
pid: Pid,
flags: ProcessEvents,
},
Signal {
signal: Signal,
times: usize,
},
Timer {
ident: intptr_t,
timer: Option<Duration>,
},
#[cfg(any(apple, freebsdlike))]
User {
ident: intptr_t,
flags: UserFlags,
user_flags: UserDefinedFlags,
},
Unknown,
}
bitflags::bitflags! {
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct EventFlags: u16 {
const ADD = c::EV_ADD as _;
const ENABLE = c::EV_ENABLE as _;
const DISABLE = c::EV_DISABLE as _;
const DELETE = c::EV_DELETE as _;
const RECEIPT = c::EV_RECEIPT as _;
const class="syntax-meta syntax-path syntax-rust">c::EV_ONESHOT as _;
const CLEAR = c::EV_CLEAR as _;
const EOF = c::EV_EOF as _;
const ERROR = c::EV_ERROR as _;
const _ = !0;
}
}
bitflags::bitflags! {
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct VnodeEvents: u32 {
const DELETE = c::NOTE_DELETE;
const WRITE = c::NOTE_WRITE;
const EXTEND = c::NOTE_EXTEND;
const ATTRIBUTES = c::NOTE_ATTRIB;
const RENAME = c::NOTE_RENAME;
const REVOKE = c::NOTE_REVOKE;
const LINK = c::NOTE_LINK;
const _ = !0;
}
}
bitflags::bitflags! {
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct ProcessEvents: u32 {
const EXIT = c::NOTE_EXIT;
const FORK = c::NOTE_FORK;
const EXEC = c::NOTE_EXEC;
const TRACK = c::NOTE_TRACK;
const TRACKERR = c::NOTE_TRACKERR;
const _ = !0;
}
}
#[cfg(any(apple, freebsdlike))]
bitflags::bitflags! {
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct UserFlags: u32 {
#[doc(alias = "NOP")]
const NOINPUT = c::NOTE_FFNOP;
const AND = c::NOTE_FFAND;
const OR = c::NOTE_FFOR;
const COPY = c::NOTE_FFCOPY;
const CTRLMASK = c::NOTE_FFCTRLMASK;
const UDFMASK = c::NOTE_FFLAGSMASK;
const TRIGGER = c::NOTE_TRIGGER;
const _ = !0;
}
}
#[repr(transparent)]
#[cfg(any(apple, freebsdlike))]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct UserDefinedFlags(u32);
#[cfg(any(apple, freebsdlike))]
impl UserDefinedFlags {
pub fn new(flags: u32) -> Self {
Self(flags & EVFILT_USER_FLAGS)
}
pub fn get(self) -> u32 {
self.0
}
}
pub fn kqueue() -> io::Result<OwnedFd> {
syscalls::kqueue()
}
pub unsafe fn kevent_timespec<Fd: AsFd, Buf: Buffer<Event>>(
kqueue: Fd,
changelist: &[Event],
mut eventlist: Buf,
timeout: Option<&Timespec>,
) -> io::Result<Buf::Output> {
let len = syscalls::kevent(kqueue.as_fd(), changelist, eventlist.parts_mut(), timeout)
.map(|res| res as _)?;
Ok(eventlist.assume_init(len))
}
pub unsafe fn kevent<Fd: AsFd, Buf: Buffer<Event>>(
kqueue: Fd,
changelist: &[Event],
eventlist: Buf,
timeout: Option<Duration>,
) -> io::Result<Buf::Output> {
let timeout = match timeout {
Some(timeout) => match timeout.as_secs().try_into() {
Ok(tv_sec) => Some(Timespec {
tv_sec,
tv_nsec: timeout.subsec_nanos() as _,
}),
Err(_) => None,
},
None => None,
};
kevent_timespec(kqueue, changelist, eventlist, timeout.as_ref())
}