From 45c64ff8542765ebe04cd9c7896166988aaa385f Mon Sep 17 00:00:00 2001 From: Rain Date: Tue, 10 Dec 2024 23:55:03 +0000 Subject: [PATCH] signal: add support for realtime signals on illumos The API was added in libc 0.2.168. Also added a test for realtime signals. --- tokio/Cargo.toml | 4 +- tokio/src/signal/unix.rs | 18 ++----- tokio/tests/signal_realtime.rs | 97 ++++++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+), 16 deletions(-) create mode 100644 tokio/tests/signal_realtime.rs diff --git a/tokio/Cargo.toml b/tokio/Cargo.toml index 6c915ecd3e4..658bf9f99b9 100644 --- a/tokio/Cargo.toml +++ b/tokio/Cargo.toml @@ -109,11 +109,11 @@ tracing = { version = "0.1.29", default-features = false, features = ["std"], op backtrace = { version = "0.3.58" } [target.'cfg(unix)'.dependencies] -libc = { version = "0.2.149", optional = true } +libc = { version = "0.2.168", optional = true } signal-hook-registry = { version = "1.1.1", optional = true } [target.'cfg(unix)'.dev-dependencies] -libc = { version = "0.2.149" } +libc = { version = "0.2.168" } nix = { version = "0.29.0", default-features = false, features = ["aio", "fs", "socket"] } [target.'cfg(windows)'.dependencies.windows-sys] diff --git a/tokio/src/signal/unix.rs b/tokio/src/signal/unix.rs index 740a01fa284..31e2905c02d 100644 --- a/tokio/src/signal/unix.rs +++ b/tokio/src/signal/unix.rs @@ -26,22 +26,12 @@ impl Init for OsStorage { #[cfg(not(any(target_os = "linux", target_os = "illumos")))] let possible = 0..=33; - // On Linux, there are additional real-time signals available. - #[cfg(target_os = "linux")] + // On Linux and illumos, there are additional real-time signals + // available. (This is also likely true on Solaris, but this should be + // verified before being enabled.) + #[cfg(any(target_os = "linux", target_os = "illumos"))] let possible = 0..=libc::SIGRTMAX(); - // On illumos, signal numbers go up to 41 (SIGINFO). The list of signals - // hasn't changed since 2013. See - // https://github.com/illumos/illumos-gate/blob/master/usr/src/uts/common/sys/iso/signal_iso.h. - // - // illumos also has real-time signals, but this capability isn't exposed - // by libc as of 0.2.167, so we don't support them at the moment. Once - // https://github.com/rust-lang/libc/pull/4171 is merged and released in - // upstream libc, we should switch the illumos impl to do what Linux - // does. - #[cfg(target_os = "illumos")] - let possible = 0..=41; - possible.map(|_| SignalInfo::default()).collect() } } diff --git a/tokio/tests/signal_realtime.rs b/tokio/tests/signal_realtime.rs new file mode 100644 index 00000000000..c95d7fdb30e --- /dev/null +++ b/tokio/tests/signal_realtime.rs @@ -0,0 +1,97 @@ +#![warn(rust_2018_idioms)] +#![cfg(feature = "full")] +#![cfg(any(target_os = "linux", target_os = "illumos"))] +#![cfg(not(miri))] // No `sigaction` in Miri. + +mod support { + pub mod signal; +} + +use support::signal::send_signal; + +use futures::stream::{FuturesUnordered, StreamExt}; +use std::collections::HashMap; +use tokio::signal::unix::{signal, SignalKind}; +use tokio::time::{sleep, Duration}; +use tokio_test::assert_ok; + +#[tokio::test] +async fn signal_realtime() { + // Attempt to register a real-time signal for everything between SIGRTMIN + // and SIGRTMAX. + let signals = (libc::SIGRTMIN()..=libc::SIGRTMAX()) + .map(|signum| { + let sig = assert_ok!( + signal(SignalKind::from_raw(signum)), + "failed to create signal for SIGRTMIN+{} (signal {})", + signum - libc::SIGRTMIN(), + signum + ); + (signum, sig) + }) + .collect::>(); + + eprintln!( + "registered {} signals in the range {}..={}", + signals.len(), + libc::SIGRTMIN(), + libc::SIGRTMAX() + ); + + // Now send signals to each of the registered signals. + for signum in libc::SIGRTMIN()..=libc::SIGRTMAX() { + send_signal(signum); + } + + let futures = signals + .into_iter() + .map(|(signum, mut sig)| async move { + let res = sig.recv().await; + (signum, res) + }) + .collect::>(); + + // Ensure that all signals are received in time -- attempt to get whatever + // we can. + let sleep = std::pin::pin!(sleep(Duration::from_secs(5))); + let done = futures.take_until(sleep).collect::>().await; + + let mut none = Vec::new(); + let mut missing = Vec::new(); + for signum in libc::SIGRTMIN()..=libc::SIGRTMAX() { + match done.get(&signum) { + Some(Some(())) => {} + Some(None) => none.push(signum), + None => missing.push(signum), + } + } + + if none.is_empty() && missing.is_empty() { + return; + } + + let mut msg = String::new(); + if !none.is_empty() { + msg.push_str("no signals received for:\n"); + for signum in none { + msg.push_str(&format!( + "- SIGRTMIN+{} (signal {})\n", + signum - libc::SIGRTMIN(), + signum + )); + } + } + + if !missing.is_empty() { + msg.push_str("missing signals for:\n"); + for signum in missing { + msg.push_str(&format!( + "- SIGRTMIN+{} (signal {})\n", + signum - libc::SIGRTMIN(), + signum + )); + } + } + + panic!("{}", msg); +}