mirror of
https://github.com/tokio-rs/tokio.git
synced 2025-09-25 12:00:35 +00:00
signal: Add SignalKind for registering signals more easily (#1430)
This avoids having consumers import libc for common signals, and it improves discoverability since users need not be aware that libc contains all supported constants.
This commit is contained in:
parent
513326e01d
commit
338b37884a
@ -40,7 +40,7 @@ use std::process::{self, ExitStatus};
|
||||
use std::task::Context;
|
||||
use std::task::Poll;
|
||||
use tokio_reactor::{Handle, PollEvented};
|
||||
use tokio_signal::unix::Signal;
|
||||
use tokio_signal::unix::{Signal, SignalKind};
|
||||
|
||||
impl Wait for process::Child {
|
||||
fn id(&self) -> u32 {
|
||||
@ -99,7 +99,7 @@ pub(crate) fn spawn_child(cmd: &mut process::Command, handle: &Handle) -> io::Re
|
||||
let stdout = stdio(child.stdout.take(), handle)?;
|
||||
let stderr = stdio(child.stderr.take(), handle)?;
|
||||
|
||||
let signal = Signal::with_handle(libc::SIGCHLD, handle)?;
|
||||
let signal = Signal::with_handle(SignalKind::sigchld(), handle)?;
|
||||
|
||||
Ok(SpawnedChild {
|
||||
child: Child {
|
||||
|
@ -7,12 +7,14 @@
|
||||
#[cfg(unix)]
|
||||
mod platform {
|
||||
use futures_util::stream::{self, StreamExt};
|
||||
use tokio_signal::unix::{Signal, SIGINT, SIGTERM};
|
||||
use tokio_signal::unix::{Signal, SignalKind};
|
||||
|
||||
pub async fn main() {
|
||||
// Create a stream for each of the signals we'd like to handle.
|
||||
let sigint = Signal::new(SIGINT).unwrap().map(|_| SIGINT);
|
||||
let sigterm = Signal::new(SIGTERM).unwrap().map(|_| SIGTERM);
|
||||
let sigint = Signal::new(SignalKind::sigint()).unwrap().map(|_| "SIGINT");
|
||||
let sigterm = Signal::new(SignalKind::sigterm())
|
||||
.unwrap()
|
||||
.map(|_| "SIGTERM");
|
||||
|
||||
// Use the `select` combinator to merge these two streams into one
|
||||
let stream = stream::select(sigint, sigterm);
|
||||
@ -27,13 +29,8 @@ mod platform {
|
||||
let (item, _rest) = stream.into_future().await;
|
||||
|
||||
// Figure out which signal we received
|
||||
let item = item.ok_or("received no signal").unwrap();
|
||||
if item == SIGINT {
|
||||
println!("received SIGINT");
|
||||
} else {
|
||||
assert_eq!(item, SIGTERM);
|
||||
println!("received SIGTERM");
|
||||
}
|
||||
let msg = item.ok_or("received no signal").unwrap();
|
||||
println!("received {}", msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,11 +5,11 @@
|
||||
#[cfg(unix)]
|
||||
mod platform {
|
||||
use futures_util::stream::StreamExt;
|
||||
use tokio_signal::unix::{Signal, SIGHUP};
|
||||
use tokio_signal::unix::{Signal, SignalKind};
|
||||
|
||||
pub async fn main() {
|
||||
// on Unix, we can listen to whatever signal we want, in this case: SIGHUP
|
||||
let mut stream = Signal::new(SIGHUP).unwrap();
|
||||
let mut stream = Signal::new(SignalKind::sighup()).unwrap();
|
||||
|
||||
println!("Waiting for SIGHUPS (Ctrl+C to quit)");
|
||||
println!(
|
||||
|
@ -59,7 +59,7 @@
|
||||
//!
|
||||
//! use futures_util::future;
|
||||
//! use futures_util::stream::StreamExt;
|
||||
//! use tokio_signal::unix::{Signal, SIGHUP};
|
||||
//! use tokio_signal::unix::{Signal, SignalKind};
|
||||
//!
|
||||
//! #[tokio::main]
|
||||
//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
@ -77,7 +77,7 @@
|
||||
//!
|
||||
//! // Like the previous example, this is an infinite stream of signals
|
||||
//! // being received, and signals may be coalesced while pending.
|
||||
//! let stream = Signal::new(SIGHUP)?;
|
||||
//! let stream = Signal::new(SignalKind::sighup())?;
|
||||
//!
|
||||
//! // Convert out stream into a future and block the program
|
||||
//! let (signal, _signal) = stream.into_future().await;
|
||||
|
@ -23,28 +23,6 @@ use tokio_sync::mpsc::{channel, Receiver};
|
||||
|
||||
use crate::registry::{globals, EventId, EventInfo, Globals, Init, Storage};
|
||||
|
||||
pub use libc::{SIGALRM, SIGHUP, SIGPIPE, SIGQUIT, SIGTRAP};
|
||||
pub use libc::{SIGINT, SIGTERM, SIGUSR1, SIGUSR2};
|
||||
|
||||
/// BSD-specific definitions
|
||||
#[cfg(any(
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "macos",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd",
|
||||
))]
|
||||
pub mod bsd {
|
||||
#[cfg(any(
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "macos",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd"
|
||||
))]
|
||||
pub use super::libc::SIGINFO;
|
||||
}
|
||||
|
||||
pub(crate) type OsStorage = Vec<SignalInfo>;
|
||||
|
||||
// Number of different unix signals
|
||||
@ -84,6 +62,131 @@ impl Init for OsExtraData {
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the specific kind of signal to listen for.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct SignalKind(c_int);
|
||||
|
||||
impl SignalKind {
|
||||
/// Allows for listening to any valid OS signal.
|
||||
///
|
||||
/// For example, this can be used for listening for platform-specific
|
||||
/// signals.
|
||||
/// ```rust,no_run
|
||||
/// # use tokio_signal::unix::SignalKind;
|
||||
/// # let signum = -1;
|
||||
/// // let signum = libc::OS_SPECIFIC_SIGNAL;
|
||||
/// let kind = SignalKind::from_raw(signum);
|
||||
/// ```
|
||||
pub fn from_raw(signum: c_int) -> Self {
|
||||
Self(signum)
|
||||
}
|
||||
|
||||
/// Represents the SIGALRM signal.
|
||||
///
|
||||
/// On Unix systems this signal is sent when a real-time timer has expired.
|
||||
/// By default, the process is terminated by this signal.
|
||||
pub fn sigalrm() -> Self {
|
||||
Self(libc::SIGALRM)
|
||||
}
|
||||
|
||||
/// Represents the SIGCHLD signal.
|
||||
///
|
||||
/// On Unix systems this signal is sent when the status of a child process
|
||||
/// has changed. By default, this signal is ignored.
|
||||
pub fn sigchld() -> Self {
|
||||
Self(libc::SIGCHLD)
|
||||
}
|
||||
|
||||
/// Represents the SIGHUP signal.
|
||||
///
|
||||
/// On Unix systems this signal is sent when the terminal is disconnected.
|
||||
/// By default, the process is terminated by this signal.
|
||||
pub fn sighup() -> Self {
|
||||
Self(libc::SIGHUP)
|
||||
}
|
||||
|
||||
/// Represents the SIGINFO signal.
|
||||
///
|
||||
/// On Unix systems this signal is sent to request a status update from the
|
||||
/// process. By default, this signal is ignored.
|
||||
#[cfg(any(
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "macos",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd"
|
||||
))]
|
||||
pub fn siginfo() -> Self {
|
||||
Self(libc::SIGINFO)
|
||||
}
|
||||
|
||||
/// Represents the SIGINT signal.
|
||||
///
|
||||
/// On Unix systems this signal is sent to interrupt a program.
|
||||
/// By default, the process is terminated by this signal.
|
||||
pub fn sigint() -> Self {
|
||||
Self(libc::SIGINT)
|
||||
}
|
||||
|
||||
/// Represents the SIGIO signal.
|
||||
///
|
||||
/// On Unix systems this signal is sent when I/O operations are possible
|
||||
/// on some file descriptor. By default, this signal is ignored.
|
||||
pub fn sigio() -> Self {
|
||||
Self(libc::SIGIO)
|
||||
}
|
||||
|
||||
/// Represents the SIGPIPE signal.
|
||||
///
|
||||
/// On Unix systems this signal is sent when the process attempts to write
|
||||
/// to a pipe which has no reader. By default, the process is terminated by
|
||||
/// this signal.
|
||||
pub fn sigpipe() -> Self {
|
||||
Self(libc::SIGPIPE)
|
||||
}
|
||||
|
||||
/// Represents the SIGQUIT signal.
|
||||
///
|
||||
/// On Unix systems this signal is sent to issue a shutdown of the
|
||||
/// process, after which the OS will dump the process core.
|
||||
/// By default, the process is terminated by this signal.
|
||||
pub fn sigquit() -> Self {
|
||||
Self(libc::SIGQUIT)
|
||||
}
|
||||
|
||||
/// Represents the SIGTERM signal.
|
||||
///
|
||||
/// On Unix systems this signal is sent to issue a shutdown of the
|
||||
/// process. By default, the process is terminated by this signal.
|
||||
pub fn sigterm() -> Self {
|
||||
Self(libc::SIGTERM)
|
||||
}
|
||||
|
||||
/// Represents the SIGUSR1 signal.
|
||||
///
|
||||
/// On Unix systems this is a user defined signal.
|
||||
/// By default, the process is terminated by this signal.
|
||||
pub fn sigusr1() -> Self {
|
||||
Self(libc::SIGUSR1)
|
||||
}
|
||||
|
||||
/// Represents the SIGUSR2 signal.
|
||||
///
|
||||
/// On Unix systems this is a user defined signal.
|
||||
/// By default, the process is terminated by this signal.
|
||||
pub fn sigusr2() -> Self {
|
||||
Self(libc::SIGUSR2)
|
||||
}
|
||||
|
||||
/// Represents the SIGWINCH signal.
|
||||
///
|
||||
/// On Unix systems this signal is sent when the terminal window is resized.
|
||||
/// By default, this signal is ignored.
|
||||
pub fn sigwinch() -> Self {
|
||||
Self(libc::SIGWINCH)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct SignalInfo {
|
||||
event_info: EventInfo,
|
||||
init: Once,
|
||||
@ -259,10 +362,7 @@ impl Signal {
|
||||
/// Creates a new stream which will receive notifications when the current
|
||||
/// process receives the signal `signal`.
|
||||
///
|
||||
/// This function will create a new stream which binds to the default event
|
||||
/// loop. This function returns a future which will
|
||||
/// then resolve to the signal stream, if successful.
|
||||
///
|
||||
/// This function will create a new stream which binds to the default reactor.
|
||||
/// The `Signal` stream is an infinite stream which will receive
|
||||
/// notifications whenever a signal is received. More documentation can be
|
||||
/// found on `Signal` itself, but to reiterate:
|
||||
@ -281,17 +381,15 @@ impl Signal {
|
||||
/// * If the previous initialization of this specific signal failed.
|
||||
/// * If the signal is one of
|
||||
/// [`signal_hook::FORBIDDEN`](https://docs.rs/signal-hook/*/signal_hook/fn.register.html#panics)
|
||||
pub fn new(signal: c_int) -> io::Result<Self> {
|
||||
Signal::with_handle(signal, &Handle::default())
|
||||
pub fn new(kind: SignalKind) -> io::Result<Self> {
|
||||
Signal::with_handle(kind, &Handle::default())
|
||||
}
|
||||
|
||||
/// Creates a new stream which will receive notifications when the current
|
||||
/// process receives the signal `signal`.
|
||||
///
|
||||
/// This function will create a new stream which may be based on the
|
||||
/// event loop handle provided. This function returns a future which will
|
||||
/// then resolve to the signal stream, if successful.
|
||||
///
|
||||
/// provided reactor handle.
|
||||
/// The `Signal` stream is an infinite stream which will receive
|
||||
/// notifications whenever a signal is received. More documentation can be
|
||||
/// found on `Signal` itself, but to reiterate:
|
||||
@ -303,7 +401,9 @@ impl Signal {
|
||||
/// A `Signal` stream can be created for a particular signal number
|
||||
/// multiple times. When a signal is received then all the associated
|
||||
/// channels will receive the signal notification.
|
||||
pub fn with_handle(signal: c_int, handle: &Handle) -> io::Result<Self> {
|
||||
pub fn with_handle(kind: SignalKind, handle: &Handle) -> io::Result<Self> {
|
||||
let signal = kind.0;
|
||||
|
||||
// Turn the signal delivery on once we are ready for it
|
||||
signal_enable(signal)?;
|
||||
|
||||
@ -320,7 +420,7 @@ impl Signal {
|
||||
}
|
||||
|
||||
pub(crate) fn ctrl_c(handle: &Handle) -> io::Result<Self> {
|
||||
Self::with_handle(libc::SIGINT, handle)
|
||||
Self::with_handle(SignalKind::sigint(), handle)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,18 +6,18 @@ use libc;
|
||||
pub mod support;
|
||||
use crate::support::*;
|
||||
|
||||
const TEST_SIGNAL: libc::c_int = libc::SIGUSR1;
|
||||
|
||||
#[test]
|
||||
fn dropping_loops_does_not_cause_starvation() {
|
||||
let (mut rt, signal) = {
|
||||
let kind = SignalKind::sigusr1();
|
||||
|
||||
let mut first_rt = CurrentThreadRuntime::new().expect("failed to init first runtime");
|
||||
let mut first_signal = Signal::new(TEST_SIGNAL).expect("failed to register first signal");
|
||||
let mut first_signal = Signal::new(kind).expect("failed to register first signal");
|
||||
|
||||
let mut second_rt = CurrentThreadRuntime::new().expect("failed to init second runtime");
|
||||
let mut second_signal = Signal::new(TEST_SIGNAL).expect("failed to register second signal");
|
||||
let mut second_signal = Signal::new(kind).expect("failed to register second signal");
|
||||
|
||||
send_signal(TEST_SIGNAL);
|
||||
send_signal(libc::SIGUSR1);
|
||||
|
||||
let _ = run_with_timeout(&mut first_rt, first_signal.next())
|
||||
.expect("failed to await first signal");
|
||||
@ -31,7 +31,7 @@ fn dropping_loops_does_not_cause_starvation() {
|
||||
(second_rt, second_signal)
|
||||
};
|
||||
|
||||
send_signal(TEST_SIGNAL);
|
||||
send_signal(libc::SIGUSR1);
|
||||
|
||||
let _ = run_with_timeout(&mut rt, signal.into_future());
|
||||
}
|
||||
|
@ -9,11 +9,12 @@ use crate::support::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn drop_then_get_a_signal() {
|
||||
let signal = Signal::new(libc::SIGUSR1).expect("failed to create first signal");
|
||||
let kind = SignalKind::sigusr1();
|
||||
let signal = Signal::new(kind).expect("failed to create first signal");
|
||||
drop(signal);
|
||||
|
||||
send_signal(libc::SIGUSR1);
|
||||
let signal = Signal::new(libc::SIGUSR1).expect("failed to create second signal");
|
||||
let signal = Signal::new(kind).expect("failed to create second signal");
|
||||
|
||||
let _ = with_timeout(signal.into_future()).await;
|
||||
}
|
||||
|
@ -7,21 +7,21 @@ use libc;
|
||||
pub mod support;
|
||||
use crate::support::*;
|
||||
|
||||
const TEST_SIGNAL: libc::c_int = libc::SIGUSR1;
|
||||
|
||||
#[tokio::test]
|
||||
async fn dropping_signal_does_not_deregister_any_other_instances() {
|
||||
let kind = SignalKind::sigusr1();
|
||||
|
||||
// NB: Testing for issue alexcrichton/tokio-signal#38:
|
||||
// signals should not starve based on ordering
|
||||
let first_duplicate_signal =
|
||||
Signal::new(TEST_SIGNAL).expect("failed to register first duplicate signal");
|
||||
let signal = Signal::new(TEST_SIGNAL).expect("failed to register signal");
|
||||
Signal::new(kind).expect("failed to register first duplicate signal");
|
||||
let signal = Signal::new(kind).expect("failed to register signal");
|
||||
let second_duplicate_signal =
|
||||
Signal::new(TEST_SIGNAL).expect("failed to register second duplicate signal");
|
||||
Signal::new(kind).expect("failed to register second duplicate signal");
|
||||
|
||||
drop(first_duplicate_signal);
|
||||
drop(second_duplicate_signal);
|
||||
|
||||
send_signal(TEST_SIGNAL);
|
||||
send_signal(libc::SIGUSR1);
|
||||
let _ = with_timeout(signal.into_future()).await;
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ fn multi_loop() {
|
||||
let sender = sender.clone();
|
||||
thread::spawn(move || {
|
||||
let mut rt = CurrentThreadRuntime::new().unwrap();
|
||||
let signal = Signal::new(libc::SIGHUP).unwrap();
|
||||
let signal = Signal::new(SignalKind::sighup()).unwrap();
|
||||
sender.send(()).unwrap();
|
||||
let _ = run_with_timeout(&mut rt, signal.into_future());
|
||||
})
|
||||
|
@ -9,9 +9,10 @@ use libc;
|
||||
|
||||
#[tokio::test]
|
||||
async fn notify_both() {
|
||||
let signal1 = Signal::new(libc::SIGUSR2).expect("failed to create signal1");
|
||||
let kind = SignalKind::sigusr2();
|
||||
let signal1 = Signal::new(kind).expect("failed to create signal1");
|
||||
|
||||
let signal2 = Signal::new(libc::SIGUSR2).expect("failed to create signal2");
|
||||
let signal2 = Signal::new(kind).expect("failed to create signal2");
|
||||
|
||||
send_signal(libc::SIGUSR2);
|
||||
let _ = with_timeout(future::join(signal1.into_future(), signal2.into_future())).await;
|
||||
|
@ -9,7 +9,7 @@ use libc;
|
||||
|
||||
#[tokio::test]
|
||||
async fn simple() {
|
||||
let signal = Signal::new(libc::SIGUSR1).expect("failed to create signal");
|
||||
let signal = Signal::new(SignalKind::sigusr1()).expect("failed to create signal");
|
||||
|
||||
send_signal(libc::SIGUSR1);
|
||||
|
||||
|
@ -10,7 +10,7 @@ use tokio_timer::Timeout;
|
||||
pub use futures_util::future;
|
||||
pub use futures_util::stream::StreamExt;
|
||||
pub use tokio::runtime::current_thread::{self, Runtime as CurrentThreadRuntime};
|
||||
pub use tokio_signal::unix::Signal;
|
||||
pub use tokio_signal::unix::{Signal, SignalKind};
|
||||
|
||||
pub fn with_timeout<F: Future>(future: F) -> impl Future<Output = F::Output> {
|
||||
Timeout::new(future, Duration::from_secs(1)).map(Result::unwrap)
|
||||
|
@ -9,7 +9,8 @@ use libc;
|
||||
|
||||
#[tokio::test]
|
||||
async fn twice() {
|
||||
let mut signal = Signal::new(libc::SIGUSR1).expect("failed to get signal");
|
||||
let kind = SignalKind::sigusr1();
|
||||
let mut signal = Signal::new(kind).expect("failed to get signal");
|
||||
|
||||
for _ in 0..2 {
|
||||
send_signal(libc::SIGUSR1);
|
||||
|
Loading…
x
Reference in New Issue
Block a user