signal: specialize windows Registry (#7885)

This commit is contained in:
Tim Vilgot Mikael Fredenberg
2026-02-06 11:29:34 +01:00
committed by GitHub
parent c0943f99f0
commit 7e952697e8
4 changed files with 40 additions and 62 deletions
+1 -8
View File
@@ -50,16 +50,9 @@ mod ctrl_c;
#[cfg(feature = "signal")] #[cfg(feature = "signal")]
pub use ctrl_c::ctrl_c; pub use ctrl_c::ctrl_c;
#[cfg(unix)]
pub(crate) mod registry; pub(crate) mod registry;
mod os {
#[cfg(unix)]
pub(crate) use super::unix::{OsExtraData, OsStorage};
#[cfg(windows)]
pub(crate) use super::windows::{OsExtraData, OsStorage};
}
pub mod unix; pub mod unix;
pub mod windows; pub mod windows;
+1 -1
View File
@@ -1,4 +1,4 @@
use crate::signal::os::{OsExtraData, OsStorage}; use crate::signal::unix::{OsExtraData, OsStorage};
use crate::sync::watch; use crate::sync::watch;
use std::ops; use std::ops;
-3
View File
@@ -16,9 +16,6 @@ use std::task::{Context, Poll};
#[path = "windows/sys.rs"] #[path = "windows/sys.rs"]
mod imp; mod imp;
#[cfg(windows)]
pub(crate) use self::imp::{OsExtraData, OsStorage};
// For building documentation on Unix machines when the `docsrs` flag is set. // For building documentation on Unix machines when the `docsrs` flag is set.
#[cfg(not(windows))] #[cfg(not(windows))]
#[path = "windows/stub.rs"] #[path = "windows/stub.rs"]
+38 -50
View File
@@ -1,35 +1,37 @@
use std::io; use std::io;
use std::sync::OnceLock; use std::sync::OnceLock;
use crate::signal::registry::{globals, EventId, EventInfo, Storage};
use crate::signal::RxFuture; use crate::signal::RxFuture;
use crate::sync::watch;
use windows_sys::core::BOOL; use windows_sys::core::BOOL;
use windows_sys::Win32::System::Console as console; use windows_sys::Win32::System::Console as console;
type EventInfo = watch::Sender<()>;
pub(super) fn ctrl_break() -> io::Result<RxFuture> { pub(super) fn ctrl_break() -> io::Result<RxFuture> {
new(console::CTRL_BREAK_EVENT) new(&registry().ctrl_break)
} }
pub(super) fn ctrl_close() -> io::Result<RxFuture> { pub(super) fn ctrl_close() -> io::Result<RxFuture> {
new(console::CTRL_CLOSE_EVENT) new(&registry().ctrl_close)
} }
pub(super) fn ctrl_c() -> io::Result<RxFuture> { pub(super) fn ctrl_c() -> io::Result<RxFuture> {
new(console::CTRL_C_EVENT) new(&registry().ctrl_c)
} }
pub(super) fn ctrl_logoff() -> io::Result<RxFuture> { pub(super) fn ctrl_logoff() -> io::Result<RxFuture> {
new(console::CTRL_LOGOFF_EVENT) new(&registry().ctrl_logoff)
} }
pub(super) fn ctrl_shutdown() -> io::Result<RxFuture> { pub(super) fn ctrl_shutdown() -> io::Result<RxFuture> {
new(console::CTRL_SHUTDOWN_EVENT) new(&registry().ctrl_shutdown)
} }
fn new(signum: u32) -> io::Result<RxFuture> { fn new(event_info: &EventInfo) -> io::Result<RxFuture> {
global_init()?; global_init()?;
let rx = globals().register_listener(signum as EventId); let rx = event_info.subscribe();
Ok(RxFuture::new(rx)) Ok(RxFuture::new(rx))
} }
@@ -40,16 +42,14 @@ fn event_requires_infinite_sleep_in_handler(signum: u32) -> bool {
// //
// For more information, see: // For more information, see:
// https://learn.microsoft.com/en-us/windows/console/handlerroutine#remarks // https://learn.microsoft.com/en-us/windows/console/handlerroutine#remarks
match signum { matches!(
console::CTRL_CLOSE_EVENT => true, signum,
console::CTRL_LOGOFF_EVENT => true, console::CTRL_CLOSE_EVENT | console::CTRL_LOGOFF_EVENT | console::CTRL_SHUTDOWN_EVENT
console::CTRL_SHUTDOWN_EVENT => true, )
_ => false,
}
} }
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub(crate) struct OsStorage { struct Registry {
ctrl_break: EventInfo, ctrl_break: EventInfo,
ctrl_close: EventInfo, ctrl_close: EventInfo,
ctrl_c: EventInfo, ctrl_c: EventInfo,
@@ -57,32 +57,24 @@ pub(crate) struct OsStorage {
ctrl_shutdown: EventInfo, ctrl_shutdown: EventInfo,
} }
impl Storage for OsStorage { impl Registry {
fn event_info(&self, id: EventId) -> Option<&EventInfo> { fn event_info(&self, signum: u32) -> Option<&EventInfo> {
match u32::try_from(id) { match signum {
Ok(console::CTRL_BREAK_EVENT) => Some(&self.ctrl_break), console::CTRL_BREAK_EVENT => Some(&self.ctrl_break),
Ok(console::CTRL_CLOSE_EVENT) => Some(&self.ctrl_close), console::CTRL_CLOSE_EVENT => Some(&self.ctrl_close),
Ok(console::CTRL_C_EVENT) => Some(&self.ctrl_c), console::CTRL_C_EVENT => Some(&self.ctrl_c),
Ok(console::CTRL_LOGOFF_EVENT) => Some(&self.ctrl_logoff), console::CTRL_LOGOFF_EVENT => Some(&self.ctrl_logoff),
Ok(console::CTRL_SHUTDOWN_EVENT) => Some(&self.ctrl_shutdown), console::CTRL_SHUTDOWN_EVENT => Some(&self.ctrl_shutdown),
_ => None, _ => None,
} }
} }
fn for_each<'a, F>(&'a self, mut f: F)
where
F: FnMut(&'a EventInfo),
{
f(&self.ctrl_break);
f(&self.ctrl_close);
f(&self.ctrl_c);
f(&self.ctrl_logoff);
f(&self.ctrl_shutdown);
}
} }
#[derive(Debug, Default)] fn registry() -> &'static Registry {
pub(crate) struct OsExtraData {} static REGISTRY: OnceLock<Registry> = OnceLock::new();
REGISTRY.get_or_init(Default::default)
}
fn global_init() -> io::Result<()> { fn global_init() -> io::Result<()> {
static INIT: OnceLock<Result<(), Option<i32>>> = OnceLock::new(); static INIT: OnceLock<Result<(), Option<i32>>> = OnceLock::new();
@@ -104,27 +96,23 @@ fn global_init() -> io::Result<()> {
} }
unsafe extern "system" fn handler(ty: u32) -> BOOL { unsafe extern "system" fn handler(ty: u32) -> BOOL {
let globals = globals(); // Ignore unknown control signal types.
globals.record_event(ty as EventId); let Some(event_info) = registry().event_info(ty) else {
return 0;
};
// According to https://docs.microsoft.com/en-us/windows/console/handlerroutine // According to https://learn.microsoft.com/en-us/windows/console/handlerroutine
// the handler routine is always invoked in a new thread, thus we don't // the handler routine is always invoked in a new thread, thus we don't
// have the same restrictions as in Unix signal handlers, meaning we can // have the same restrictions as in Unix signal handlers, meaning we can
// go ahead and perform the broadcast here. // go ahead and perform the broadcast here.
let event_was_handled = globals.broadcast(); match event_info.send(()) {
Ok(_) if event_requires_infinite_sleep_in_handler(ty) => loop {
if event_was_handled && event_requires_infinite_sleep_in_handler(ty) {
loop {
std::thread::park(); std::thread::park();
} },
} Ok(_) => 1,
if event_was_handled {
1
} else {
// No one is listening for this notification any more // No one is listening for this notification any more
// let the OS fire the next (possibly the default) handler. // let the OS fire the next (possibly the default) handler.
0 Err(_) => 0,
} }
} }