use core::ffi::c_void;
use core::sync::atomic::{AtomicUsize, Ordering};
use std::io;
use std::sync::Arc;
#[cfg(feature = "io-stats")]
use std::sync::OnceLock;
use std::time::Duration;
#[cfg(feature = "io-stats")]
use std::time::Instant;
#[cfg(feature = "io-stats")]
use crate::rt::counters::{IoKind, IoTimes};
use crate::syscore::windows::{
BOOLEAN, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, CancelWaitableTimer, CloseHandle,
CreateIoCompletionPort, CreateWaitableTimerExW, FILE_COMPLETION_INFORMATION,
FILE_REPLACE_COMPLETION_INFORMATION, FILE_SKIP_SET_EVENT_ON_HANDLE, GENERIC_ALL,
GetQueuedCompletionStatusEx, HANDLE, INFINITE, IO_STATUS_BLOCK,
NtAssociateWaitCompletionPacket, NtCancelWaitCompletionPacket, NtCreateWaitCompletionPacket,
NtSetInformationFile, OVERLAPPED_ENTRY, PostQueuedCompletionStatus, STATUS_SUCCESS,
SetFileCompletionNotificationModes, SetWaitableTimer, TIMER_ALL_ACCESS, ULONG, WAIT_TIMEOUT,
last_error,
};
const WAKE: usize = usize::MAX;
const TIMER: usize = usize::MAX - 1;
pub(crate) const SOURCE: usize = 0;
pub(crate) const JOB: usize = usize::MAX - 2;
pub(crate) const EXIT: usize = usize::MAX - 3;
pub(crate) struct Port {
handle: HANDLE,
out: AtomicUsize,
#[cfg(feature = "io-stats")]
times: OnceLock<Arc<IoTimes>>,
}
unsafe impl Send for Port {}
unsafe impl Sync for Port {}
impl Drop for Port {
fn drop(&mut self) {
unsafe { CloseHandle(self.handle) };
}
}
impl Port {
pub(crate) fn handle(&self) -> HANDLE {
self.handle
}
pub(crate) fn issued(&self) {
self.out.fetch_add(1, Ordering::AcqRel);
}
pub(crate) fn completed(&self) {
self.out.fetch_sub(1, Ordering::AcqRel);
}
#[cfg(feature = "io-stats")]
pub(crate) fn time_with(&self, times: Arc<IoTimes>) {
let _ = self.times.set(times);
}
#[cfg(feature = "io-stats")]
pub(crate) fn stamp(&self) -> Option<Instant> {
self.times.get().map(|_| Instant::now())
}
#[cfg(feature = "io-stats")]
pub(crate) fn time(&self, kind: IoKind, issued: Instant) {
if let Some(times) = self.times.get() {
times.add(kind, issued.elapsed());
}
}
pub(crate) fn out(&self) -> usize {
self.out.load(Ordering::Acquire)
}
pub(crate) fn keep(
&self,
stopped: impl Fn() -> bool,
mut deliver: impl FnMut(&OVERLAPPED_ENTRY),
) {
let mut entries = [OVERLAPPED_ENTRY::zeroed(); 64];
while self.out() > 0 && !stopped() {
let mut removed: ULONG = 0;
let ok = unsafe {
GetQueuedCompletionStatusEx(
self.handle,
entries.as_mut_ptr(),
entries.len() as ULONG,
&mut removed,
INFINITE,
0,
)
};
if ok == 0 {
return;
}
for entry in &entries[..removed as usize] {
if !matches!(entry.lpCompletionKey, WAKE | TIMER | EXIT)
&& !entry.lpOverlapped.is_null()
{
deliver(entry);
}
}
}
}
pub(crate) fn watch_exit(&self, process: HANDLE, token: u64) -> io::Result<HANDLE> {
let mut packet: HANDLE = core::ptr::null_mut();
unsafe {
let made =
NtCreateWaitCompletionPacket(&mut packet, GENERIC_ALL, core::ptr::null_mut());
if made != STATUS_SUCCESS {
return Err(nt_error(made));
}
let mut already: BOOLEAN = 0;
let status = NtAssociateWaitCompletionPacket(
packet,
self.handle,
process,
EXIT as *mut c_void,
token as usize as *mut c_void,
STATUS_SUCCESS,
0,
&mut already,
);
if status != STATUS_SUCCESS {
CloseHandle(packet);
return Err(nt_error(status));
}
}
Ok(packet)
}
pub(crate) fn unwatch_exit(packet: HANDLE) {
unsafe {
NtCancelWaitCompletionPacket(packet, 1);
CloseHandle(packet);
}
}
pub(crate) fn close_packet(packet: HANDLE) {
unsafe { CloseHandle(packet) };
}
pub(crate) fn bind(&self, handle: HANDLE) -> io::Result<()> {
let bound = unsafe { CreateIoCompletionPort(handle, self.handle, SOURCE, 0) };
if bound.is_null() {
let first = last_error();
let mut status = IO_STATUS_BLOCK {
Status: 0,
Information: 0,
};
let mut info = FILE_COMPLETION_INFORMATION {
Port: self.handle,
Key: SOURCE as *mut c_void,
};
let moved = unsafe {
NtSetInformationFile(
handle,
&mut status,
core::ptr::from_mut(&mut info).cast(),
size_of::<FILE_COMPLETION_INFORMATION>() as ULONG,
FILE_REPLACE_COMPLETION_INFORMATION,
)
};
if moved != STATUS_SUCCESS {
return Err(first);
}
}
unsafe { SetFileCompletionNotificationModes(handle, FILE_SKIP_SET_EVENT_ON_HANDLE) };
Ok(())
}
}
fn nt_error(status: i32) -> io::Error {
io::Error::from_raw_os_error(crate::syscore::windows::status_error(status).unwrap_or(0))
}
struct Timer {
timer: HANDLE,
packet: HANDLE,
armed: bool,
}
impl Drop for Timer {
fn drop(&mut self) {
unsafe {
CloseHandle(self.packet);
CloseHandle(self.timer);
}
}
}
pub(crate) struct Poller {
port: Arc<Port>,
timer: Option<Timer>,
entries: Vec<OVERLAPPED_ENTRY>,
}
unsafe impl Send for Poller {}
#[derive(Clone)]
pub(crate) struct Notifier {
port: Arc<Port>,
}
impl Poller {
pub(crate) fn new() -> io::Result<Self> {
crate::syscore::windows::winsock::start()?;
let handle = unsafe {
CreateIoCompletionPort(
crate::syscore::windows::INVALID_HANDLE_VALUE,
core::ptr::null_mut(),
0,
ULONG::MAX,
)
};
if handle.is_null() {
return Err(last_error());
}
Ok(Self {
port: Arc::new(Port {
handle,
out: AtomicUsize::new(0),
#[cfg(feature = "io-stats")]
times: OnceLock::new(),
}),
timer: Timer::new(),
entries: vec![OVERLAPPED_ENTRY::zeroed(); 256],
})
}
pub(crate) fn notifier(&self) -> Notifier {
Notifier {
port: Arc::clone(&self.port),
}
}
pub(crate) fn port(&self) -> &Arc<Port> {
&self.port
}
pub(crate) fn wait_at_most(
&mut self,
max: usize,
timeout: Option<Duration>,
mut on_packet: impl FnMut(&OVERLAPPED_ENTRY),
) -> io::Result<bool> {
let ms = match timeout {
None => INFINITE,
Some(t) if t.is_zero() => 0,
Some(t) => match self.timer.as_mut().map(|timer| timer.arm(&self.port, t)) {
Some(Ok(())) => INFINITE,
_ => t
.as_nanos()
.div_ceil(1_000_000)
.min(u128::from(INFINITE - 1)) as u32,
},
};
let capacity = self.entries.len().min(max).max(1) as ULONG;
let mut removed: ULONG = 0;
let ok = unsafe {
GetQueuedCompletionStatusEx(
self.port.handle,
self.entries.as_mut_ptr(),
capacity,
&mut removed,
ms,
0,
)
};
if let Some(timer) = self.timer.as_mut() {
timer.disarm();
}
if ok == 0 {
let err = last_error();
return match err.raw_os_error() {
Some(WAIT_TIMEOUT) => Ok(false),
_ => Err(err),
};
}
let mut woken = false;
for entry in &self.entries[..removed as usize] {
match entry.lpCompletionKey {
WAKE => woken = true,
TIMER => {
if let Some(timer) = self.timer.as_mut() {
timer.armed = false;
}
}
_ if entry.lpOverlapped.is_null() => {}
_ => on_packet(entry),
}
}
Ok(woken)
}
}
impl Timer {
fn new() -> Option<Self> {
unsafe {
let timer = CreateWaitableTimerExW(
core::ptr::null_mut(),
core::ptr::null(),
CREATE_WAITABLE_TIMER_HIGH_RESOLUTION,
TIMER_ALL_ACCESS,
);
if timer.is_null() {
return None;
}
let mut packet: HANDLE = core::ptr::null_mut();
if NtCreateWaitCompletionPacket(&mut packet, GENERIC_ALL, core::ptr::null_mut())
!= STATUS_SUCCESS
{
CloseHandle(timer);
return None;
}
Some(Self {
timer,
packet,
armed: false,
})
}
}
fn arm(&mut self, port: &Port, after: Duration) -> io::Result<()> {
let due = -(i64::try_from(after.as_nanos().div_ceil(100)).unwrap_or(i64::MAX));
unsafe {
if SetWaitableTimer(
self.timer,
&due,
0,
core::ptr::null(),
core::ptr::null_mut(),
0,
) == 0
{
return Err(last_error());
}
let mut already: BOOLEAN = 0;
let status = NtAssociateWaitCompletionPacket(
self.packet,
port.handle,
self.timer,
TIMER as *mut c_void,
core::ptr::null_mut(),
STATUS_SUCCESS,
0,
&mut already,
);
if status != STATUS_SUCCESS {
CancelWaitableTimer(self.timer);
return Err(io::Error::from_raw_os_error(
crate::syscore::windows::status_error(status).unwrap_or(0),
));
}
}
self.armed = true;
Ok(())
}
fn disarm(&mut self) {
if !self.armed {
return;
}
unsafe {
NtCancelWaitCompletionPacket(self.packet, 1);
CancelWaitableTimer(self.timer);
}
self.armed = false;
}
}
impl Notifier {
pub(crate) fn notify(&self) {
unsafe { PostQueuedCompletionStatus(self.port.handle, 0, WAKE, core::ptr::null_mut()) };
}
}
#[cfg(test)]
mod tests;