From 240cc44da87ac30188e946ed1003b0e67f64aad8 Mon Sep 17 00:00:00 2001 From: Tim Vilgot Mikael Fredenberg <26655508+vilgotf@users.noreply.github.com> Date: Wed, 14 Jan 2026 13:20:41 +0100 Subject: [PATCH] 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. --- tokio/src/signal/unix.rs | 51 +++++++++++---------------------- tokio/src/signal/windows/sys.rs | 28 +++++++++--------- 2 files changed, 31 insertions(+), 48 deletions(-) diff --git a/tokio/src/signal/unix.rs b/tokio/src/signal/unix.rs index ea75e8215..c58771ee9 100644 --- a/tokio/src/signal/unix.rs +++ b/tokio/src/signal/unix.rs @@ -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 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>>, } /// 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. diff --git a/tokio/src/signal/windows/sys.rs b/tokio/src/signal/windows/sys.rs index 518560ecf..9fe1261ff 100644 --- a/tokio/src/signal/windows/sys.rs +++ b/tokio/src/signal/windows/sys.rs @@ -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>> = 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 {