nix/sys/timer.rs
1//! Timer API via signals.
2//!
3//! Timer is a POSIX API to create timers and get expiration notifications
4//! through queued Unix signals, for the current process. This is similar to
5//! Linux's timerfd mechanism, except that API is specific to Linux and makes
6//! use of file polling.
7//!
8//! For more documentation, please read [timer_create](https://pubs.opengroup.org/onlinepubs/9699919799/functions/timer_create.html).
9//!
10//! # Examples
11//!
12//! Create an interval timer that signals SIGALARM every 250 milliseconds.
13//!
14//! ```no_run
15//! use nix::sys::signal::{self, SigEvent, SigHandler, SigevNotify, Signal};
16//! use nix::sys::timer::{Expiration, Timer, TimerSetTimeFlags};
17//! use nix::time::ClockId;
18//! use std::convert::TryFrom;
19//! use std::sync::atomic::{AtomicU64, Ordering};
20//! use std::thread::yield_now;
21//! use std::time::Duration;
22//!
23//! const SIG: Signal = Signal::SIGALRM;
24//! static ALARMS: AtomicU64 = AtomicU64::new(0);
25//!
26//! extern "C" fn handle_alarm(signal: libc::c_int) {
27//! let signal = Signal::try_from(signal).unwrap();
28//! if signal == SIG {
29//! ALARMS.fetch_add(1, Ordering::Relaxed);
30//! }
31//! }
32//!
33//! fn main() {
34//! let clockid = ClockId::CLOCK_MONOTONIC;
35//! let sigevent = SigEvent::new(SigevNotify::SigevSignal {
36//! signal: SIG,
37//! si_value: 0,
38//! });
39//!
40//! let mut timer = Timer::new(clockid, sigevent).unwrap();
41//! let expiration = Expiration::Interval(Duration::from_millis(250).into());
42//! let flags = TimerSetTimeFlags::empty();
43//! timer.set(expiration, flags).expect("could not set timer");
44//!
45//! let handler = SigHandler::Handler(handle_alarm);
46//! unsafe { signal::signal(SIG, handler) }.unwrap();
47//!
48//! loop {
49//! let alarms = ALARMS.load(Ordering::Relaxed);
50//! if alarms >= 10 {
51//! println!("total alarms handled: {}", alarms);
52//! break;
53//! }
54//! yield_now()
55//! }
56//! }
57//! ```
58use crate::sys::signal::SigEvent;
59use crate::sys::time::timer::TimerSpec;
60pub use crate::sys::time::timer::{Expiration, TimerSetTimeFlags};
61use crate::time::ClockId;
62use crate::{errno::Errno, Result};
63use core::mem;
64
65/// A Unix signal per-process timer.
66#[derive(Debug)]
67#[repr(transparent)]
68pub struct Timer(libc::timer_t);
69
70impl Timer {
71 /// Creates a new timer based on the clock defined by `clockid`. The details
72 /// of the signal and its handler are defined by the passed `sigevent`.
73 #[doc(alias("timer_create"))]
74 pub fn new(clockid: ClockId, mut sigevent: SigEvent) -> Result<Self> {
75 let mut timer_id: mem::MaybeUninit<libc::timer_t> =
76 mem::MaybeUninit::uninit();
77 Errno::result(unsafe {
78 libc::timer_create(
79 clockid.as_raw(),
80 sigevent.as_mut_ptr(),
81 timer_id.as_mut_ptr(),
82 )
83 })
84 .map(|_| {
85 // SAFETY: libc::timer_create is responsible for initializing
86 // timer_id.
87 unsafe { Self(timer_id.assume_init()) }
88 })
89 }
90
91 /// Set a new alarm on the timer.
92 ///
93 /// # Types of alarm
94 ///
95 /// There are 3 types of alarms you can set:
96 ///
97 /// - one shot: the alarm will trigger once after the specified amount of
98 /// time.
99 /// Example: I want an alarm to go off in 60s and then disable itself.
100 ///
101 /// - interval: the alarm will trigger every specified interval of time.
102 /// Example: I want an alarm to go off every 60s. The alarm will first
103 /// go off 60s after I set it and every 60s after that. The alarm will
104 /// not disable itself.
105 ///
106 /// - interval delayed: the alarm will trigger after a certain amount of
107 /// time and then trigger at a specified interval.
108 /// Example: I want an alarm to go off every 60s but only start in 1h.
109 /// The alarm will first trigger 1h after I set it and then every 60s
110 /// after that. The alarm will not disable itself.
111 ///
112 /// # Relative vs absolute alarm
113 ///
114 /// If you do not set any `TimerSetTimeFlags`, then the `TimeSpec` you pass
115 /// to the `Expiration` you want is relative. If however you want an alarm
116 /// to go off at a certain point in time, you can set `TFD_TIMER_ABSTIME`.
117 /// Then the one shot TimeSpec and the delay TimeSpec of the delayed
118 /// interval are going to be interpreted as absolute.
119 ///
120 /// # Disabling alarms
121 ///
122 /// Note: Only one alarm can be set for any given timer. Setting a new alarm
123 /// actually removes the previous one.
124 ///
125 /// Note: Setting a one shot alarm with a 0s TimeSpec disable the alarm
126 /// altogether.
127 #[doc(alias("timer_settime"))]
128 pub fn set(
129 &mut self,
130 expiration: Expiration,
131 flags: TimerSetTimeFlags,
132 ) -> Result<()> {
133 let timerspec: TimerSpec = expiration.into();
134 Errno::result(unsafe {
135 libc::timer_settime(
136 self.0,
137 flags.bits(),
138 timerspec.as_ref(),
139 core::ptr::null_mut(),
140 )
141 })
142 .map(drop)
143 }
144
145 /// Get the parameters for the alarm currently set, if any.
146 #[doc(alias("timer_gettime"))]
147 pub fn get(&self) -> Result<Option<Expiration>> {
148 let mut timerspec = TimerSpec::none();
149 Errno::result(unsafe {
150 libc::timer_gettime(self.0, timerspec.as_mut())
151 })
152 .map(|_| {
153 if timerspec.as_ref().it_interval.tv_sec == 0
154 && timerspec.as_ref().it_interval.tv_nsec == 0
155 && timerspec.as_ref().it_value.tv_sec == 0
156 && timerspec.as_ref().it_value.tv_nsec == 0
157 {
158 None
159 } else {
160 Some(timerspec.into())
161 }
162 })
163 }
164
165 /// Return the number of timers that have overrun
166 ///
167 /// Each timer is able to queue one signal to the process at a time, meaning
168 /// if the signal is not handled before the next expiration the timer has
169 /// 'overrun'. This function returns how many times that has happened to
170 /// this timer, up to `libc::DELAYTIMER_MAX`. If more than the maximum
171 /// number of overruns have happened the return is capped to the maximum.
172 #[doc(alias("timer_getoverrun"))]
173 pub fn overruns(&self) -> i32 {
174 unsafe { libc::timer_getoverrun(self.0) }
175 }
176}
177
178impl Drop for Timer {
179 fn drop(&mut self) {
180 if !std::thread::panicking() {
181 let result = Errno::result(unsafe { libc::timer_delete(self.0) });
182 if let Err(Errno::EINVAL) = result {
183 panic!("close of Timer encountered EINVAL");
184 }
185 }
186 }
187}