use super::super::{Driver, Handle};
use super::RegisterResult;
use crate::runtime::driver::op::Lifecycle;
use io_uring::{opcode, Probe};
use std::os::fd::AsRawFd;
fn ready_handle() -> Option<(Driver, Handle)> {
let (driver, handle) = Driver::new(1024).ok()?;
let mut probe = Probe::new();
{
let mut guard = handle.get_uring().lock();
if !guard.try_init(&mut probe).ok()? {
return None;
}
}
handle.uring_probe.set(Some(probe)).unwrap();
Some((driver, handle))
}
struct BrokenRing {
ring_fd: i32,
saved: i32,
}
impl BrokenRing {
fn new(handle: &Handle) -> Self {
let ring_fd = handle.get_uring().lock().ring().as_raw_fd();
let null = std::fs::File::open("/dev/null").unwrap();
let saved = unsafe { libc::dup(ring_fd) };
assert!(saved >= 0);
let rc = unsafe { libc::dup2(null.as_raw_fd(), ring_fd) };
assert_eq!(rc, ring_fd);
Self { ring_fd, saved }
}
}
impl Drop for BrokenRing {
fn drop(&mut self) {
unsafe {
libc::dup2(self.saved, self.ring_fd);
libc::close(self.saved);
}
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn register_op_keeps_entry_on_submit_failure() {
let Some((_driver, handle)) = ready_handle() else {
return;
};
let broken = BrokenRing::new(&handle);
let entry = opcode::Nop::new().build();
let res = unsafe { handle.register_op(entry, futures::task::noop_waker()) };
let RegisterResult::Pushed { index, .. } = res else {
panic!("expected Pushed after a post-push submit failure");
};
assert!(matches!(
handle.get_uring().lock().ops.get(index),
Some(Lifecycle::Waiting(_))
));
drop(broken);
}