Skip to content

Commit

Permalink
signal: add support for realtime signals on illumos
Browse files Browse the repository at this point in the history
The API was added in libc 0.2.168.

Also added a test for realtime signals.
  • Loading branch information
sunshowers committed Dec 11, 2024
1 parent bfa8cad commit d3cdf49
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 16 deletions.
4 changes: 2 additions & 2 deletions tokio/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
18 changes: 4 additions & 14 deletions tokio/src/signal/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
}
Expand Down
98 changes: 98 additions & 0 deletions tokio/tests/signal_realtime.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#![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())
.into_iter()
.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::<Vec<_>>();

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::<FuturesUnordered<_>>();

// 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::<HashMap<_, _>>().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);
}

0 comments on commit d3cdf49

Please sign in to comment.