1use nix::sys::signal::{ 2 sigaction, SaFlags, SigAction, SigEvent, SigHandler, SigSet, SigevNotify, 3 Signal, 4}; 5use nix::sys::timer::{Expiration, Timer, TimerSetTimeFlags}; 6use nix::time::ClockId; 7use std::convert::TryFrom; 8use std::sync::atomic::{AtomicBool, Ordering}; 9use std::thread; 10use std::time::{Duration, Instant}; 11 12const SIG: Signal = Signal::SIGALRM; 13static ALARM_CALLED: AtomicBool = AtomicBool::new(false); 14 15pub extern "C" fn handle_sigalarm(raw_signal: libc::c_int) { 16 let signal = Signal::try_from(raw_signal).unwrap(); 17 if signal == SIG { 18 ALARM_CALLED.store(true, Ordering::Release); 19 } 20} 21 22#[test] 23fn alarm_fires() { 24 // Avoid interfering with other signal using tests by taking a mutex shared 25 // among other tests in this crate. 26 let _m = crate::SIGNAL_MTX.lock(); 27 const TIMER_PERIOD: Duration = Duration::from_millis(100); 28 29 // 30 // Setup 31 // 32 33 // Create a handler for the test signal, `SIG`. The handler is responsible 34 // for flipping `ALARM_CALLED`. 35 let handler = SigHandler::Handler(handle_sigalarm); 36 let signal_action = 37 SigAction::new(handler, SaFlags::SA_RESTART, SigSet::empty()); 38 let old_handler = unsafe { 39 sigaction(SIG, &signal_action) 40 .expect("unable to set signal handler for alarm") 41 }; 42 43 // Create the timer. We use the monotonic clock here, though any would do 44 // really. The timer is set to fire every 250 milliseconds with no delay for 45 // the initial firing. 46 let clockid = ClockId::CLOCK_MONOTONIC; 47 let sigevent = SigEvent::new(SigevNotify::SigevSignal { 48 signal: SIG, 49 si_value: 0, 50 }); 51 let mut timer = 52 Timer::new(clockid, sigevent).expect("failed to create timer"); 53 let expiration = Expiration::Interval(TIMER_PERIOD.into()); 54 let flags = TimerSetTimeFlags::empty(); 55 timer.set(expiration, flags).expect("could not set timer"); 56 57 // 58 // Test 59 // 60 61 // Determine that there's still an expiration tracked by the 62 // timer. Depending on when this runs either an `Expiration::Interval` or 63 // `Expiration::IntervalDelayed` will be present. That is, if the timer has 64 // not fired yet we'll get our original `expiration`, else the one that 65 // represents a delay to the next expiration. We're only interested in the 66 // timer still being extant. 67 match timer.get() { 68 Ok(Some(exp)) => assert!(matches!( 69 exp, 70 Expiration::Interval(..) | Expiration::IntervalDelayed(..) 71 )), 72 _ => panic!("timer lost its expiration"), 73 } 74 75 // Wait for 2 firings of the alarm before checking that it has fired and 76 // been handled at least the once. If we wait for 3 seconds and the handler 77 // is never called something has gone sideways and the test fails. 78 let starttime = Instant::now(); 79 loop { 80 thread::sleep(2 * TIMER_PERIOD); 81 if ALARM_CALLED.load(Ordering::Acquire) { 82 break; 83 } 84 if starttime.elapsed() > Duration::from_secs(3) { 85 panic!("Timeout waiting for SIGALRM"); 86 } 87 } 88 89 // Cleanup: 90 // 1) deregister the OS's timer. 91 // 2) Wait for a full timer period, since POSIX does not require that 92 // disabling the timer will clear pending signals, and on NetBSD at least 93 // it does not. 94 // 2) Replace the old signal handler now that we've completed the test. If 95 // the test fails this process panics, so the fact we might not get here 96 // is okay. 97 drop(timer); 98 thread::sleep(TIMER_PERIOD); 99 unsafe { 100 sigaction(SIG, &old_handler).expect("unable to reset signal handler"); 101 } 102} 103