| //! 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); | 
 | } |