use std::io;
use std::os::windows::io::AsRawHandle;
use std::sync::{Arc, Mutex};
use windows_sys::Win32::Storage::FileSystem::SetIoRingCompletionEvent;
use windows_threadpool_sys::callback_env::CallbackEnviron;
use windows_threadpool_sys::wait::{ThreadpoolWait, WaitableHandle};
use crate::capability::capabilities;
use crate::error::check;
use crate::ring::{Completion, IoRing};
fn drain(ring: &Mutex<IoRing>, on_completion: &(dyn Fn(Completion) + Send + Sync)) {
loop {
let popped = {
let mut ring = ring
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
ring.try_pop()
};
match popped {
Ok(Some(completion)) => on_completion(completion),
Ok(None) => break,
Err(error) => {
debug_assert!(
false,
"IoRing::try_pop failed during event-driven drain: {error}"
);
break;
}
}
}
}
pub struct EventDelivery {
#[allow(
dead_code,
reason = "held only for its Drop side effect and ordering relative to `ring`"
)]
wait: ThreadpoolWait,
ring: Arc<Mutex<IoRing>>,
}
impl EventDelivery {
pub fn new<F>(
ring: IoRing,
on_completion: F,
env: Option<&mut CallbackEnviron<'_>>,
) -> io::Result<Self>
where
F: Fn(Completion) + Send + Sync + 'static,
{
if !capabilities()?.supports_completion_event {
return Err(io::Error::new(
io::ErrorKind::Unsupported,
"this system's IoRing does not report IORING_FEATURE_SET_COMPLETION_EVENT",
));
}
let event = WaitableHandle::event(false, false)?;
let hr =
unsafe { SetIoRingCompletionEvent(ring.raw_handle(), event.handle().as_raw_handle()) };
check(hr)?;
let ring = Arc::new(Mutex::new(ring));
let ring_for_wait = Arc::clone(&ring);
let on_completion: Arc<dyn Fn(Completion) + Send + Sync> = Arc::new(on_completion);
let wait = ThreadpoolWait::new(
event,
move |activation| {
drain(&ring_for_wait, on_completion.as_ref());
activation.rearm(None);
drain(&ring_for_wait, on_completion.as_ref());
},
env,
)?;
wait.arm(None);
Ok(Self { wait, ring })
}
#[must_use]
pub fn ring(&self) -> &Mutex<IoRing> {
&self.ring
}
}
#[cfg(test)]
mod tests;