summaryrefslogtreecommitdiffstats
path: root/vendor/signal-hook/tests/shutdown.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/signal-hook/tests/shutdown.rs')
-rw-r--r--vendor/signal-hook/tests/shutdown.rs81
1 files changed, 81 insertions, 0 deletions
diff --git a/vendor/signal-hook/tests/shutdown.rs b/vendor/signal-hook/tests/shutdown.rs
new file mode 100644
index 000000000..381fab729
--- /dev/null
+++ b/vendor/signal-hook/tests/shutdown.rs
@@ -0,0 +1,81 @@
+//! Tests for the shutdown.
+//!
+//! The tests work like this:
+//!
+//! * The register an alarm, to fail if anything takes too long (which is very much possible here).
+//! * A fork is done, with the child registering a signal with a NOP and cleanup operation (one or
+//! the other).
+//! * The child puts some kind of infinite loop or sleep inside itself, so it never actually
+//! terminates on the first, but would terminate after the signal.
+
+#![cfg(not(windows))] // Forks don't work on Windows, but windows has the same implementation.
+
+use std::io::Error;
+use std::ptr;
+use std::sync::atomic::AtomicBool;
+use std::sync::Arc;
+use std::thread;
+use std::time::Duration;
+
+use signal_hook::consts::signal::*;
+use signal_hook::flag;
+use signal_hook::low_level;
+
+fn do_test<C: FnOnce()>(child: C) {
+ unsafe {
+ libc::alarm(10); // Time out the test after 10 seconds and get it killed.
+ match libc::fork() {
+ -1 => panic!("Fork failed: {}", Error::last_os_error()),
+ 0 => {
+ child();
+ loop {
+ thread::sleep(Duration::from_secs(1));
+ }
+ }
+ pid => {
+ // Give the child some time to register signals and stuff
+ // We could actually signal that the child is ready by it eg. closing STDOUT, but
+ // this is just a test so we don't really bother.
+ thread::sleep(Duration::from_millis(250));
+ libc::kill(pid, libc::SIGTERM);
+ // Wait a small bit to make sure the signal got delivered.
+ thread::sleep(Duration::from_millis(50));
+ // The child is still running, because the first signal got "handled" by being
+ // ignored.
+ let terminated = libc::waitpid(pid, ptr::null_mut(), libc::WNOHANG);
+ assert_eq!(0, terminated, "Process {} terminated prematurely", pid);
+ // But it terminates on the second attempt (we do block on wait here).
+ libc::kill(pid, libc::SIGTERM);
+ let terminated = libc::waitpid(pid, ptr::null_mut(), 0);
+ assert_eq!(pid, terminated);
+ }
+ }
+ }
+}
+
+/// Use automatic cleanup inside the signal handler to get rid of old signals, the aggressive way.
+#[test]
+fn cleanup_inside_signal() {
+ fn hook() {
+ // Make sure we have some signal handler, not the default.
+ unsafe { low_level::register(SIGTERM, || ()).unwrap() };
+ let shutdown_cond = Arc::new(AtomicBool::new(false));
+ // „disarmed“ shutdown
+ flag::register_conditional_shutdown(SIGTERM, 0, Arc::clone(&shutdown_cond)).unwrap();
+ // But arm at the first SIGTERM
+ flag::register(SIGTERM, shutdown_cond).unwrap();
+ }
+ do_test(hook);
+}
+
+/// Manually remove the signal handler just after receiving the signal but before going into an
+/// infinite loop.
+#[test]
+fn cleanup_after_signal() {
+ fn hook() {
+ let mut signals = signal_hook::iterator::Signals::new(&[libc::SIGTERM]).unwrap();
+ assert_eq!(Some(SIGTERM), signals.into_iter().next());
+ flag::register_conditional_shutdown(SIGTERM, 0, Arc::new(AtomicBool::new(true))).unwrap();
+ }
+ do_test(hook);
+}