use crate::sys::{mode_to_flags, FilterFlags};
use crate::{PollMode, Poller};
use std::convert::TryInto;
use std::process::Child;
use std::time::Duration;
use std::{io, mem};
use super::__private::PollerSealed;
use __private::FilterSealed;
pub trait PollerKqueueExt<F: Filter>: PollerSealed {
fn add_filter(&self, filter: F, key: usize, mode: PollMode) -> io::Result<()>;
fn modify_filter(&self, filter: F, key: usize, mode: PollMode) -> io::Result<()>;
fn delete_filter(&self, filter: F) -> io::Result<()>;
}
impl<F: Filter> PollerKqueueExt<F> for Poller {
#[inline(always)]
fn add_filter(&self, filter: F, key: usize, mode: PollMode) -> io::Result<()> {
self.modify_filter(filter, key, mode)
}
fn modify_filter(&self, filter: F, key: usize, mode: PollMode) -> io::Result<()> {
let event = filter.filter(libc::EV_ADD | mode_to_flags(mode), key);
self.poller.submit_changes([event])
}
fn delete_filter(&self, filter: F) -> io::Result<()> {
let event = filter.filter(libc::EV_DELETE, 0);
self.poller.submit_changes([event])
}
}
pub trait Filter: FilterSealed {}
unsafe impl<T: FilterSealed + ?Sized> FilterSealed for &T {
#[inline(always)]
fn filter(&self, flags: FilterFlags, key: usize) -> libc::kevent {
(**self).filter(flags, key)
}
}
impl<T: Filter + ?Sized> Filter for &T {}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Signal(pub c_int);
#[allow(non_camel_case_types)]
pub type c_int = i32;
unsafe impl FilterSealed for Signal {
#[inline(always)]
fn filter(&self, flags: FilterFlags, key: usize) -> libc::kevent {
libc::kevent {
ident: self.0 as _,
filter: libc::EVFILT_SIGNAL,
flags: flags | libc::EV_RECEIPT,
udata: key as _,
..unsafe { mem::zeroed() }
}
}
}
impl Filter for Signal {}
#[derive(Debug)]
pub struct Process<'a> {
child: &'a Child,
ops: ProcessOps,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[non_exhaustive]
pub enum ProcessOps {
Exit,
Fork,
Exec,
}
impl<'a> Process<'a> {
pub fn new(child: &'a Child, ops: ProcessOps) -> Self {
Self { child, ops }
}
}
unsafe impl FilterSealed for Process<'_> {
#[inline(always)]
fn filter(&self, flags: FilterFlags, key: usize) -> libc::kevent {
let fflags = match self.ops {
ProcessOps::Exit => libc::NOTE_EXIT,
ProcessOps::Fork => libc::NOTE_FORK,
ProcessOps::Exec => libc::NOTE_EXEC,
};
libc::kevent {
ident: self.child.id() as _,
filter: libc::EVFILT_PROC,
flags: flags | libc::EV_RECEIPT,
fflags,
udata: key as _,
..unsafe { mem::zeroed() }
}
}
}
impl Filter for Process<'_> {}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Timer {
pub id: usize,
pub timeout: Duration,
}
unsafe impl FilterSealed for Timer {
fn filter(&self, flags: FilterFlags, key: usize) -> libc::kevent {
let (fflags, data) = {
#[cfg(not(any(target_os = "dragonfly", target_os = "netbsd", target_os = "openbsd")))]
{
let subsec_nanos = self.timeout.subsec_nanos();
match (subsec_nanos % 1_000, subsec_nanos % 1_000_000, subsec_nanos) {
(_, _, 0) => (
libc::NOTE_SECONDS,
self.timeout.as_secs().try_into().expect("too many seconds"),
),
(_, 0, _) => (
0,
self.timeout
.as_millis()
.try_into()
.expect("too many milliseconds"),
),
(0, _, _) => (
libc::NOTE_USECONDS,
self.timeout
.as_micros()
.try_into()
.expect("too many microseconds"),
),
(_, _, _) => (
libc::NOTE_NSECONDS,
self.timeout
.as_nanos()
.try_into()
.expect("too many nanoseconds"),
),
}
}
#[cfg(any(target_os = "dragonfly", target_os = "netbsd", target_os = "openbsd"))]
{
(
0,
self.timeout
.as_millis()
.try_into()
.expect("too many milliseconds"),
)
}
};
#[allow(clippy::needless_update)]
libc::kevent {
ident: self.id as _,
filter: libc::EVFILT_TIMER,
flags: flags | libc::EV_RECEIPT,
fflags,
data,
udata: key as _,
..unsafe { mem::zeroed() }
}
}
}
impl Filter for Timer {}
mod __private {
use crate::sys::FilterFlags;
#[doc(hidden)]
pub unsafe trait FilterSealed {
fn filter(&self, flags: FilterFlags, key: usize) -> libc::kevent;
}
}