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