use super::readiness::{Poller as PollerTrait, Ready, WAKE};
use super::{timeout_ms, DriverStats, Waker, EVENTS_START};
use crate::net::RawSock;
use crate::os::linux as sys;
use std::io;
use std::os::fd::{AsRawFd, FromRawFd, OwnedFd};
use std::sync::Arc;
use std::time::Duration;
pub(crate) struct Poller {
ep: OwnedFd,
_efd: Arc<OwnedFd>,
events: Vec<sys::epoll_event>,
max_events: usize,
}
pub(crate) fn eventfd() -> io::Result<OwnedFd> {
let fd = unsafe { sys::eventfd(0, sys::EFD_CLOEXEC | sys::EFD_NONBLOCK) };
if fd < 0 {
return Err(io::Error::last_os_error());
}
Ok(unsafe { OwnedFd::from_raw_fd(fd) })
}
pub(crate) fn eventfd_waker(efd: Arc<OwnedFd>) -> Waker {
Waker::new(Box::new(move || {
let one: u64 = 1;
if unsafe { sys::write(efd.as_raw_fd(), (&one as *const u64).cast(), 8) } < 0 {
drain_eventfd(&efd);
unsafe { sys::write(efd.as_raw_fd(), (&one as *const u64).cast(), 8) };
}
}))
}
pub(crate) fn drain_eventfd(fd: &OwnedFd) {
let mut v: u64 = 0;
unsafe { sys::read(fd.as_raw_fd(), (&mut v as *mut u64).cast(), 8) };
}
impl PollerTrait for Poller {
fn new(events: usize) -> io::Result<(Self, Waker)> {
let ep = unsafe { sys::epoll_create1(sys::EPOLL_CLOEXEC) };
if ep < 0 {
return Err(io::Error::last_os_error());
}
let ep = unsafe { OwnedFd::from_raw_fd(ep) };
let efd = Arc::new(eventfd()?);
let mut ev = sys::epoll_event { events: sys::EPOLLIN | sys::EPOLLET, data: WAKE };
if unsafe { sys::epoll_ctl(ep.as_raw_fd(), sys::EPOLL_CTL_ADD, efd.as_raw_fd(), &mut ev) } < 0 {
return Err(io::Error::last_os_error());
}
let waker = eventfd_waker(efd.clone());
Ok((Poller { ep, _efd: efd, events: vec![sys::epoll_event { events: 0, data: 0 }; EVENTS_START.min(events)], max_events: events }, waker))
}
fn add(&mut self, sock: RawSock, token: u64, listener: bool, stats: &mut DriverStats) -> io::Result<()> {
let flags = if listener { sys::EPOLLIN | sys::EPOLLET } else { sys::EPOLLIN | sys::EPOLLOUT | sys::EPOLLRDHUP | sys::EPOLLET };
let mut ev = sys::epoll_event { events: flags, data: token };
stats.other_calls += 1;
if unsafe { sys::epoll_ctl(self.ep.as_raw_fd(), sys::EPOLL_CTL_ADD, sock, &mut ev) } < 0 {
return Err(io::Error::last_os_error());
}
Ok(())
}
fn wait(&mut self, out: &mut Vec<Ready>, timeout: Option<Duration>, stats: &mut DriverStats) -> io::Result<()> {
stats.waits += 1;
let n = unsafe { sys::epoll_wait(self.ep.as_raw_fd(), self.events.as_mut_ptr(), self.events.len() as i32, timeout_ms(timeout)) };
if n < 0 {
let e = io::Error::last_os_error();
if e.kind() == io::ErrorKind::Interrupted {
return Ok(());
}
return Err(e);
}
let full = n as usize == self.events.len();
for ev in &self.events[..n as usize] {
let (flags, token) = (ev.events, ev.data);
out.push(Ready {
token,
readable: flags & (sys::EPOLLIN | sys::EPOLLRDHUP) != 0,
writable: flags & sys::EPOLLOUT != 0,
closed: flags & (sys::EPOLLHUP | sys::EPOLLERR) != 0,
hup: flags & sys::EPOLLRDHUP != 0,
});
}
if full && self.events.len() < self.max_events {
let len = (self.events.len() * 2).min(self.max_events);
self.events.resize(len, sys::epoll_event { events: 0, data: 0 });
}
Ok(())
}
}