Skip to content

Commit aa7e0ce

Browse files
authored
signal: add support for realtime signals on illumos (#7029)
The API was added in libc 0.2.168. Also added a test for realtime signals.
1 parent bfa8cad commit aa7e0ce

File tree

4 files changed

+120
-17
lines changed

4 files changed

+120
-17
lines changed

tokio/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,11 +109,11 @@ tracing = { version = "0.1.29", default-features = false, features = ["std"], op
109109
backtrace = { version = "0.3.58" }
110110

111111
[target.'cfg(unix)'.dependencies]
112-
libc = { version = "0.2.149", optional = true }
112+
libc = { version = "0.2.168", optional = true }
113113
signal-hook-registry = { version = "1.1.1", optional = true }
114114

115115
[target.'cfg(unix)'.dev-dependencies]
116-
libc = { version = "0.2.149" }
116+
libc = { version = "0.2.168" }
117117
nix = { version = "0.29.0", default-features = false, features = ["aio", "fs", "socket"] }
118118

119119
[target.'cfg(windows)'.dependencies.windows-sys]

tokio/src/signal/unix.rs

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,22 +26,12 @@ impl Init for OsStorage {
2626
#[cfg(not(any(target_os = "linux", target_os = "illumos")))]
2727
let possible = 0..=33;
2828

29-
// On Linux, there are additional real-time signals available.
30-
#[cfg(target_os = "linux")]
29+
// On Linux and illumos, there are additional real-time signals
30+
// available. (This is also likely true on Solaris, but this should be
31+
// verified before being enabled.)
32+
#[cfg(any(target_os = "linux", target_os = "illumos"))]
3133
let possible = 0..=libc::SIGRTMAX();
3234

33-
// On illumos, signal numbers go up to 41 (SIGINFO). The list of signals
34-
// hasn't changed since 2013. See
35-
// https://github.com/illumos/illumos-gate/blob/master/usr/src/uts/common/sys/iso/signal_iso.h.
36-
//
37-
// illumos also has real-time signals, but this capability isn't exposed
38-
// by libc as of 0.2.167, so we don't support them at the moment. Once
39-
// https://github.com/rust-lang/libc/pull/4171 is merged and released in
40-
// upstream libc, we should switch the illumos impl to do what Linux
41-
// does.
42-
#[cfg(target_os = "illumos")]
43-
let possible = 0..=41;
44-
4535
possible.map(|_| SignalInfo::default()).collect()
4636
}
4737
}

tokio/tests/signal_realtime.rs

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
#![warn(rust_2018_idioms)]
2+
#![cfg(feature = "full")]
3+
#![cfg(any(target_os = "linux", target_os = "illumos"))]
4+
#![cfg(not(miri))] // No `sigaction` in Miri.
5+
6+
mod support {
7+
pub mod signal;
8+
}
9+
10+
use libc::c_int;
11+
use support::signal::send_signal;
12+
13+
use futures::stream::{FuturesUnordered, StreamExt};
14+
use std::collections::HashMap;
15+
use tokio::signal::unix::{signal, SignalKind};
16+
use tokio::time::{sleep, Duration};
17+
use tokio_test::assert_ok;
18+
19+
#[tokio::test]
20+
async fn signal_realtime() {
21+
// Attempt to register a real-time signal for everything between SIGRTMIN
22+
// and SIGRTMAX.
23+
let signals = (libc::SIGRTMIN()..=sigrt_max())
24+
.map(|signum| {
25+
let sig = assert_ok!(
26+
signal(SignalKind::from_raw(signum)),
27+
"failed to create signal for {}",
28+
sigrtnum_to_string(signum),
29+
);
30+
(signum, sig)
31+
})
32+
.collect::<Vec<_>>();
33+
34+
eprintln!(
35+
"registered {} signals in the range {}..={}",
36+
signals.len(),
37+
libc::SIGRTMIN(),
38+
libc::SIGRTMAX()
39+
);
40+
41+
// Now send signals to each of the registered signals.
42+
for signum in libc::SIGRTMIN()..=sigrt_max() {
43+
send_signal(signum);
44+
}
45+
46+
let futures = signals
47+
.into_iter()
48+
.map(|(signum, mut sig)| async move {
49+
let res = sig.recv().await;
50+
(signum, res)
51+
})
52+
.collect::<FuturesUnordered<_>>();
53+
54+
// Ensure that all signals are received in time -- attempt to get whatever
55+
// we can.
56+
let sleep = std::pin::pin!(sleep(Duration::from_secs(5)));
57+
let done = futures.take_until(sleep).collect::<HashMap<_, _>>().await;
58+
59+
let mut none = Vec::new();
60+
let mut missing = Vec::new();
61+
for signum in libc::SIGRTMIN()..=sigrt_max() {
62+
match done.get(&signum) {
63+
Some(Some(())) => {}
64+
Some(None) => none.push(signum),
65+
None => missing.push(signum),
66+
}
67+
}
68+
69+
if none.is_empty() && missing.is_empty() {
70+
return;
71+
}
72+
73+
let mut msg = String::new();
74+
if !none.is_empty() {
75+
msg.push_str("no signals received for:\n");
76+
for signum in none {
77+
msg.push_str(&format!("- {}\n", sigrtnum_to_string(signum)));
78+
}
79+
}
80+
81+
if !missing.is_empty() {
82+
msg.push_str("missing signals for:\n");
83+
for signum in missing {
84+
msg.push_str(&format!("- {}\n", sigrtnum_to_string(signum)));
85+
}
86+
}
87+
88+
panic!("{}", msg);
89+
}
90+
91+
fn sigrt_max() -> c_int {
92+
// Generally, you would expect this to be SIGRTMAX. But QEMU only supports
93+
// 28 real-time signals even though it might report SIGRTMAX to be higher.
94+
// See https://wiki.qemu.org/ChangeLog/9.2#signals.
95+
//
96+
// The goal of this test is to test that real-time signals are supported in
97+
// general, not necessarily that every single signal is supported (which, as
98+
// this example suggests, depends on the execution environment). So cap this
99+
// at SIGRTMIN+27 (i.e. SIGRTMIN..=SIGRTMIN+27, so 28 signals inclusive).
100+
libc::SIGRTMAX().min(libc::SIGRTMIN() + 27)
101+
}
102+
103+
fn sigrtnum_to_string(signum: i32) -> String {
104+
format!("SIGRTMIN+{} (signal {})", signum - libc::SIGRTMIN(), signum)
105+
}

tokio/tests/support/signal.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@ pub fn send_signal(signal: libc::c_int) {
22
use libc::{getpid, kill};
33

44
unsafe {
5-
assert_eq!(kill(getpid(), signal), 0);
5+
let pid = getpid();
6+
assert_eq!(
7+
kill(pid, signal),
8+
0,
9+
"kill(pid = {}, {}) failed with error: {}",
10+
pid,
11+
signal,
12+
std::io::Error::last_os_error(),
13+
);
614
}
715
}

0 commit comments

Comments
 (0)