use std::io;
use std::os::windows::io::{AsHandle, AsRawHandle};
use std::time::Duration;
use windows_sys::Win32::Foundation::{
FALSE, TRUE, WAIT_ABANDONED, WAIT_FAILED, WAIT_IO_COMPLETION, WAIT_OBJECT_0, WAIT_TIMEOUT,
};
use windows_sys::Win32::System::Threading::{INFINITE, WaitForSingleObject, WaitForSingleObjectEx};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Alertable {
No,
Yes,
}
impl Alertable {
pub(crate) fn as_bool(self) -> i32 {
match self {
Self::No => FALSE,
Self::Yes => TRUE,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum WaitOutcome {
Signalled,
Abandoned,
TimedOut,
Alerted,
}
pub fn wait_for_single_object(
object: impl AsHandle,
timeout: Option<Duration>,
) -> io::Result<WaitOutcome> {
let milliseconds = milliseconds(timeout)?;
let code = unsafe { WaitForSingleObject(object.as_handle().as_raw_handle(), milliseconds) };
outcome(code)
}
pub fn wait_for_single_object_ex(
object: impl AsHandle,
timeout: Option<Duration>,
alertable: Alertable,
) -> io::Result<WaitOutcome> {
let milliseconds = milliseconds(timeout)?;
let code = unsafe {
WaitForSingleObjectEx(
object.as_handle().as_raw_handle(),
milliseconds,
alertable.as_bool(),
)
};
outcome(code)
}
pub(crate) fn milliseconds(timeout: Option<Duration>) -> io::Result<u32> {
let Some(timeout) = timeout else {
return Ok(INFINITE);
};
let rounded_up = timeout.as_nanos().div_ceil(1_000_000);
match u32::try_from(rounded_up) {
Ok(milliseconds) if milliseconds != INFINITE => Ok(milliseconds),
_ => Err(io::Error::new(
io::ErrorKind::InvalidInput,
"a wait of this many milliseconds cannot be expressed: the longest finite wait is \
u32::MAX - 1",
)),
}
}
pub(crate) fn outcome(code: u32) -> io::Result<WaitOutcome> {
match code {
WAIT_OBJECT_0 => Ok(WaitOutcome::Signalled),
WAIT_ABANDONED => Ok(WaitOutcome::Abandoned),
WAIT_TIMEOUT => Ok(WaitOutcome::TimedOut),
WAIT_IO_COMPLETION => Ok(WaitOutcome::Alerted),
WAIT_FAILED => Err(io::Error::last_os_error()),
other => Err(io::Error::other(format!("a wait returned {other:#x}"))),
}
}
#[cfg(test)]
mod tests;