• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use nix::sys::signal::{
2     sigaction, SaFlags, SigAction, SigEvent, SigHandler, SigSet, SigevNotify,
3     Signal,
4 };
5 use nix::sys::timer::{Expiration, Timer, TimerSetTimeFlags};
6 use nix::time::ClockId;
7 use std::convert::TryFrom;
8 use std::sync::atomic::{AtomicBool, Ordering};
9 use std::thread;
10 use std::time::{Duration, Instant};
11 
12 const SIG: Signal = Signal::SIGALRM;
13 static ALARM_CALLED: AtomicBool = AtomicBool::new(false);
14 
handle_sigalarm(raw_signal: libc::c_int)15 pub 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]
alarm_fires()23 fn 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