summaryrefslogtreecommitdiffstats
path: root/vendor/signal-hook/src/flag.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:41:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:41:41 +0000
commit10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87 (patch)
treebdffd5d80c26cf4a7a518281a204be1ace85b4c1 /vendor/signal-hook/src/flag.rs
parentReleasing progress-linux version 1.70.0+dfsg1-9~progress7.99u1. (diff)
downloadrustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.tar.xz
rustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.zip
Merging upstream version 1.70.0+dfsg2.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/signal-hook/src/flag.rs')
-rw-r--r--vendor/signal-hook/src/flag.rs283
1 files changed, 283 insertions, 0 deletions
diff --git a/vendor/signal-hook/src/flag.rs b/vendor/signal-hook/src/flag.rs
new file mode 100644
index 000000000..8573fcd7f
--- /dev/null
+++ b/vendor/signal-hook/src/flag.rs
@@ -0,0 +1,283 @@
+//! Module for actions setting flags.
+//!
+//! This contains helper functions to set flags whenever a signal happens. The flags are atomic
+//! bools or numbers and the library manipulates them with the `SeqCst` ordering, in case someone
+//! cares about relative order to some *other* atomic variables. If you don't care about the
+//! relative order, you are free to use `Ordering::Relaxed` when reading and resetting the flags.
+//!
+//! # When to use
+//!
+//! The flags in this module allow for polling if a signal arrived since the previous poll. The do
+//! not allow blocking until something arrives.
+//!
+//! Therefore, the natural way to use them is in applications that have some kind of iterative work
+//! with both some upper and lower time limit on one iteration. If one iteration could block for
+//! arbitrary time, the handling of the signal would be postponed for a long time. If the iteration
+//! didn't block at all, the checking for the signal would turn into a busy-loop.
+//!
+//! If what you need is blocking until a signal comes, you might find better tools in the
+//! [`pipe`][crate::low_level::pipe] and [`iterator`][crate::iterator] modules.
+//!
+//! # Examples
+//!
+//! Doing something until terminated. This also knows by which signal it was terminated. In case
+//! multiple termination signals arrive before it is handled, it recognizes the last one.
+//!
+//! ```rust
+//! use std::io::Error;
+//! use std::sync::Arc;
+//! use std::sync::atomic::{AtomicUsize, Ordering};
+//!
+//! use signal_hook::consts::signal::*;
+//! use signal_hook::flag as signal_flag;
+//!
+//! fn main() -> Result<(), Error> {
+//! let term = Arc::new(AtomicUsize::new(0));
+//! const SIGTERM_U: usize = SIGTERM as usize;
+//! const SIGINT_U: usize = SIGINT as usize;
+//! # #[cfg(not(windows))]
+//! const SIGQUIT_U: usize = SIGQUIT as usize;
+//! signal_flag::register_usize(SIGTERM, Arc::clone(&term), SIGTERM_U)?;
+//! signal_flag::register_usize(SIGINT, Arc::clone(&term), SIGINT_U)?;
+//! # #[cfg(not(windows))]
+//! signal_flag::register_usize(SIGQUIT, Arc::clone(&term), SIGQUIT_U)?;
+//!
+//! # // Hack to terminate the example when run as a doc-test.
+//! # term.store(SIGTERM_U, Ordering::Relaxed);
+//! loop {
+//! match term.load(Ordering::Relaxed) {
+//! 0 => {
+//! // Do some useful stuff here
+//! }
+//! SIGTERM_U => {
+//! eprintln!("Terminating on the TERM signal");
+//! break;
+//! }
+//! SIGINT_U => {
+//! eprintln!("Terminating on the INT signal");
+//! break;
+//! }
+//! # #[cfg(not(windows))]
+//! SIGQUIT_U => {
+//! eprintln!("Terminating on the QUIT signal");
+//! break;
+//! }
+//! _ => unreachable!(),
+//! }
+//! }
+//!
+//! Ok(())
+//! }
+//! ```
+//!
+//! Sending a signal to self and seeing it arrived (not of a practical usage on itself):
+//!
+//! ```rust
+//! use std::io::Error;
+//! use std::sync::Arc;
+//! use std::sync::atomic::{AtomicBool, Ordering};
+//! use std::thread;
+//! use std::time::Duration;
+//!
+//! use signal_hook::consts::signal::*;
+//! use signal_hook::low_level::raise;
+//!
+//! fn main() -> Result<(), Error> {
+//! let got = Arc::new(AtomicBool::new(false));
+//! # #[cfg(not(windows))]
+//! signal_hook::flag::register(SIGUSR1, Arc::clone(&got))?;
+//! # #[cfg(windows)]
+//! # signal_hook::flag::register(SIGTERM, Arc::clone(&got))?;
+//! # #[cfg(not(windows))]
+//! raise(SIGUSR1).unwrap();
+//! # #[cfg(windows)]
+//! # raise(SIGTERM).unwrap();
+//! // A sleep here, because it could run the signal handler in another thread and we may not
+//! // see the flag right away. This is still a hack and not guaranteed to work, it is just an
+//! // example!
+//! thread::sleep(Duration::from_secs(1));
+//! assert!(got.load(Ordering::Relaxed));
+//! Ok(())
+//! }
+//! ```
+//!
+//! Reloading a configuration on `SIGHUP` (which is a common behaviour of many UNIX daemons,
+//! together with reopening the log file).
+//!
+//! ```rust
+//! use std::io::Error;
+//! use std::sync::Arc;
+//! use std::sync::atomic::{AtomicBool, Ordering};
+//!
+//! use signal_hook::consts::signal::*;
+//! use signal_hook::flag as signal_flag;
+//!
+//! fn main() -> Result<(), Error> {
+//! // We start with true, to load the configuration in the very first iteration too.
+//! let reload = Arc::new(AtomicBool::new(true));
+//! let term = Arc::new(AtomicBool::new(false));
+//! # #[cfg(not(windows))]
+//! signal_flag::register(SIGHUP, Arc::clone(&reload))?;
+//! signal_flag::register(SIGINT, Arc::clone(&term))?;
+//! signal_flag::register(SIGTERM, Arc::clone(&term))?;
+//! # #[cfg(not(windows))]
+//! signal_flag::register(SIGQUIT, Arc::clone(&term))?;
+//! while !term.load(Ordering::Relaxed) {
+//! // Using swap here, not load, to reset it back to false once it is reloaded.
+//! if reload.swap(false, Ordering::Relaxed) {
+//! // Reload the config here
+//! #
+//! # // Hiden hack to make the example terminate when run as doc-test. Not part of the
+//! # // real code.
+//! # term.store(true, Ordering::Relaxed);
+//! }
+//! // Serve one request
+//! }
+//! Ok(())
+//! }
+//! ```
+
+use std::io::Error;
+use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
+use std::sync::Arc;
+
+use libc::{c_int, EINVAL};
+
+use crate::{low_level, SigId};
+
+/// Registers an action to set the flag to `true` whenever the given signal arrives.
+///
+/// # Panics
+///
+/// If the signal is one of the forbidden.
+pub fn register(signal: c_int, flag: Arc<AtomicBool>) -> Result<SigId, Error> {
+ // We use SeqCst for two reasons:
+ // * Signals should not come very often, so the performance does not really matter.
+ // * We promise the order of actions, but setting different atomics with Relaxed or similar
+ // would not guarantee the effective order.
+ unsafe { low_level::register(signal, move || flag.store(true, Ordering::SeqCst)) }
+}
+
+/// Registers an action to set the flag to the given value whenever the signal arrives.
+pub fn register_usize(signal: c_int, flag: Arc<AtomicUsize>, value: usize) -> Result<SigId, Error> {
+ unsafe { low_level::register(signal, move || flag.store(value, Ordering::SeqCst)) }
+}
+
+/// Terminate the application on a signal if the given condition is true.
+///
+/// This can be used for different use cases. One of them (with the condition being always true) is
+/// just unconditionally terminate on the given signal.
+///
+/// Another is being able to turn on and off the behaviour by the shared flag.
+///
+/// The last one is handling double CTRL+C ‒ if the user presses CTRL+C, we would like to start a
+/// graceful shutdown. But if anything ever gets stuck in the shutdown, second CTRL+C (or other
+/// such termination signal) should terminate the application without further delay.
+///
+/// To do that, one can combine this with [`register`]. On the first run, the flag is `false` and
+/// this doesn't terminate. But then the flag is set to true during the first run and „arms“ the
+/// shutdown on the second run. Note that it matters in which order the actions are registered (the
+/// shutdown must go first). And yes, this also allows asking the user „Do you want to terminate“
+/// and disarming the abrupt shutdown if the user answers „No“.
+///
+/// # Panics
+///
+/// If the signal is one of the forbidden.
+pub fn register_conditional_shutdown(
+ signal: c_int,
+ status: c_int,
+ condition: Arc<AtomicBool>,
+) -> Result<SigId, Error> {
+ let action = move || {
+ if condition.load(Ordering::SeqCst) {
+ low_level::exit(status);
+ }
+ };
+ unsafe { low_level::register(signal, action) }
+}
+
+/// Conditionally runs an emulation of the default action on the given signal.
+///
+/// If the provided condition is true at the time of invoking the signal handler, the equivalent of
+/// the default action of the given signal is run. It is a bit similar to
+/// [`register_conditional_shutdown`], except that it doesn't terminate for non-termination
+/// signals, it runs their default handler.
+///
+/// # Panics
+///
+/// If the signal is one of the forbidden
+///
+/// # Errors
+///
+/// Similarly to the [`emulate_default_handler`][low_level::emulate_default_handler] function, this
+/// one looks the signal up in a table. If it is unknown, an error is returned.
+///
+/// Additionally to that, any errors that can be caused by a registration of a handler can happen
+/// too.
+pub fn register_conditional_default(
+ signal: c_int,
+ condition: Arc<AtomicBool>,
+) -> Result<SigId, Error> {
+ // Verify we know about this particular signal.
+ low_level::signal_name(signal).ok_or_else(|| Error::from_raw_os_error(EINVAL))?;
+ let action = move || {
+ if condition.load(Ordering::SeqCst) {
+ let _ = low_level::emulate_default_handler(signal);
+ }
+ };
+ unsafe { low_level::register(signal, action) }
+}
+
+#[cfg(test)]
+mod tests {
+ use std::sync::atomic;
+ use std::time::{Duration, Instant};
+
+ use super::*;
+ use crate::consts::signal::*;
+
+ fn self_signal() {
+ #[cfg(not(windows))]
+ const SIG: c_int = SIGUSR1;
+ #[cfg(windows)]
+ const SIG: c_int = SIGTERM;
+ crate::low_level::raise(SIG).unwrap();
+ }
+
+ fn wait_flag(flag: &AtomicBool) -> bool {
+ let start = Instant::now();
+ while !flag.load(Ordering::Relaxed) {
+ // Replaced by hint::spin_loop, but we want to support older compiler
+ #[allow(deprecated)]
+ atomic::spin_loop_hint();
+ if Instant::now() - start > Duration::from_secs(1) {
+ // We reached a timeout and nothing happened yet.
+ // In theory, using timeouts for thread-synchronization tests is wrong, but a
+ // second should be enough in practice.
+ return false;
+ }
+ }
+ true
+ }
+
+ #[test]
+ fn register_unregister() {
+ // When we register the action, it is active.
+ let flag = Arc::new(AtomicBool::new(false));
+ #[cfg(not(windows))]
+ let signal = register(SIGUSR1, Arc::clone(&flag)).unwrap();
+ #[cfg(windows)]
+ let signal = register(crate::SIGTERM, Arc::clone(&flag)).unwrap();
+ self_signal();
+ assert!(wait_flag(&flag));
+ // But stops working after it is unregistered.
+ assert!(crate::low_level::unregister(signal));
+ flag.store(false, Ordering::Relaxed);
+ self_signal();
+ assert!(!wait_flag(&flag));
+ // And the unregistration actually dropped its copy of the Arc
+ assert_eq!(1, Arc::strong_count(&flag));
+ }
+
+ // The shutdown is tested in tests/shutdown.rs
+}