use crate::{
Result,
backend::{
PollInfo, Wake,
poller::{Poller, Ready, last_error, timespec},
},
};
use std::{
os::fd::{AsRawFd, FromRawFd, OwnedFd, RawFd},
sync::{
Arc,
atomic::{AtomicU64, Ordering},
},
time::Duration,
};
pub struct KqueueWake {
fd: OwnedFd,
calls: AtomicU64,
}
impl Wake for KqueueWake {
fn wake(&self) -> Result<()> {
let ev = event(0, libc::EVFILT_USER, 0, libc::NOTE_TRIGGER, 0);
loop {
self.calls.fetch_add(1, Ordering::Relaxed);
let n = unsafe {
libc::kevent(
self.fd.as_raw_fd(),
&ev,
1,
std::ptr::null_mut(),
0,
std::ptr::null(),
)
};
if n >= 0 {
return Ok(());
}
let e = std::io::Error::last_os_error();
if e.kind() != std::io::ErrorKind::Interrupted {
return Err(e.into());
}
}
}
fn syscall_count(&self) -> u64 {
self.calls.load(Ordering::Relaxed)
}
}
pub(crate) struct Kqueue {
wake: Arc<KqueueWake>,
events: Vec<libc::kevent>,
}
fn event(ident: usize, filter: i16, flags: u16, fflags: u32, key: u64) -> libc::kevent {
libc::kevent {
ident,
filter,
flags,
fflags,
data: 0,
udata: key as usize as *mut libc::c_void,
}
}
impl Poller for Kqueue {
type W = KqueueWake;
fn new(capacity: usize) -> Result<Self> {
let fd = unsafe { libc::kqueue() };
if fd < 0 {
return Err(last_error());
}
let fd = unsafe { OwnedFd::from_raw_fd(fd) };
if unsafe { libc::fcntl(fd.as_raw_fd(), libc::F_SETFD, libc::FD_CLOEXEC) } < 0 {
return Err(last_error());
}
let ev = event(0, libc::EVFILT_USER, libc::EV_ADD | libc::EV_CLEAR, 0, 0);
if unsafe {
libc::kevent(
fd.as_raw_fd(),
&ev,
1,
std::ptr::null_mut(),
0,
std::ptr::null(),
)
} < 0
{
return Err(last_error());
}
Ok(Self {
wake: Arc::new(KqueueWake {
fd,
calls: AtomicU64::new(0),
}),
events: vec![event(0, 0, 0, 0, 0); capacity],
})
}
fn waker(&self) -> Arc<Self::W> {
self.wake.clone()
}
fn register(&mut self, fd: RawFd, key: u64) -> Result<()> {
let changes = [
event(
fd as usize,
libc::EVFILT_READ,
libc::EV_ADD | libc::EV_CLEAR,
0,
key,
),
event(
fd as usize,
libc::EVFILT_WRITE,
libc::EV_ADD | libc::EV_CLEAR,
0,
key,
),
];
let mode = unsafe { libc::fcntl(fd, libc::F_GETFL) };
if mode < 0 {
return Err(last_error());
}
let (offset, count) = match mode & libc::O_ACCMODE {
libc::O_RDONLY => (0, 1),
libc::O_WRONLY => (1, 1),
_ => (0, 2),
};
if unsafe {
libc::kevent(
self.fd(),
changes[offset..].as_ptr(),
count,
std::ptr::null_mut(),
0,
std::ptr::null(),
)
} < 0
{
return Err(last_error());
}
Ok(())
}
fn deregister(&mut self, fd: RawFd) -> Result<()> {
let mode = unsafe { libc::fcntl(fd, libc::F_GETFL) };
if mode < 0 {
return Err(last_error());
}
for filter in [libc::EVFILT_READ, libc::EVFILT_WRITE] {
if (filter == libc::EVFILT_READ && mode & libc::O_ACCMODE == libc::O_WRONLY)
|| (filter == libc::EVFILT_WRITE && mode & libc::O_ACCMODE == libc::O_RDONLY)
{
continue;
}
let ev = event(fd as usize, filter, libc::EV_DELETE, 0, 0);
if unsafe { libc::kevent(self.fd(), &ev, 1, std::ptr::null_mut(), 0, std::ptr::null()) }
< 0
{
let e = last_error();
if e.os != Some(libc::ENOENT) {
return Err(e);
}
}
}
Ok(())
}
fn process(&mut self, pid: u32, key: u64) -> Result<Option<OwnedFd>> {
let ev = event(
pid as usize,
libc::EVFILT_PROC,
libc::EV_ADD | libc::EV_ONESHOT,
libc::NOTE_EXIT,
key,
);
if unsafe { libc::kevent(self.fd(), &ev, 1, std::ptr::null_mut(), 0, std::ptr::null()) } < 0
{
return Err(last_error());
}
Ok(None)
}
fn remove_process(&mut self, pid: u32, _fd: Option<RawFd>) {
let ev = event(pid as usize, libc::EVFILT_PROC, libc::EV_DELETE, 0, 0);
unsafe {
libc::kevent(self.fd(), &ev, 1, std::ptr::null_mut(), 0, std::ptr::null());
}
}
fn wait(&mut self, timeout: Option<Duration>, out: &mut Vec<Ready>) -> Result<PollInfo> {
let ts = timeout.map(timespec);
let n = unsafe {
libc::kevent(
self.fd(),
std::ptr::null(),
0,
self.events.as_mut_ptr(),
self.events.len() as i32,
ts.as_ref().map_or(std::ptr::null(), |v| v),
)
};
if n < 0 {
let e = std::io::Error::last_os_error();
if e.kind() == std::io::ErrorKind::Interrupted {
return Ok(PollInfo {
waits: 1,
zero_event_waits: 1,
});
}
return Err(e.into());
}
for e in &self.events[..n as usize] {
if e.filter == libc::EVFILT_USER {
continue;
}
out.push(Ready {
key: e.udata as usize as u64,
read: e.filter == libc::EVFILT_READ,
write: e.filter == libc::EVFILT_WRITE,
});
}
Ok(PollInfo {
waits: 1,
zero_event_waits: u32::from(n == 0),
})
}
fn fd(&self) -> RawFd {
self.wake.fd.as_raw_fd()
}
}