Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

signal: add support for realtime signals on illumos #7029

Merged
merged 1 commit into from
Dec 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
105 changes: 105 additions & 0 deletions tokio/tests/signal_realtime.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#![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 libc::c_int;
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()..=sigrt_max())
.map(|signum| {
let sig = assert_ok!(
signal(SignalKind::from_raw(signum)),
"failed to create signal for {}",
sigrtnum_to_string(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()..=sigrt_max() {
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()..=sigrt_max() {
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!("- {}\n", sigrtnum_to_string(signum)));
}
}

if !missing.is_empty() {
msg.push_str("missing signals for:\n");
for signum in missing {
msg.push_str(&format!("- {}\n", sigrtnum_to_string(signum)));
}
}

panic!("{}", msg);
}

fn sigrt_max() -> c_int {
// Generally, you would expect this to be SIGRTMAX. But QEMU only supports
// 28 real-time signals even though it might report SIGRTMAX to be higher.
// See https://wiki.qemu.org/ChangeLog/9.2#signals.
//
// The goal of this test is to test that real-time signals are supported in
// general, not necessarily that every single signal is supported (which, as
// this example suggests, depends on the execution environment). So cap this
// at SIGRTMIN+27 (i.e. SIGRTMIN..=SIGRTMIN+27, so 28 signals inclusive).
libc::SIGRTMAX().min(libc::SIGRTMIN() + 27)
}

fn sigrtnum_to_string(signum: i32) -> String {
format!("SIGRTMIN+{} (signal {})", signum - libc::SIGRTMIN(), signum)
}
10 changes: 9 additions & 1 deletion tokio/tests/support/signal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@ pub fn send_signal(signal: libc::c_int) {
use libc::{getpid, kill};

unsafe {
assert_eq!(kill(getpid(), signal), 0);
let pid = getpid();
assert_eq!(
kill(pid, signal),
0,
"kill(pid = {}, {}) failed with error: {}",
pid,
signal,
std::io::Error::last_os_error(),
);
}
}
Loading