• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Tests for the shutdown.
2 //!
3 //! The tests work like this:
4 //!
5 //! * The register an alarm, to fail if anything takes too long (which is very much possible here).
6 //! * A fork is done, with the child registering a signal with a NOP and cleanup operation (one or
7 //!   the other).
8 //! * The child puts some kind of infinite loop or sleep inside itself, so it never actually
9 //!   terminates on the first, but would terminate after the signal.
10 
11 #![cfg(not(windows))] // Forks don't work on Windows, but windows has the same implementation.
12 
13 use std::io::Error;
14 use std::ptr;
15 use std::sync::atomic::AtomicBool;
16 use std::sync::Arc;
17 use std::thread;
18 use std::time::Duration;
19 
20 use signal_hook::consts::signal::*;
21 use signal_hook::flag;
22 use signal_hook::low_level;
23 
do_test<C: FnOnce()>(child: C)24 fn do_test<C: FnOnce()>(child: C) {
25     unsafe {
26         libc::alarm(10); // Time out the test after 10 seconds and get it killed.
27         match libc::fork() {
28             -1 => panic!("Fork failed: {}", Error::last_os_error()),
29             0 => {
30                 child();
31                 loop {
32                     thread::sleep(Duration::from_secs(1));
33                 }
34             }
35             pid => {
36                 // Give the child some time to register signals and stuff
37                 // We could actually signal that the child is ready by it eg. closing STDOUT, but
38                 // this is just a test so we don't really bother.
39                 thread::sleep(Duration::from_millis(250));
40                 libc::kill(pid, libc::SIGTERM);
41                 // Wait a small bit to make sure the signal got delivered.
42                 thread::sleep(Duration::from_millis(50));
43                 // The child is still running, because the first signal got "handled" by being
44                 // ignored.
45                 let terminated = libc::waitpid(pid, ptr::null_mut(), libc::WNOHANG);
46                 assert_eq!(0, terminated, "Process {} terminated prematurely", pid);
47                 // But it terminates on the second attempt (we do block on wait here).
48                 libc::kill(pid, libc::SIGTERM);
49                 let terminated = libc::waitpid(pid, ptr::null_mut(), 0);
50                 assert_eq!(pid, terminated);
51             }
52         }
53     }
54 }
55 
56 /// Use automatic cleanup inside the signal handler to get rid of old signals, the aggressive way.
57 #[test]
cleanup_inside_signal()58 fn cleanup_inside_signal() {
59     fn hook() {
60         // Make sure we have some signal handler, not the default.
61         unsafe { low_level::register(SIGTERM, || ()).unwrap() };
62         let shutdown_cond = Arc::new(AtomicBool::new(false));
63         // „disarmed“ shutdown
64         flag::register_conditional_shutdown(SIGTERM, 0, Arc::clone(&shutdown_cond)).unwrap();
65         // But arm at the first SIGTERM
66         flag::register(SIGTERM, shutdown_cond).unwrap();
67     }
68     do_test(hook);
69 }
70 
71 /// Manually remove the signal handler just after receiving the signal but before going into an
72 /// infinite loop.
73 #[test]
cleanup_after_signal()74 fn cleanup_after_signal() {
75     fn hook() {
76         let mut signals = signal_hook::iterator::Signals::new(&[libc::SIGTERM]).unwrap();
77         assert_eq!(Some(SIGTERM), signals.into_iter().next());
78         flag::register_conditional_shutdown(SIGTERM, 0, Arc::new(AtomicBool::new(true))).unwrap();
79     }
80     do_test(hook);
81 }
82