use std::{
io,
mem::MaybeUninit,
os::fd::{AsRawFd, OwnedFd},
ptr,
time::Duration,
};
use rustix::{
event::kqueue::{Event, EventFilter, EventFlags, kevent, kqueue},
io::{Errno, FdFlags, fcntl_setfd},
};
use super::{Directions, RawSource, Ready, pipe::Pipe};
pub(crate) struct Poller {
queue: OwnedFd,
wake: Pipe,
}
impl Poller {
pub(crate) const LIVE: bool = true;
pub(crate) fn new() -> io::Result<Self> {
let queue = kqueue()?;
fcntl_setfd(&queue, FdFlags::CLOEXEC)?;
let poller = Self {
queue,
wake: Pipe::new()?,
};
let read_end = poller.wake.read_end().as_raw_fd();
poller.apply([change(EventFilter::Read(read_end), true, WAKE)])?;
Ok(poller)
}
pub(crate) fn notify(&self) -> io::Result<()> {
self.wake.notify()
}
pub(crate) fn add(&self, _key: usize, _descriptor: RawSource) -> io::Result<()> {
Ok(())
}
pub(crate) fn modify(
&self,
key: usize,
descriptor: RawSource,
from: Directions,
to: Directions,
) -> io::Result<()> {
let read = (from.readable != to.readable)
.then(|| change(EventFilter::Read(descriptor), to.readable, key));
let write = (from.writable != to.writable)
.then(|| change(EventFilter::Write(descriptor), to.writable, key));
match (read, write) {
(Some(read), Some(write)) => self.apply([read, write]),
(Some(only), None) | (None, Some(only)) => self.apply([only]),
(None, None) => Ok(()),
}
}
pub(crate) fn delete(
&self,
key: usize,
descriptor: RawSource,
from: Directions,
) -> io::Result<()> {
if from.is_empty() {
return Ok(());
}
self.modify(key, descriptor, from, Directions::NONE)
}
pub(crate) fn wait(&self, timeout: Option<Duration>) -> io::Result<Vec<Ready>> {
let mut events = [MaybeUninit::uninit(); EVENTS];
let events = match unsafe { kevent(&self.queue, &[], &mut events, timeout) } {
Ok((events, _)) => events,
Err(Errno::INTR) => return Ok(Vec::new()),
Err(e) => return Err(e.into()),
};
let wake = self.wake.read_end().as_raw_fd();
let mut ready = Vec::with_capacity(events.len());
for event in events.iter() {
let directions = match event.filter() {
EventFilter::Read(descriptor) if descriptor == wake => {
self.wake.drain();
continue;
}
EventFilter::Read(_) => Directions {
readable: true,
writable: false,
},
EventFilter::Write(_) => Directions {
readable: false,
writable: true,
},
_ => continue,
};
let key = event.udata().addr();
let dropped = if event.flags().contains(EventFlags::ONESHOT) {
directions
} else {
Directions::NONE
};
ready.push(Ready {
key,
directions,
dropped,
});
}
Ok(ready)
}
fn apply<const N: usize>(&self, changes: [Event; N]) -> io::Result<()> {
let mut receipts = [MaybeUninit::uninit(); N];
let (receipts, _) =
unsafe { kevent(&self.queue, &changes, &mut receipts, Some(Duration::ZERO)) }?;
debug_assert_eq!(receipts.len(), N, "every change comes back as a receipt");
for (change, receipt) in changes.iter().zip(receipts.iter()) {
if !receipt.flags().contains(EventFlags::ERROR) {
continue;
}
let errno = match receipt.data() {
0 => continue,
errno => Errno::from_raw_os_error(errno as i32),
};
let gone = errno == Errno::NOENT && change.flags().contains(EventFlags::DELETE);
if !gone {
return Err(errno.into());
}
}
Ok(())
}
}
fn change(filter: EventFilter, watched: bool, key: usize) -> Event {
let action = if watched {
EventFlags::ADD
} else {
EventFlags::DELETE
};
let udata = ptr::without_provenance_mut(key);
Event::new(filter, action | EventFlags::RECEIPT, udata)
}
const WAKE: usize = usize::MAX;
const EVENTS: usize = 256;