summaryrefslogtreecommitdiffstats
path: root/third_party/rust/nix/test/test_timer.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/nix/test/test_timer.rs')
-rw-r--r--third_party/rust/nix/test/test_timer.rs102
1 files changed, 102 insertions, 0 deletions
diff --git a/third_party/rust/nix/test/test_timer.rs b/third_party/rust/nix/test/test_timer.rs
new file mode 100644
index 0000000000..ffd146867b
--- /dev/null
+++ b/third_party/rust/nix/test/test_timer.rs
@@ -0,0 +1,102 @@
+use nix::sys::signal::{
+ sigaction, SaFlags, SigAction, SigEvent, SigHandler, SigSet, SigevNotify,
+ Signal,
+};
+use nix::sys::timer::{Expiration, Timer, TimerSetTimeFlags};
+use nix::time::ClockId;
+use std::convert::TryFrom;
+use std::sync::atomic::{AtomicBool, Ordering};
+use std::thread;
+use std::time::{Duration, Instant};
+
+const SIG: Signal = Signal::SIGALRM;
+static ALARM_CALLED: AtomicBool = AtomicBool::new(false);
+
+pub extern "C" fn handle_sigalarm(raw_signal: libc::c_int) {
+ let signal = Signal::try_from(raw_signal).unwrap();
+ if signal == SIG {
+ ALARM_CALLED.store(true, Ordering::Release);
+ }
+}
+
+#[test]
+fn alarm_fires() {
+ // Avoid interfering with other signal using tests by taking a mutex shared
+ // among other tests in this crate.
+ let _m = crate::SIGNAL_MTX.lock();
+ const TIMER_PERIOD: Duration = Duration::from_millis(100);
+
+ //
+ // Setup
+ //
+
+ // Create a handler for the test signal, `SIG`. The handler is responsible
+ // for flipping `ALARM_CALLED`.
+ let handler = SigHandler::Handler(handle_sigalarm);
+ let signal_action =
+ SigAction::new(handler, SaFlags::SA_RESTART, SigSet::empty());
+ let old_handler = unsafe {
+ sigaction(SIG, &signal_action)
+ .expect("unable to set signal handler for alarm")
+ };
+
+ // Create the timer. We use the monotonic clock here, though any would do
+ // really. The timer is set to fire every 250 milliseconds with no delay for
+ // the initial firing.
+ let clockid = ClockId::CLOCK_MONOTONIC;
+ let sigevent = SigEvent::new(SigevNotify::SigevSignal {
+ signal: SIG,
+ si_value: 0,
+ });
+ let mut timer =
+ Timer::new(clockid, sigevent).expect("failed to create timer");
+ let expiration = Expiration::Interval(TIMER_PERIOD.into());
+ let flags = TimerSetTimeFlags::empty();
+ timer.set(expiration, flags).expect("could not set timer");
+
+ //
+ // Test
+ //
+
+ // Determine that there's still an expiration tracked by the
+ // timer. Depending on when this runs either an `Expiration::Interval` or
+ // `Expiration::IntervalDelayed` will be present. That is, if the timer has
+ // not fired yet we'll get our original `expiration`, else the one that
+ // represents a delay to the next expiration. We're only interested in the
+ // timer still being extant.
+ match timer.get() {
+ Ok(Some(exp)) => assert!(matches!(
+ exp,
+ Expiration::Interval(..) | Expiration::IntervalDelayed(..)
+ )),
+ _ => panic!("timer lost its expiration"),
+ }
+
+ // Wait for 2 firings of the alarm before checking that it has fired and
+ // been handled at least the once. If we wait for 3 seconds and the handler
+ // is never called something has gone sideways and the test fails.
+ let starttime = Instant::now();
+ loop {
+ thread::sleep(2 * TIMER_PERIOD);
+ if ALARM_CALLED.load(Ordering::Acquire) {
+ break;
+ }
+ if starttime.elapsed() > Duration::from_secs(3) {
+ panic!("Timeout waiting for SIGALRM");
+ }
+ }
+
+ // Cleanup:
+ // 1) deregister the OS's timer.
+ // 2) Wait for a full timer period, since POSIX does not require that
+ // disabling the timer will clear pending signals, and on NetBSD at least
+ // it does not.
+ // 2) Replace the old signal handler now that we've completed the test. If
+ // the test fails this process panics, so the fact we might not get here
+ // is okay.
+ drop(timer);
+ thread::sleep(TIMER_PERIOD);
+ unsafe {
+ sigaction(SIG, &old_handler).expect("unable to reset signal handler");
+ }
+}