use io_uring::{IoUring, opcode, types};
use crate::core::line::CompletionLine;
use crate::core::reactor::Idle;
use crate::core::time::{Clock, DurNs, MonoNs};
use crate::host::std_host::{StdClock, StdIdle};
const ETIME: i32 = 62;
pub struct UringIdle {
ring: IoUring,
clock: StdClock,
park: StdIdle,
failed_timeouts: u64,
}
impl UringIdle {
pub fn new(clock: StdClock, park: StdIdle) -> std::io::Result<Self> {
Ok(Self {
ring: IoUring::new(8)?,
clock,
park,
failed_timeouts: 0,
})
}
#[must_use]
pub fn failed_timeouts(&self) -> u64 {
self.failed_timeouts
}
pub fn probe() -> std::io::Result<()> {
IoUring::new(2).map(drop)
}
fn timeout(&mut self, ns: u64) -> std::io::Result<()> {
let ts = types::Timespec::new()
.sec(ns / 1_000_000_000)
.nsec(u32::try_from(ns % 1_000_000_000).unwrap_or(0));
let sqe = opcode::Timeout::new(&raw const ts).build();
unsafe { self.ring.submission().push(&sqe) }
.map_err(|_| std::io::Error::other("submission queue full"))?;
self.ring.submit_and_wait(1)?;
let cqe = self.ring.completion().next();
match cqe.map(|c| c.result()) {
Some(r) if r == -ETIME || r >= 0 => Ok(()),
Some(r) => Err(std::io::Error::from_raw_os_error(-r)),
None => Ok(()),
}
}
}
impl Idle for UringIdle {
fn sleep_until(&mut self, at: MonoNs) {
let left = at.since(self.clock.monotonic_ns());
if left.0 > 0 && self.timeout(left.0).is_err() {
self.failed_timeouts += 1;
self.park.sleep_until(at);
}
}
fn park_line<R: Fn(&CompletionLine) -> bool>(
&mut self,
line: &CompletionLine,
ready: R,
bound: DurNs,
) -> bool {
self.park.park_line(line, ready, bound)
}
}