• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 //! ```
58 use crate::sys::signal::SigEvent;
59 use crate::sys::time::timer::TimerSpec;
60 pub use crate::sys::time::timer::{Expiration, TimerSetTimeFlags};
61 use crate::time::ClockId;
62 use crate::{errno::Errno, Result};
63 use core::mem;
64 
65 /// A Unix signal per-process timer.
66 #[derive(Debug)]
67 #[repr(transparent)]
68 pub struct Timer(libc::timer_t);
69 
70 impl 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"))]
new(clockid: ClockId, mut sigevent: SigEvent) -> Result<Self>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"))]
set( &mut self, expiration: Expiration, flags: TimerSetTimeFlags, ) -> Result<()>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"))]
get(&self) -> Result<Option<Expiration>>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"))]
overruns(&self) -> i32173     pub fn overruns(&self) -> i32 {
174         unsafe { libc::timer_getoverrun(self.0) }
175     }
176 }
177 
178 impl Drop for Timer {
drop(&mut self)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 }
188