use std::io;
use std::os::fd::{AsRawFd, FromRawFd, OwnedFd, RawFd};
use std::sync::Arc;
use std::time::{Duration, Instant};
use super::Readiness;
const WAKE: u64 = u64::MAX;
const TIMER: u64 = u64::MAX - 1;
pub(crate) struct Poller {
epoll: Arc<OwnedFd>,
wake: Arc<OwnedFd>,
events: Vec<libc::epoll_event>,
timer: Timer,
}
enum Timer {
Unmade,
Made {
fd: OwnedFd,
armed_for: Option<Instant>,
},
Failed,
}
#[derive(Clone)]
pub(crate) struct Notifier {
wake: Arc<OwnedFd>,
}
#[derive(Clone)]
pub(crate) struct Registrar {
epoll: Arc<OwnedFd>,
}
fn check(ret: libc::c_int) -> io::Result<libc::c_int> {
if ret < 0 {
Err(io::Error::last_os_error())
} else {
Ok(ret)
}
}
impl Poller {
pub(crate) fn new() -> io::Result<Self> {
unsafe {
let epoll = OwnedFd::from_raw_fd(check(libc::epoll_create1(libc::EPOLL_CLOEXEC))?);
let wake = OwnedFd::from_raw_fd(check(libc::eventfd(
0,
libc::EFD_CLOEXEC | libc::EFD_NONBLOCK,
))?);
let mut event = libc::epoll_event {
events: (libc::EPOLLIN | libc::EPOLLET) as u32,
u64: WAKE,
};
check(libc::epoll_ctl(
epoll.as_raw_fd(),
libc::EPOLL_CTL_ADD,
wake.as_raw_fd(),
&mut event,
))?;
Ok(Self {
epoll: Arc::new(epoll),
wake: Arc::new(wake),
events: Vec::with_capacity(256),
timer: Timer::Unmade,
})
}
}
pub(crate) fn notifier(&self) -> Notifier {
Notifier {
wake: Arc::clone(&self.wake),
}
}
pub(crate) fn registrar(&self) -> Registrar {
Registrar {
epoll: Arc::clone(&self.epoll),
}
}
#[cfg(test)]
pub(crate) fn wait(
&mut self,
timeout: Option<Duration>,
mut on_event: impl FnMut(u64, Readiness),
) -> io::Result<bool> {
self.wait_at_most(usize::MAX, timeout, &mut on_event)
}
#[cfg(test)]
pub(crate) fn precise_armed_in(&self) -> Option<Duration> {
let Timer::Made { fd, .. } = &self.timer else {
return None;
};
let mut spec: libc::itimerspec = unsafe { core::mem::zeroed() };
if unsafe { libc::timerfd_gettime(fd.as_raw_fd(), &mut spec) } != 0 {
return None;
}
let left = Duration::new(spec.it_value.tv_sec as u64, spec.it_value.tv_nsec as u32);
(!left.is_zero()).then_some(left)
}
pub(crate) fn arm_precise(&mut self, at: Instant, after: Duration) {
if let Timer::Unmade = self.timer {
self.timer = match self.make_timer() {
Ok(fd) => Timer::Made {
fd,
armed_for: None,
},
Err(err) => {
log::warn!(
"precise sleeps on this shard end on the millisecond: its timer could not be made: {err}"
);
Timer::Failed
}
};
}
let Timer::Made { fd, armed_for } = &mut self.timer else {
return;
};
if *armed_for == Some(at) {
return;
}
let after = after.max(Duration::from_nanos(1));
let spec = libc::itimerspec {
it_interval: libc::timespec {
tv_sec: 0,
tv_nsec: 0,
},
it_value: libc::timespec {
tv_sec: after.as_secs().min(libc::time_t::MAX as u64) as libc::time_t,
tv_nsec: after.subsec_nanos() as _,
},
};
let ret = unsafe { libc::timerfd_settime(fd.as_raw_fd(), 0, &spec, core::ptr::null_mut()) };
if ret == 0 {
*armed_for = Some(at);
} else {
*armed_for = None;
log::warn!(
"a precise sleep ends on the millisecond: its timer could not be armed: {}",
io::Error::last_os_error()
);
}
}
fn make_timer(&self) -> io::Result<OwnedFd> {
unsafe {
let fd = OwnedFd::from_raw_fd(check(libc::timerfd_create(
libc::CLOCK_MONOTONIC,
libc::TFD_CLOEXEC | libc::TFD_NONBLOCK,
))?);
let mut event = libc::epoll_event {
events: (libc::EPOLLIN | libc::EPOLLET) as u32,
u64: TIMER,
};
check(libc::epoll_ctl(
self.epoll.as_raw_fd(),
libc::EPOLL_CTL_ADD,
fd.as_raw_fd(),
&mut event,
))?;
Ok(fd)
}
}
pub(crate) fn wait_at_most(
&mut self,
max: usize,
timeout: Option<Duration>,
mut on_event: impl FnMut(u64, Readiness),
) -> io::Result<bool> {
let ms = match timeout {
None => -1,
Some(t) => {
let ms = t
.as_secs()
.saturating_mul(1000)
.saturating_add(u64::from(t.subsec_nanos()).div_ceil(1_000_000));
ms.min(libc::c_int::MAX as u64) as libc::c_int
}
};
let capacity = self.events.capacity().min(max).max(1) as libc::c_int;
#[cfg(target_arch = "x86_64")]
let call = libc::SYS_epoll_wait;
#[cfg(not(target_arch = "x86_64"))]
let call = libc::SYS_epoll_pwait;
let n = match unsafe {
crate::io::sys::syscall(
call,
[
self.epoll.as_raw_fd() as usize,
self.events.as_mut_ptr() as usize,
capacity as usize,
ms as isize as usize,
0,
0,
],
)
} {
Ok(n) => n,
Err(err) if err.kind() == io::ErrorKind::Interrupted => 0,
Err(err) => return Err(err),
};
unsafe { self.events.set_len(n) };
let mut woken = false;
for event in &self.events {
let token = event.u64;
if token == WAKE {
woken = true;
continue;
}
if token == TIMER {
if let Timer::Made { armed_for, .. } = &mut self.timer {
*armed_for = None;
}
continue;
}
let bits = event.events as libc::c_int;
let hup = bits & (libc::EPOLLHUP | libc::EPOLLERR | libc::EPOLLRDHUP) != 0;
let urgent = bits & libc::EPOLLPRI != 0;
on_event(
token,
Readiness {
readable: bits & libc::EPOLLIN != 0 || hup || urgent,
writable: bits & libc::EPOLLOUT != 0
|| bits & (libc::EPOLLHUP | libc::EPOLLERR) != 0,
urgent,
hangup: hup,
},
);
}
Ok(woken)
}
}
impl Notifier {
#[cfg(feature = "iouring")]
pub(crate) fn of(wake: Arc<OwnedFd>) -> Self {
Self { wake }
}
pub(crate) fn notify(&self) {
let one = 1u64;
unsafe {
libc::write(
self.wake.as_raw_fd(),
(&one as *const u64).cast(),
size_of::<u64>(),
)
};
}
}
impl Registrar {
pub(crate) fn add(&self, fd: RawFd, token: u64, urgent: bool) -> io::Result<()> {
let pri = if urgent { libc::EPOLLPRI } else { 0 };
let mut event = libc::epoll_event {
events: (libc::EPOLLIN | pri | libc::EPOLLOUT | libc::EPOLLRDHUP | libc::EPOLLET)
as u32,
u64: token,
};
check(unsafe {
libc::epoll_ctl(self.epoll.as_raw_fd(), libc::EPOLL_CTL_ADD, fd, &mut event)
})
.map(drop)
}
pub(crate) fn add_exit(&self, pidfd: RawFd, token: u64) -> io::Result<()> {
let mut event = libc::epoll_event {
events: (libc::EPOLLIN | libc::EPOLLONESHOT) as u32,
u64: token,
};
check(unsafe {
libc::epoll_ctl(
self.epoll.as_raw_fd(),
libc::EPOLL_CTL_ADD,
pidfd,
&mut event,
)
})
.map(drop)
}
pub(crate) fn delete(&self, fd: RawFd, _urgent: bool) -> io::Result<()> {
let mut event = libc::epoll_event { events: 0, u64: 0 };
check(unsafe {
libc::epoll_ctl(self.epoll.as_raw_fd(), libc::EPOLL_CTL_DEL, fd, &mut event)
})
.map(drop)
}
}