//! Timer API via signals. //! //! Timer is a POSIX API to create timers and get expiration notifications //! through queued Unix signals, for the current process. This is similar to //! Linux's timerfd mechanism, except that API is specific to Linux and makes //! use of file polling. //! //! For more documentation, please read [timer_create](https://pubs.opengroup.org/onlinepubs/9699919799/functions/timer_create.html). //! //! # Examples //! //! Create an interval timer that signals SIGALARM every 250 milliseconds. //! //! ```no_run //! use nix::sys::signal::{self, SigEvent, SigHandler, SigevNotify, Signal}; //! use nix::sys::timer::{Expiration, Timer, TimerSetTimeFlags}; //! use nix::time::ClockId; //! use std::convert::TryFrom; //! use std::sync::atomic::{AtomicU64, Ordering}; //! use std::thread::yield_now; //! use std::time::Duration; //! //! const SIG: Signal = Signal::SIGALRM; //! static ALARMS: AtomicU64 = AtomicU64::new(0); //! //! extern "C" fn handle_alarm(signal: libc::c_int) { //! let signal = Signal::try_from(signal).unwrap(); //! if signal == SIG { //! ALARMS.fetch_add(1, Ordering::Relaxed); //! } //! } //! //! fn main() { //! let clockid = ClockId::CLOCK_MONOTONIC; //! let sigevent = SigEvent::new(SigevNotify::SigevSignal { //! signal: SIG, //! si_value: 0, //! }); //! //! let mut timer = Timer::new(clockid, sigevent).unwrap(); //! let expiration = Expiration::Interval(Duration::from_millis(250).into()); //! let flags = TimerSetTimeFlags::empty(); //! timer.set(expiration, flags).expect("could not set timer"); //! //! let handler = SigHandler::Handler(handle_alarm); //! unsafe { signal::signal(SIG, handler) }.unwrap(); //! //! loop { //! let alarms = ALARMS.load(Ordering::Relaxed); //! if alarms >= 10 { //! println!("total alarms handled: {}", alarms); //! break; //! } //! yield_now() //! } //! } //! ``` use crate::sys::signal::SigEvent; use crate::sys::time::timer::TimerSpec; pub use crate::sys::time::timer::{Expiration, TimerSetTimeFlags}; use crate::time::ClockId; use crate::{errno::Errno, Result}; use core::mem; /// A Unix signal per-process timer. #[derive(Debug)] #[repr(transparent)] pub struct Timer(libc::timer_t); impl Timer { /// Creates a new timer based on the clock defined by `clockid`. The details /// of the signal and its handler are defined by the passed `sigevent`. #[doc(alias("timer_create"))] pub fn new(clockid: ClockId, mut sigevent: SigEvent) -> Result { let mut timer_id: mem::MaybeUninit = mem::MaybeUninit::uninit(); Errno::result(unsafe { libc::timer_create( clockid.as_raw(), sigevent.as_mut_ptr(), timer_id.as_mut_ptr(), ) }) .map(|_| { // SAFETY: libc::timer_create is responsible for initializing // timer_id. unsafe { Self(timer_id.assume_init()) } }) } /// Set a new alarm on the timer. /// /// # Types of alarm /// /// There are 3 types of alarms you can set: /// /// - one shot: the alarm will trigger once after the specified amount of /// time. /// Example: I want an alarm to go off in 60s and then disable itself. /// /// - interval: the alarm will trigger every specified interval of time. /// Example: I want an alarm to go off every 60s. The alarm will first /// go off 60s after I set it and every 60s after that. The alarm will /// not disable itself. /// /// - interval delayed: the alarm will trigger after a certain amount of /// time and then trigger at a specified interval. /// Example: I want an alarm to go off every 60s but only start in 1h. /// The alarm will first trigger 1h after I set it and then every 60s /// after that. The alarm will not disable itself. /// /// # Relative vs absolute alarm /// /// If you do not set any `TimerSetTimeFlags`, then the `TimeSpec` you pass /// to the `Expiration` you want is relative. If however you want an alarm /// to go off at a certain point in time, you can set `TFD_TIMER_ABSTIME`. /// Then the one shot TimeSpec and the delay TimeSpec of the delayed /// interval are going to be interpreted as absolute. /// /// # Disabling alarms /// /// Note: Only one alarm can be set for any given timer. Setting a new alarm /// actually removes the previous one. /// /// Note: Setting a one shot alarm with a 0s TimeSpec disable the alarm /// altogether. #[doc(alias("timer_settime"))] pub fn set( &mut self, expiration: Expiration, flags: TimerSetTimeFlags, ) -> Result<()> { let timerspec: TimerSpec = expiration.into(); Errno::result(unsafe { libc::timer_settime( self.0, flags.bits(), timerspec.as_ref(), core::ptr::null_mut(), ) }) .map(drop) } /// Get the parameters for the alarm currently set, if any. #[doc(alias("timer_gettime"))] pub fn get(&self) -> Result> { let mut timerspec = TimerSpec::none(); Errno::result(unsafe { libc::timer_gettime(self.0, timerspec.as_mut()) }) .map(|_| { if timerspec.as_ref().it_interval.tv_sec == 0 && timerspec.as_ref().it_interval.tv_nsec == 0 && timerspec.as_ref().it_value.tv_sec == 0 && timerspec.as_ref().it_value.tv_nsec == 0 { None } else { Some(timerspec.into()) } }) } /// Return the number of timers that have overrun /// /// Each timer is able to queue one signal to the process at a time, meaning /// if the signal is not handled before the next expiration the timer has /// 'overrun'. This function returns how many times that has happened to /// this timer, up to `libc::DELAYTIMER_MAX`. If more than the maximum /// number of overruns have happened the return is capped to the maximum. #[doc(alias("timer_getoverrun"))] pub fn overruns(&self) -> i32 { unsafe { libc::timer_getoverrun(self.0) } } } impl Drop for Timer { fn drop(&mut self) { if !std::thread::panicking() { let result = Errno::result(unsafe { libc::timer_delete(self.0) }); if let Err(Errno::EINVAL) = result { panic!("close of Timer encountered EINVAL"); } } } }