use std::cell::Cell;
use std::io;
use std::os::windows::io::{AsRawHandle, FromRawHandle, OwnedHandle, RawHandle};
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration;
use windows_sys::Win32::Foundation::{
GetLastError, HANDLE, INVALID_HANDLE_VALUE, WAIT_IO_COMPLETION, WAIT_TIMEOUT,
};
use windows_sys::Win32::System::IO::{
CreateIoCompletionPort, GetQueuedCompletionStatusEx, OVERLAPPED, OVERLAPPED_ENTRY,
PostQueuedCompletionStatus,
};
use windows_sys::Win32::System::Performance::{QueryPerformanceCounter, QueryPerformanceFrequency};
use windows_sys::Win32::System::Threading::{
CancelWaitableTimer, CreateWaitableTimerExW, INFINITE, SetWaitableTimer, TIMER_ALL_ACCESS,
};
use crate::platform::runtime_shared::{DriverBackend, Notifier};
pub use crate::platform::runtime_shared::ReadyEvents;
#[link(name = "ntdll")]
unsafe extern "system" {
fn RtlNtStatusToDosError(status: i32) -> u32;
}
const WAKE_KEY: usize = 1;
const IO_KEY: usize = 2;
const COMPLETION_BATCH: usize = 64;
#[derive(Clone, Copy, Debug)]
pub(crate) struct OverlappedResult {
pub(crate) error: u32,
pub(crate) bytes: usize,
}
impl OverlappedResult {
pub(crate) fn into_result(self) -> io::Result<usize> {
if self.error == 0 {
Ok(self.bytes)
} else {
Err(io::Error::from_raw_os_error(self.error as i32))
}
}
}
#[repr(C)]
pub(crate) struct OverlappedHeader {
pub(crate) overlapped: OVERLAPPED,
pub(crate) complete: unsafe fn(*mut OverlappedHeader, OverlappedResult),
}
impl OverlappedHeader {
pub(crate) fn new(complete: unsafe fn(*mut OverlappedHeader, OverlappedResult)) -> Self {
Self {
overlapped: unsafe { std::mem::zeroed() },
complete,
}
}
}
struct NotifierInner {
port: Arc<OwnedHandle>,
closed: Arc<AtomicBool>,
}
impl NotifierInner {
fn notify(&self) -> io::Result<()> {
if self.closed.load(Ordering::Acquire) {
return Err(io::Error::new(
io::ErrorKind::BrokenPipe,
"target runtime driver is closed",
));
}
let posted = unsafe {
PostQueuedCompletionStatus(self.port.as_raw_handle(), 0, WAKE_KEY, std::ptr::null_mut())
};
if posted == 0 {
return Err(io::Error::last_os_error());
}
Ok(())
}
}
#[derive(Clone)]
pub struct ThreadNotifier {
inner: Arc<NotifierInner>,
}
impl Notifier for ThreadNotifier {
fn notify(&self) -> io::Result<()> {
self.inner.notify()
}
}
pub struct Driver {
port: Arc<OwnedHandle>,
timer: OwnedHandle,
closed: Arc<AtomicBool>,
timer_deadline: Cell<Option<Duration>>,
pending_wakes: Cell<u64>,
pending_timers: Cell<u64>,
}
pub fn create_driver() -> io::Result<(Driver, ThreadNotifier)> {
let raw_port =
unsafe { CreateIoCompletionPort(INVALID_HANDLE_VALUE, std::ptr::null_mut(), 0, 1) };
if raw_port.is_null() {
return Err(io::Error::last_os_error());
}
let port = Arc::new(unsafe { OwnedHandle::from_raw_handle(raw_port) });
let timer = create_waitable_timer()?;
let closed = Arc::new(AtomicBool::new(false));
let notifier = ThreadNotifier {
inner: Arc::new(NotifierInner {
port: Arc::clone(&port),
closed: Arc::clone(&closed),
}),
};
let driver = Driver {
port,
timer,
closed,
timer_deadline: Cell::new(None),
pending_wakes: Cell::new(0),
pending_timers: Cell::new(0),
};
Ok((driver, notifier))
}
fn create_waitable_timer() -> io::Result<OwnedHandle> {
let timer =
unsafe { CreateWaitableTimerExW(std::ptr::null(), std::ptr::null(), 0, TIMER_ALL_ACCESS) };
if timer.is_null() {
return Err(io::Error::last_os_error());
}
Ok(unsafe { OwnedHandle::from_raw_handle(timer) })
}
unsafe extern "system" fn timer_apc(
_context: *const core::ffi::c_void,
_timer_low: u32,
_timer_high: u32,
) {
}
impl Driver {
pub(crate) fn bind_current_thread(&self) {}
pub(crate) fn unbind_current_thread(&self) {}
pub fn poll(&self) -> io::Result<Option<ReadyEvents>> {
let mut pending = ReadyEvents::default();
if self.pending_wakes.get() > 0 {
pending.wake = true;
}
if self.pending_timers.get() > 0 {
pending.timer = true;
}
if pending.wake || pending.timer {
return Ok(Some(pending));
}
self.process(Some(Duration::ZERO))
}
pub fn wait(&self) -> io::Result<()> {
let now = monotonic_now()?;
let timeout = self
.timer_deadline
.get()
.map(|deadline| deadline.saturating_sub(now));
let _ = self.process(timeout)?;
Ok(())
}
pub fn rearm_timer(&self, deadline: Option<Duration>) -> io::Result<()> {
self.timer_deadline.set(deadline);
let Some(deadline) = deadline else {
unsafe { CancelWaitableTimer(self.timer.as_raw_handle()) };
return Ok(());
};
let relative = deadline.saturating_sub(monotonic_now()?);
let ticks = (relative.as_nanos() / 100).min(i64::MAX as u128) as i64;
let due = -ticks.max(1);
let armed = unsafe {
SetWaitableTimer(
self.timer.as_raw_handle(),
&due,
0,
Some(timer_apc),
std::ptr::null(),
0,
)
};
if armed == 0 {
return Err(io::Error::last_os_error());
}
Ok(())
}
pub fn drain_wake(&self) -> Option<u64> {
let wakes = self.pending_wakes.replace(0);
if wakes == 0 { None } else { Some(wakes) }
}
pub fn drain_timer(&self) -> Option<u64> {
let timers = self.pending_timers.replace(0);
if timers == 0 { None } else { Some(timers) }
}
pub(crate) fn associate_handle(&self, handle: RawHandle) -> io::Result<()> {
let result = unsafe {
CreateIoCompletionPort(handle as HANDLE, self.port.as_raw_handle(), IO_KEY, 0)
};
if result.is_null() {
return Err(io::Error::last_os_error());
}
Ok(())
}
fn process(&self, timeout: Option<Duration>) -> io::Result<Option<ReadyEvents>> {
let mut ready = ReadyEvents::default();
let mut entries = [unsafe { std::mem::zeroed::<OVERLAPPED_ENTRY>() }; COMPLETION_BATCH];
let mut removed = 0u32;
let dequeued = unsafe {
GetQueuedCompletionStatusEx(
self.port.as_raw_handle(),
entries.as_mut_ptr(),
entries.len() as u32,
&mut removed,
timeout_to_millis(timeout),
1,
)
};
let mut saw_any = false;
if dequeued == 0 {
let error = unsafe { GetLastError() };
match error {
WAIT_TIMEOUT => {}
WAIT_IO_COMPLETION => {}
_ => return Err(io::Error::from_raw_os_error(error as i32)),
}
} else {
let count = removed as usize;
if count > 0 {
saw_any = true;
}
for entry in entries.iter().take(count) {
match entry.lpCompletionKey {
WAKE_KEY => {
ready.wake = true;
self.pending_wakes
.set(self.pending_wakes.get().saturating_add(1));
}
IO_KEY => dispatch_io_entry(entry),
_ => {}
}
}
}
if let Some(deadline) = self.timer_deadline.get()
&& monotonic_now()? >= deadline
{
ready.timer = true;
saw_any = true;
self.timer_deadline.set(None);
self.pending_timers
.set(self.pending_timers.get().saturating_add(1));
}
if saw_any { Ok(Some(ready)) } else { Ok(None) }
}
}
fn dispatch_io_entry(entry: &OVERLAPPED_ENTRY) {
let header = entry.lpOverlapped as *mut OverlappedHeader;
if header.is_null() {
return;
}
let (complete, result) = unsafe {
let status = (*header).overlapped.Internal as i32;
let error = if status == 0 {
0
} else {
RtlNtStatusToDosError(status)
};
(
(*header).complete,
OverlappedResult {
error,
bytes: entry.dwNumberOfBytesTransferred as usize,
},
)
};
unsafe { complete(header, result) };
}
impl Drop for Driver {
fn drop(&mut self) {
self.closed.store(true, Ordering::Release);
unsafe { CancelWaitableTimer(self.timer.as_raw_handle()) };
loop {
let mut entries = [unsafe { std::mem::zeroed::<OVERLAPPED_ENTRY>() }; COMPLETION_BATCH];
let mut removed = 0u32;
let dequeued = unsafe {
GetQueuedCompletionStatusEx(
self.port.as_raw_handle(),
entries.as_mut_ptr(),
entries.len() as u32,
&mut removed,
0,
0,
)
};
if dequeued == 0 || removed == 0 {
break;
}
for entry in entries.iter().take(removed as usize) {
if entry.lpCompletionKey == IO_KEY {
dispatch_io_entry(entry);
}
}
}
unsafe {
windows_sys::Win32::System::Threading::SleepEx(0, 1);
}
}
}
impl DriverBackend for Driver {
fn poll(&self) -> io::Result<Option<ReadyEvents>> {
Driver::poll(self)
}
fn wait(&self) -> io::Result<()> {
Driver::wait(self)
}
fn rearm_timer(&self, deadline: Option<Duration>) -> io::Result<()> {
Driver::rearm_timer(self, deadline)
}
fn drain_wake(&self) -> Option<u64> {
Driver::drain_wake(self)
}
fn drain_timer(&self) -> Option<u64> {
Driver::drain_timer(self)
}
fn bind_current_thread(&self) {
Driver::bind_current_thread(self)
}
fn unbind_current_thread(&self) {
Driver::unbind_current_thread(self)
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
fn timeout_to_millis(timeout: Option<Duration>) -> u32 {
match timeout {
None => INFINITE,
Some(value) => {
let millis = value
.as_millis()
.saturating_add(u128::from(value.subsec_nanos() % 1_000_000 != 0));
millis.min(u128::from(INFINITE - 1)) as u32
}
}
}
pub fn monotonic_now() -> io::Result<Duration> {
use std::sync::OnceLock;
static FREQUENCY: OnceLock<i64> = OnceLock::new();
let frequency = *FREQUENCY.get_or_init(|| {
let mut frequency = 0i64;
unsafe { QueryPerformanceFrequency(&mut frequency) };
frequency.max(1)
});
let mut counter = 0i64;
let ok = unsafe { QueryPerformanceCounter(&mut counter) };
if ok == 0 {
return Err(io::Error::last_os_error());
}
let counter = counter as u128;
let frequency = frequency as u128;
let seconds = counter / frequency;
let nanos = (counter % frequency) * 1_000_000_000 / frequency;
Ok(Duration::new(seconds as u64, nanos as u32))
}