use crate::cvt;
use std::{
io,
os::fd::{AsRawFd, BorrowedFd, FromRawFd, OwnedFd},
ptr,
time::Duration,
};
#[derive(Debug)]
pub struct ExitWatch {
pid: libc::pid_t,
filter: i16,
}
impl ExitWatch {
pub fn send_signal(&self, signal: i32) -> io::Result<()> {
cvt!(unsafe { libc::kill(self.pid, signal) });
Ok(())
}
}
#[derive(Debug)]
pub struct Poller {
kq: OwnedFd,
events: Vec<libc::kevent>,
}
fn kev(ident: usize, filter: i16, flags: u16, fflags: u32, token: u64) -> libc::kevent {
libc::kevent {
ident,
filter,
flags,
fflags,
data: 0,
udata: token as *mut libc::c_void,
}
}
impl Poller {
pub fn new() -> io::Result<Self> {
let fd = cvt!(unsafe { libc::kqueue() });
let kq = unsafe { OwnedFd::from_raw_fd(fd) };
cvt!(unsafe { libc::fcntl(kq.as_raw_fd(), libc::F_SETFD, libc::FD_CLOEXEC) });
Ok(Self {
kq,
events: vec![kev(0, 0, 0, 0, 0); 64],
})
}
pub fn watch_read(&self, fd: BorrowedFd<'_>, token: u64) -> io::Result<()> {
self.change(kev(
fd.as_raw_fd() as usize,
libc::EVFILT_READ,
libc::EV_ADD,
0,
token,
))
}
pub fn unwatch_read(&self, fd: BorrowedFd<'_>) -> io::Result<()> {
self.change(kev(
fd.as_raw_fd() as usize,
libc::EVFILT_READ,
libc::EV_DELETE,
0,
0,
))
}
pub fn watch_exit(&self, pid: libc::pid_t, token: u64) -> io::Result<ExitWatch> {
let proc_ev = kev(
pid as usize,
libc::EVFILT_PROC,
libc::EV_ADD,
libc::NOTE_EXIT,
token,
);
match self.change(proc_ev) {
Ok(()) => Ok(ExitWatch {
pid,
filter: libc::EVFILT_PROC,
}),
Err(e) if e.raw_os_error() == Some(libc::ESRCH) => {
self.change(kev(
pid as usize,
libc::EVFILT_USER,
libc::EV_ADD | libc::EV_ONESHOT,
0,
token,
))?;
self.change(kev(
pid as usize,
libc::EVFILT_USER,
0,
libc::NOTE_TRIGGER,
token,
))?;
Ok(ExitWatch {
pid,
filter: libc::EVFILT_USER,
})
}
Err(e) => Err(e),
}
}
pub fn unwatch_exit(&self, watch: &ExitWatch) -> io::Result<()> {
let ev = kev(watch.pid as usize, watch.filter, libc::EV_DELETE, 0, 0);
match self.change(ev) {
Err(e) if matches!(e.raw_os_error(), Some(libc::ENOENT) | Some(libc::ESRCH)) => Ok(()),
result => result,
}
}
fn change(&self, ev: libc::kevent) -> io::Result<()> {
cvt!(unsafe { libc::kevent(self.kq.as_raw_fd(), &ev, 1, ptr::null_mut(), 0, ptr::null()) });
Ok(())
}
pub fn wait(&mut self, tokens: &mut Vec<u64>, timeout: Option<Duration>) -> io::Result<usize> {
let ts;
let ts_ptr = match timeout {
None => ptr::null(),
Some(d) => {
ts = libc::timespec {
tv_sec: d.as_secs().min(libc::time_t::MAX as u64) as libc::time_t,
tv_nsec: d.subsec_nanos() as libc::c_long,
};
&ts
}
};
let n = cvt!(unsafe {
libc::kevent(
self.kq.as_raw_fd(),
ptr::null(),
0,
self.events.as_mut_ptr(),
self.events.len() as libc::c_int,
ts_ptr,
)
});
tokens.clear();
tokens.extend(self.events[..n as usize].iter().map(|ev| ev.udata as u64));
Ok(n as usize)
}
}