diff options
author | Ivan Petkov <ivanppetkov@gmail.com> | 2019-08-13 21:07:22 -0700 |
---|---|---|
committer | Carl Lerche <me@carllerche.com> | 2019-08-13 21:07:22 -0700 |
commit | 338b37884a73782fe72d4e9a0616010bcd12180e (patch) | |
tree | a56fde0234b07cd418cc6da3b54770435302561b | |
parent | 513326e01da05261fbb05f722836c7cb80552773 (diff) |
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.
-rw-r--r-- | tokio-process/src/unix/mod.rs | 4 | ||||
-rw-r--r-- | tokio-signal/examples/multiple.rs | 17 | ||||
-rw-r--r-- | tokio-signal/examples/sighup-example.rs | 4 | ||||
-rw-r--r-- | tokio-signal/src/lib.rs | 4 | ||||
-rw-r--r-- | tokio-signal/src/unix.rs | 166 | ||||
-rw-r--r-- | tokio-signal/tests/drop_multi_loop.rs | 12 | ||||
-rw-r--r-- | tokio-signal/tests/drop_then_get_a_signal.rs | 5 | ||||
-rw-r--r-- | tokio-signal/tests/dropping_does_not_deregister_other_instances.rs | 12 | ||||
-rw-r--r-- | tokio-signal/tests/multi_loop.rs | 2 | ||||
-rw-r--r-- | tokio-signal/tests/notify_both.rs | 5 | ||||
-rw-r--r-- | tokio-signal/tests/simple.rs | 2 | ||||
-rw-r--r-- | tokio-signal/tests/support.rs | 2 | ||||
-rw-r--r-- | tokio-signal/tests/twice.rs | 3 |
13 files changed, 169 insertions, 69 deletions
diff --git a/tokio-process/src/unix/mod.rs b/tokio-process/src/unix/mod.rs index 55497cad..f6d6b67a 100644 --- a/tokio-process/src/unix/mod.rs +++ b/tokio-process/src/unix/mod.rs @@ -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 { diff --git a/tokio-signal/examples/multiple.rs b/tokio-signal/examples/multiple.rs index 5157fb1b..7732d221 100644 --- a/tokio-signal/examples/multiple.rs +++ b/tokio-signal/examples/multiple.rs @@ -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); } } diff --git a/tokio-signal/examples/sighup-example.rs b/tokio-signal/examples/sighup-example.rs index 6ee46a9d..ed4b04f5 100644 --- a/tokio-signal/examples/sighup-example.rs +++ b/tokio-signal/examples/sighup-example.rs @@ -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!( diff --git a/tokio-signal/src/lib.rs b/tokio-signal/src/lib.rs index 36f265cf..c12ed535 100644 --- a/tokio-signal/src/lib.rs +++ b/tokio-signal/src/lib.rs @@ -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; diff --git a/tokio-signal/src/unix.rs b/tokio-signal/src/unix.rs index 65cdc99f..ce7723e0 100644 --- a/tokio-signal/src/unix.rs +++ b/tokio-signal/src/unix.rs @@ -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) } } diff --git a/tokio-signal/tests/drop_multi_loop.rs b/tokio-signal/tests/drop_multi_loop.rs index fa04bc07..c83cf72d 100644 --- a/tokio-signal/tests/drop_multi_loop.rs +++ b/tokio-signal/tests/drop_multi_loop.rs @@ -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()); } diff --git a/tokio-signal/tests/drop_then_get_a_signal.rs b/tokio-signal/tests/drop_then_get_a_signal.rs index 0573d6b9..3782474b 100644 --- a/tokio-signal/tests/drop_then_get_a_signal.rs +++ b/tokio-signal/tests/drop_then_get_a_signal.rs @@ -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; } diff --git a/tokio-signal/tests/dropping_does_not_deregister_other_instances.rs b/tokio-signal/tests/dropping_does_not_deregister_other_instances.rs index 2b80d114..277a3c91 100644 --- a/tokio-signal/tests/dropping_does_not_deregister_other_instances.rs +++ b/tokio-signal/tests/dropping_does_not_deregister_other_instances.rs @@ -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; } diff --git a/tokio-signal/tests/multi_loop.rs b/tokio-signal/tests/multi_loop.rs index 2fbf4415..f8d32d37 100644 --- a/tokio-signal/tests/multi_loop.rs +++ b/tokio-signal/tests/multi_loop.rs @@ -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()); }) diff --git a/tokio-signal/tests/notify_both.rs b/tokio-signal/tests/notify_both.rs index 9927bf05..5237b9ad 100644 --- a/tokio-signal/tests/notify_both.rs +++ b/tokio-signal/tests/notify_both.rs @@ -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; diff --git a/tokio-signal/tests/simple.rs b/tokio-signal/tests/simple.rs index 9eafe596..39cdf54a 100644 --- a/tokio-signal/tests/simple.rs +++ b/tokio-signal/tests/simple.rs @@ -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); diff --git a/tokio-signal/tests/support.rs b/tokio-signal/tests/support.rs index ee6387a5..1d539f1d 100644 --- a/tokio-signal/tests/support.rs +++ b/tokio-signal/tests/support.rs @@ -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) diff --git a/tokio-signal/tests/twice.rs b/tokio-signal/tests/twice.rs index edef0d0c..3a52e411 100644 --- a/tokio-signal/tests/twice.rs +++ b/tokio-signal/tests/twice.rs @@ -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); |