use std::os::unix::io::{AsRawFd, RawFd};
use super::{Interest, Mode, PollEvent, Readiness, Token};
use nix::sys::{
epoll::{
epoll_create1, epoll_ctl, epoll_wait, EpollCreateFlags, EpollEvent, EpollFlags, EpollOp,
},
time::TimeSpec,
timerfd::{ClockId, Expiration, TimerFd, TimerFlags, TimerSetTimeFlags},
};
pub struct Epoll {
epoll_fd: RawFd,
timer_fd: Option<TimerFd>,
}
const TIMER_DATA: u64 = u64::MAX;
fn make_flags(interest: Interest, mode: Mode) -> EpollFlags {
let mut flags = EpollFlags::empty();
if interest.readable {
flags |= EpollFlags::EPOLLIN;
}
if interest.writable {
flags |= EpollFlags::EPOLLOUT;
}
match mode {
Mode::Level => { }
Mode::Edge => flags |= EpollFlags::EPOLLET,
Mode::OneShot => flags |= EpollFlags::EPOLLONESHOT,
}
flags
}
fn flags_to_readiness(flags: EpollFlags) -> Readiness {
Readiness {
readable: flags.contains(EpollFlags::EPOLLIN),
writable: flags.contains(EpollFlags::EPOLLOUT),
error: flags.contains(EpollFlags::EPOLLERR),
}
}
impl Epoll {
pub(crate) fn new(high_precision: bool) -> crate::Result<Epoll> {
let epoll_fd = epoll_create1(EpollCreateFlags::EPOLL_CLOEXEC)?;
let mut timer_fd = None;
if high_precision {
let timer = TimerFd::new(
ClockId::CLOCK_MONOTONIC,
TimerFlags::TFD_CLOEXEC | TimerFlags::TFD_NONBLOCK,
)?;
let mut timer_event = EpollEvent::new(EpollFlags::EPOLLIN, TIMER_DATA);
epoll_ctl(
epoll_fd,
EpollOp::EpollCtlAdd,
timer.as_raw_fd(),
&mut timer_event,
)?;
timer_fd = Some(timer);
}
Ok(Epoll { epoll_fd, timer_fd })
}
pub(crate) fn poll(
&mut self,
timeout: Option<std::time::Duration>,
) -> crate::Result<Vec<PollEvent>> {
let mut buffer = [EpollEvent::empty(); 32];
if let Some(ref timer) = self.timer_fd {
if let Some(timeout) = timeout {
timer.set(
Expiration::OneShot(TimeSpec::from_duration(timeout)),
TimerSetTimeFlags::empty(),
)?;
}
}
let timeout = timeout.map(|d| (d.as_millis() + 1) as isize).unwrap_or(-1);
let n_ready = epoll_wait(self.epoll_fd, &mut buffer, timeout)?;
let events = buffer
.iter()
.take(n_ready)
.flat_map(|event| {
if event.data() == TIMER_DATA {
let _ = self
.timer_fd
.as_ref()
.expect("Got an event from high-precision timer while it is not set up?!")
.wait();
None
} else {
let token_ptr = event.data() as usize as *const Token;
Some(PollEvent {
readiness: flags_to_readiness(event.events()),
token: unsafe { *token_ptr },
})
}
})
.collect();
if let Some(ref timer) = self.timer_fd {
timer.unset()?;
let _ = timer.wait();
}
Ok(events)
}
pub fn register(
&mut self,
fd: RawFd,
interest: Interest,
mode: Mode,
token: *const Token,
) -> crate::Result<()> {
let mut event = EpollEvent::new(make_flags(interest, mode), token as usize as u64);
epoll_ctl(self.epoll_fd, EpollOp::EpollCtlAdd, fd, &mut event).map_err(Into::into)
}
pub fn reregister(
&mut self,
fd: RawFd,
interest: Interest,
mode: Mode,
token: *const Token,
) -> crate::Result<()> {
let mut event = EpollEvent::new(make_flags(interest, mode), token as usize as u64);
epoll_ctl(self.epoll_fd, EpollOp::EpollCtlMod, fd, &mut event).map_err(Into::into)
}
pub fn unregister(&mut self, fd: RawFd) -> crate::Result<()> {
epoll_ctl(self.epoll_fd, EpollOp::EpollCtlDel, fd, None).map_err(Into::into)
}
}
impl Drop for Epoll {
fn drop(&mut self) {
let _ = nix::unistd::close(self.epoll_fd);
}
}