use std::{
os::windows::prelude::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle},
time::Duration,
};
use windows_sys::Win32::{
Foundation::{CloseHandle, HANDLE, INVALID_HANDLE_VALUE},
System::IO::{
CreateIoCompletionPort, GetQueuedCompletionStatusEx, PostQueuedCompletionStatus,
OVERLAPPED_ENTRY,
},
};
#[derive(Debug)]
pub struct CompletionPort {
handle: HANDLE,
}
impl CompletionPort {
pub fn new(value: u32) -> std::io::Result<Self> {
let handle = unsafe { CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, value) };
if handle == 0 {
Err(std::io::Error::last_os_error())
} else {
Ok(Self { handle })
}
}
pub fn add_handle(&self, token: usize, handle: HANDLE) -> std::io::Result<()> {
let result = unsafe { CreateIoCompletionPort(handle, self.handle, token, 0) };
if result == 0 {
Err(std::io::Error::last_os_error())
} else {
Ok(())
}
}
pub fn get_many<'a>(
&self,
entries: &'a mut [OVERLAPPED_ENTRY],
timeout: Option<Duration>,
) -> std::io::Result<&'a mut [OVERLAPPED_ENTRY]> {
let mut count = 0;
let result = unsafe {
GetQueuedCompletionStatusEx(
self.handle,
entries.as_mut_ptr(),
std::cmp::min(entries.len(), u32::MAX as usize) as u32,
&mut count,
duration_millis(timeout),
0,
)
};
if result == 0 {
Err(std::io::Error::last_os_error())
} else {
Ok(&mut entries[..count as usize])
}
}
pub fn post(&self, entry: OVERLAPPED_ENTRY) -> std::io::Result<()> {
let result = unsafe {
PostQueuedCompletionStatus(
self.handle,
entry.dwNumberOfBytesTransferred,
entry.lpCompletionKey,
entry.lpOverlapped,
)
};
if result == 0 {
Err(std::io::Error::last_os_error())
} else {
Ok(())
}
}
}
impl Drop for CompletionPort {
fn drop(&mut self) {
unsafe { CloseHandle(self.handle) };
}
}
impl AsRawHandle for CompletionPort {
fn as_raw_handle(&self) -> RawHandle {
self.handle as RawHandle
}
}
impl FromRawHandle for CompletionPort {
unsafe fn from_raw_handle(handle: RawHandle) -> Self {
Self {
handle: handle as HANDLE,
}
}
}
impl IntoRawHandle for CompletionPort {
fn into_raw_handle(self) -> RawHandle {
self.handle as RawHandle
}
}
#[inline]
fn duration_millis(dur: Option<Duration>) -> u32 {
if let Some(dur) = dur {
let dur_ms = dur
.checked_add(Duration::from_nanos(999_999))
.unwrap_or(dur)
.as_millis();
std::cmp::min(dur_ms, u32::MAX as u128) as u32
} else {
u32::MAX
}
}