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