use std::io;
use std::sync::atomic::{AtomicBool, Ordering};
use crate::platform::process::ShutdownRequest;
static REQUESTED: AtomicBool = AtomicBool::new(false);
unsafe extern "system" fn console_ctrl_handler(
ctrl_type: winapi::shared::minwindef::DWORD,
) -> winapi::shared::minwindef::BOOL {
use winapi::um::wincon::{
CTRL_BREAK_EVENT, CTRL_CLOSE_EVENT, CTRL_C_EVENT, CTRL_LOGOFF_EVENT, CTRL_SHUTDOWN_EVENT,
};
match ctrl_type {
CTRL_C_EVENT | CTRL_BREAK_EVENT | CTRL_CLOSE_EVENT | CTRL_LOGOFF_EVENT
| CTRL_SHUTDOWN_EVENT => {
REQUESTED.store(true, Ordering::Relaxed);
winapi::shared::minwindef::TRUE
}
_ => winapi::shared::minwindef::FALSE,
}
}
pub fn install_shutdown_request_handler() -> io::Result<ShutdownRequest> {
REQUESTED.store(false, Ordering::Relaxed);
let installed = unsafe {
winapi::um::consoleapi::SetConsoleCtrlHandler(
Some(console_ctrl_handler),
winapi::shared::minwindef::TRUE,
)
};
if installed == 0 {
return Err(io::Error::last_os_error());
}
Ok(ShutdownRequest::watching(&REQUESTED))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn installing_reports_nothing_asked_yet() {
let request = install_shutdown_request_handler().expect("install");
assert!(!request.requested());
}
#[test]
fn every_shutdown_event_is_claimed_and_requests_shutdown() {
use winapi::um::wincon::{
CTRL_BREAK_EVENT, CTRL_CLOSE_EVENT, CTRL_C_EVENT, CTRL_LOGOFF_EVENT,
CTRL_SHUTDOWN_EVENT,
};
let request = install_shutdown_request_handler().expect("install");
for event in [
CTRL_C_EVENT,
CTRL_BREAK_EVENT,
CTRL_CLOSE_EVENT,
CTRL_LOGOFF_EVENT,
CTRL_SHUTDOWN_EVENT,
] {
REQUESTED.store(false, Ordering::Relaxed);
let handled = unsafe { console_ctrl_handler(event) };
assert_eq!(
handled,
winapi::shared::minwindef::TRUE,
"event {event} must be claimed, or Windows applies the default terminate and the drain never runs"
);
assert!(
request.requested(),
"event {event} did not request shutdown"
);
}
REQUESTED.store(false, Ordering::Relaxed);
}
#[test]
fn an_unrecognized_console_event_is_declined() {
let request = install_shutdown_request_handler().expect("install");
REQUESTED.store(false, Ordering::Relaxed);
let handled = unsafe { console_ctrl_handler(0xDEAD_BEEF) };
assert_eq!(
handled,
winapi::shared::minwindef::FALSE,
"an unknown event must be declined so the next handler sees it"
);
assert!(
!request.requested(),
"an unknown event must not request shutdown"
);
}
}