use std::io;
use std::os::windows::io::{AsRawHandle, FromRawHandle, OwnedHandle};
use std::time::Duration;
use windows::Win32::Foundation::{HANDLE, WAIT_TIMEOUT};
use windows::Win32::System::IO::{
CreateIoCompletionPort, GetQueuedCompletionStatusEx, OVERLAPPED_ENTRY,
PostQueuedCompletionStatus,
};
use windows::Win32::System::Threading::INFINITE;
use windows::core::HRESULT;
pub(super) const WAKE_KEY: usize = usize::MAX;
pub(super) struct CompletionPort(OwnedHandle);
impl CompletionPort {
pub(super) fn new() -> io::Result<Self> {
let port = unsafe { CreateIoCompletionPort(HANDLE(-1isize as _), None, 0, u32::MAX) }
.map_err(io::Error::from)?;
Ok(Self(unsafe { OwnedHandle::from_raw_handle(port.0 as _) }))
}
fn raw(&self) -> HANDLE {
HANDLE(self.0.as_raw_handle() as _)
}
pub(super) unsafe fn bind(&self, handle: HANDLE, key: usize) -> io::Result<()> {
unsafe { CreateIoCompletionPort(handle, Some(self.raw()), key, 0) }
.map(drop)
.map_err(io::Error::from)
}
pub(super) fn post(&self) -> io::Result<()> {
unsafe { PostQueuedCompletionStatus(self.raw(), 0, WAKE_KEY, None) }
.map_err(io::Error::from)
}
pub(super) fn dequeue(
&self,
entries: &mut [OVERLAPPED_ENTRY],
timeout: Option<Duration>,
) -> io::Result<usize> {
let millis = timeout.map_or(INFINITE, |wait| {
let rounded = wait.as_nanos().div_ceil(1_000_000);
u32::try_from(rounded).map_or(INFINITE - 1, |value| value.min(INFINITE - 1))
});
let mut removed = 0_u32;
let result = unsafe {
GetQueuedCompletionStatusEx(self.raw(), entries, &mut removed, millis, false)
};
match result {
Ok(()) => Ok(removed as usize),
Err(error) if error.code() == HRESULT::from_win32(WAIT_TIMEOUT.0) => Ok(0),
Err(error) => Err(error.into()),
}
}
}