signal: remember the result of SetConsoleCtrlHandler (#7833)

The unix implementation remembers whether it failed or not, so the
windows implementation should do so as well. This PR also replaces the
`(Once, AtomicState)` pair with `OnceLock` and tries to remember the
original errno value.
This commit is contained in:
Tim Vilgot Mikael Fredenberg
2026-01-14 13:20:41 +01:00
committed by GitHub
parent 7ed6da6733
commit 240cc44da8
2 changed files with 31 additions and 48 deletions
+17 -34
View File
@@ -14,8 +14,7 @@ use crate::sync::watch;
use mio::net::UnixStream;
use std::io::{self, Error, ErrorKind, Write};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Once;
use std::sync::OnceLock;
use std::task::{Context, Poll};
#[cfg(not(any(target_os = "linux", target_os = "illumos")))]
@@ -239,20 +238,10 @@ impl From<SignalKind> for std::os::raw::c_int {
}
}
#[derive(Default)]
pub(crate) struct SignalInfo {
event_info: EventInfo,
init: Once,
initialized: AtomicBool,
}
impl Default for SignalInfo {
fn default() -> SignalInfo {
SignalInfo {
event_info: EventInfo::default(),
init: Once::new(),
initialized: AtomicBool::new(false),
}
}
init: OnceLock<Result<(), Option<i32>>>,
}
/// Our global signal handler for all signals registered by this module.
@@ -294,26 +283,20 @@ fn signal_enable(signal: SignalKind, handle: &Handle) -> io::Result<()> {
Some(slot) => slot,
None => return Err(io::Error::new(io::ErrorKind::Other, "signal too large")),
};
let mut registered = Ok(());
siginfo.init.call_once(|| {
registered = unsafe {
signal_hook_registry::register(signal, move || action(globals, signal)).map(|_| ())
};
if registered.is_ok() {
siginfo.initialized.store(true, Ordering::Relaxed);
}
});
registered?;
// If the call_once failed, it won't be retried on the next attempt to register the signal. In
// such case it is not run, registered is still `Ok(())`, initialized is still `false`.
if siginfo.initialized.load(Ordering::Relaxed) {
Ok(())
} else {
Err(Error::new(
ErrorKind::Other,
"Failed to register signal handler",
))
}
siginfo
.init
.get_or_init(|| {
unsafe { signal_hook_registry::register(signal, move || action(globals, signal)) }
.map(|_| ())
.map_err(|e| e.raw_os_error())
})
.map_err(|e| {
e.map_or_else(
|| Error::new(ErrorKind::Other, "registering signal handler failed"),
Error::from_raw_os_error,
)
})
}
/// An listener for receiving a particular type of OS signal.
+14 -14
View File
@@ -1,5 +1,5 @@
use std::io;
use std::sync::Once;
use std::sync::OnceLock;
use crate::signal::registry::{globals, EventId, EventInfo, Storage};
use crate::signal::RxFuture;
@@ -85,22 +85,22 @@ impl Storage for OsStorage {
pub(crate) struct OsExtraData {}
fn global_init() -> io::Result<()> {
static INIT: Once = Once::new();
static INIT: OnceLock<Result<(), Option<i32>>> = OnceLock::new();
let mut init = None;
INIT.call_once(|| unsafe {
let rc = console::SetConsoleCtrlHandler(Some(handler), 1);
let ret = if rc == 0 {
Err(io::Error::last_os_error())
INIT.get_or_init(|| {
let rc = unsafe { console::SetConsoleCtrlHandler(Some(handler), 1) };
if rc == 0 {
Err(io::Error::last_os_error().raw_os_error())
} else {
Ok(())
};
init = Some(ret);
});
init.unwrap_or_else(|| Ok(()))
}
})
.map_err(|e| {
e.map_or_else(
|| io::Error::new(io::ErrorKind::Other, "registering signal handler failed"),
io::Error::from_raw_os_error,
)
})
}
unsafe extern "system" fn handler(ty: u32) -> BOOL {