use crate::{Error, Result};
use nix::errno::Errno;
use nix::libc::{pollfd, ppoll, sigset_t, POLLIN};
use nix::sys::time::TimeSpec;
use nix::unistd::read;
use std::os::unix::io::RawFd;
use std::time::{Duration, Instant};
pub(crate) struct TimeoutUpdater {
timeout: Duration,
last: Option<Instant>,
}
impl TimeoutUpdater {
pub(crate) fn new(initial_timeout: Duration) -> Self {
Self {
timeout: initial_timeout,
last: None,
}
}
pub fn next(&mut self) -> Duration {
let now = Instant::now();
if let Some(last) = self.last {
self.timeout = match self.timeout.checked_sub(now.duration_since(last)) {
Some(t) => t,
None => Duration::new(0, 0),
};
}
self.last = Some(now);
self.timeout
}
}
pub(crate) fn wait_for_completion_fd(
fd: RawFd,
timeout: Option<Duration>,
sig: Option<&sigset_t>,
) -> Result<()> {
let mut pfd = pollfd {
fd,
events: POLLIN,
revents: 0,
};
let ts = timeout.map(TimeSpec::from_duration);
let ts_ptr = ts.as_ref().map_or(std::ptr::null(), |ts| ts.as_ref());
let sig_ptr = sig.map_or(std::ptr::null(), |s| s);
let ret = unsafe { ppoll(&mut pfd, 1, ts_ptr, sig_ptr) };
if ret < 0 {
Err(Error::from_last_os_error())
} else if ret == 0 {
Err(Error::new(Errno::ETIME, "Timed out"))
} else if ret == 1 && (pfd.revents & POLLIN) == POLLIN {
let mut val = [0u8; 8];
match read(fd, &mut val) {
Ok(_) => Ok(()),
Err(e) => Err(Error::from(e)),
}
} else {
unreachable!()
}
}
pub(crate) fn loop_until(
mut predicate: impl FnMut() -> bool,
timeout_updater: &mut Option<&mut TimeoutUpdater>,
) -> Result<()> {
while !predicate() {
if let Some(timeout_updater) = timeout_updater {
if timeout_updater.next() <= Duration::new(0, 0) {
return Err(Error::new(Errno::ETIME, "Timed out"));
}
}
#[allow(deprecated)]
std::sync::atomic::spin_loop_hint(); }
Ok(())
}