use std::{
io,
mem::MaybeUninit,
os::fd::{BorrowedFd, OwnedFd},
time::Duration,
};
use rustix::{
event::{
Timespec,
epoll::{self, CreateFlags, Event, EventData, EventFlags},
},
io::{Errno, read},
time::{
Itimerspec, TimerfdClockId, TimerfdFlags, TimerfdTimerFlags, timerfd_create,
timerfd_settime,
},
};
use super::{Directions, RawSource, Ready, pipe::Pipe};
pub(crate) struct Poller {
epoll: OwnedFd,
wake: Pipe,
timer: OwnedFd,
}
impl Poller {
pub(crate) const LIVE: bool = true;
pub(crate) fn new() -> io::Result<Self> {
let epoll = epoll::create(CreateFlags::CLOEXEC)?;
let wake = Pipe::new()?;
epoll::add(
&epoll,
wake.read_end(),
EventData::new_u64(WAKE),
EventFlags::IN,
)?;
let timer = timerfd_create(
TimerfdClockId::Monotonic,
TimerfdFlags::CLOEXEC | TimerfdFlags::NONBLOCK,
)?;
epoll::add(&epoll, &timer, EventData::new_u64(TIMER), EventFlags::IN)?;
Ok(Self { epoll, wake, timer })
}
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<()> {
if from == to {
return Ok(());
}
let fd = unsafe { BorrowedFd::borrow_raw(descriptor) };
if to.is_empty() {
return Ok(epoll::delete(&self.epoll, fd)?);
}
let mut flags = EventFlags::empty();
if to.readable {
flags |= EventFlags::IN;
}
if to.writable {
flags |= EventFlags::OUT;
}
let data = EventData::new_u64(key as u64);
if from.is_empty() {
Ok(epoll::add(&self.epoll, fd, data, flags)?)
} else {
Ok(epoll::modify(&self.epoll, fd, data, flags)?)
}
}
pub(crate) fn delete(
&self,
_key: usize,
descriptor: RawSource,
from: Directions,
) -> io::Result<()> {
if from.is_empty() {
return Ok(());
}
let fd = unsafe { BorrowedFd::borrow_raw(descriptor) };
Ok(epoll::delete(&self.epoll, fd)?)
}
pub(crate) fn wait(&self, timeout: Option<Duration>) -> io::Result<Vec<Ready>> {
let mut events = [MaybeUninit::<Event>::uninit(); EVENTS];
let timeout = match timeout {
Some(Duration::ZERO) => Some(Timespec::default()),
Some(timeout) => {
self.set_timer(timeout)?;
None
}
None => None,
};
let (events, _) = match epoll::wait(&self.epoll, &mut events, timeout.as_ref()) {
Ok(filled) => filled,
Err(Errno::INTR) => return Ok(Vec::new()),
Err(e) => return Err(e.into()),
};
let mut ready = Vec::new();
for event in &*events {
let (flags, data) = (event.flags, event.data);
if data.u64() == WAKE {
self.wake.drain();
continue;
}
if data.u64() == TIMER {
let _ = read(&self.timer, &mut [0u8; 8]);
continue;
}
let hung_up = flags.intersects(EventFlags::ERR | EventFlags::HUP);
let directions = Directions {
readable: flags.contains(EventFlags::IN) || hung_up,
writable: flags.contains(EventFlags::OUT) || hung_up,
};
if !directions.is_empty() {
let key = data.u64() as usize;
ready.push(Ready {
key,
directions,
dropped: Directions::NONE,
});
}
}
Ok(ready)
}
fn set_timer(&self, timeout: Duration) -> io::Result<()> {
let it_value = Timespec::try_from(timeout).map_err(|_| io::ErrorKind::InvalidInput)?;
let value = Itimerspec {
it_interval: Timespec::default(),
it_value,
};
timerfd_settime(&self.timer, TimerfdTimerFlags::empty(), &value)?;
Ok(())
}
}
const WAKE: u64 = u64::MAX;
const TIMER: u64 = u64::MAX - 1;
const EVENTS: usize = 256;