13da5c369Sopenharmony_ci//! Timer API via signals. 23da5c369Sopenharmony_ci//! 33da5c369Sopenharmony_ci//! Timer is a POSIX API to create timers and get expiration notifications 43da5c369Sopenharmony_ci//! through queued Unix signals, for the current process. This is similar to 53da5c369Sopenharmony_ci//! Linux's timerfd mechanism, except that API is specific to Linux and makes 63da5c369Sopenharmony_ci//! use of file polling. 73da5c369Sopenharmony_ci//! 83da5c369Sopenharmony_ci//! For more documentation, please read [timer_create](https://pubs.opengroup.org/onlinepubs/9699919799/functions/timer_create.html). 93da5c369Sopenharmony_ci//! 103da5c369Sopenharmony_ci//! # Examples 113da5c369Sopenharmony_ci//! 123da5c369Sopenharmony_ci//! Create an interval timer that signals SIGALARM every 250 milliseconds. 133da5c369Sopenharmony_ci//! 143da5c369Sopenharmony_ci//! ```no_run 153da5c369Sopenharmony_ci//! use nix::sys::signal::{self, SigEvent, SigHandler, SigevNotify, Signal}; 163da5c369Sopenharmony_ci//! use nix::sys::timer::{Expiration, Timer, TimerSetTimeFlags}; 173da5c369Sopenharmony_ci//! use nix::time::ClockId; 183da5c369Sopenharmony_ci//! use std::convert::TryFrom; 193da5c369Sopenharmony_ci//! use std::sync::atomic::{AtomicU64, Ordering}; 203da5c369Sopenharmony_ci//! use std::thread::yield_now; 213da5c369Sopenharmony_ci//! use std::time::Duration; 223da5c369Sopenharmony_ci//! 233da5c369Sopenharmony_ci//! const SIG: Signal = Signal::SIGALRM; 243da5c369Sopenharmony_ci//! static ALARMS: AtomicU64 = AtomicU64::new(0); 253da5c369Sopenharmony_ci//! 263da5c369Sopenharmony_ci//! extern "C" fn handle_alarm(signal: libc::c_int) { 273da5c369Sopenharmony_ci//! let signal = Signal::try_from(signal).unwrap(); 283da5c369Sopenharmony_ci//! if signal == SIG { 293da5c369Sopenharmony_ci//! ALARMS.fetch_add(1, Ordering::Relaxed); 303da5c369Sopenharmony_ci//! } 313da5c369Sopenharmony_ci//! } 323da5c369Sopenharmony_ci//! 333da5c369Sopenharmony_ci//! fn main() { 343da5c369Sopenharmony_ci//! let clockid = ClockId::CLOCK_MONOTONIC; 353da5c369Sopenharmony_ci//! let sigevent = SigEvent::new(SigevNotify::SigevSignal { 363da5c369Sopenharmony_ci//! signal: SIG, 373da5c369Sopenharmony_ci//! si_value: 0, 383da5c369Sopenharmony_ci//! }); 393da5c369Sopenharmony_ci//! 403da5c369Sopenharmony_ci//! let mut timer = Timer::new(clockid, sigevent).unwrap(); 413da5c369Sopenharmony_ci//! let expiration = Expiration::Interval(Duration::from_millis(250).into()); 423da5c369Sopenharmony_ci//! let flags = TimerSetTimeFlags::empty(); 433da5c369Sopenharmony_ci//! timer.set(expiration, flags).expect("could not set timer"); 443da5c369Sopenharmony_ci//! 453da5c369Sopenharmony_ci//! let handler = SigHandler::Handler(handle_alarm); 463da5c369Sopenharmony_ci//! unsafe { signal::signal(SIG, handler) }.unwrap(); 473da5c369Sopenharmony_ci//! 483da5c369Sopenharmony_ci//! loop { 493da5c369Sopenharmony_ci//! let alarms = ALARMS.load(Ordering::Relaxed); 503da5c369Sopenharmony_ci//! if alarms >= 10 { 513da5c369Sopenharmony_ci//! println!("total alarms handled: {}", alarms); 523da5c369Sopenharmony_ci//! break; 533da5c369Sopenharmony_ci//! } 543da5c369Sopenharmony_ci//! yield_now() 553da5c369Sopenharmony_ci//! } 563da5c369Sopenharmony_ci//! } 573da5c369Sopenharmony_ci//! ``` 583da5c369Sopenharmony_ciuse crate::sys::signal::SigEvent; 593da5c369Sopenharmony_ciuse crate::sys::time::timer::TimerSpec; 603da5c369Sopenharmony_cipub use crate::sys::time::timer::{Expiration, TimerSetTimeFlags}; 613da5c369Sopenharmony_ciuse crate::time::ClockId; 623da5c369Sopenharmony_ciuse crate::{errno::Errno, Result}; 633da5c369Sopenharmony_ciuse core::mem; 643da5c369Sopenharmony_ci 653da5c369Sopenharmony_ci/// A Unix signal per-process timer. 663da5c369Sopenharmony_ci#[derive(Debug)] 673da5c369Sopenharmony_ci#[repr(transparent)] 683da5c369Sopenharmony_cipub struct Timer(libc::timer_t); 693da5c369Sopenharmony_ci 703da5c369Sopenharmony_ciimpl Timer { 713da5c369Sopenharmony_ci /// Creates a new timer based on the clock defined by `clockid`. The details 723da5c369Sopenharmony_ci /// of the signal and its handler are defined by the passed `sigevent`. 733da5c369Sopenharmony_ci #[doc(alias("timer_create"))] 743da5c369Sopenharmony_ci pub fn new(clockid: ClockId, mut sigevent: SigEvent) -> Result<Self> { 753da5c369Sopenharmony_ci let mut timer_id: mem::MaybeUninit<libc::timer_t> = 763da5c369Sopenharmony_ci mem::MaybeUninit::uninit(); 773da5c369Sopenharmony_ci Errno::result(unsafe { 783da5c369Sopenharmony_ci libc::timer_create( 793da5c369Sopenharmony_ci clockid.as_raw(), 803da5c369Sopenharmony_ci sigevent.as_mut_ptr(), 813da5c369Sopenharmony_ci timer_id.as_mut_ptr(), 823da5c369Sopenharmony_ci ) 833da5c369Sopenharmony_ci }) 843da5c369Sopenharmony_ci .map(|_| { 853da5c369Sopenharmony_ci // SAFETY: libc::timer_create is responsible for initializing 863da5c369Sopenharmony_ci // timer_id. 873da5c369Sopenharmony_ci unsafe { Self(timer_id.assume_init()) } 883da5c369Sopenharmony_ci }) 893da5c369Sopenharmony_ci } 903da5c369Sopenharmony_ci 913da5c369Sopenharmony_ci /// Set a new alarm on the timer. 923da5c369Sopenharmony_ci /// 933da5c369Sopenharmony_ci /// # Types of alarm 943da5c369Sopenharmony_ci /// 953da5c369Sopenharmony_ci /// There are 3 types of alarms you can set: 963da5c369Sopenharmony_ci /// 973da5c369Sopenharmony_ci /// - one shot: the alarm will trigger once after the specified amount of 983da5c369Sopenharmony_ci /// time. 993da5c369Sopenharmony_ci /// Example: I want an alarm to go off in 60s and then disable itself. 1003da5c369Sopenharmony_ci /// 1013da5c369Sopenharmony_ci /// - interval: the alarm will trigger every specified interval of time. 1023da5c369Sopenharmony_ci /// Example: I want an alarm to go off every 60s. The alarm will first 1033da5c369Sopenharmony_ci /// go off 60s after I set it and every 60s after that. The alarm will 1043da5c369Sopenharmony_ci /// not disable itself. 1053da5c369Sopenharmony_ci /// 1063da5c369Sopenharmony_ci /// - interval delayed: the alarm will trigger after a certain amount of 1073da5c369Sopenharmony_ci /// time and then trigger at a specified interval. 1083da5c369Sopenharmony_ci /// Example: I want an alarm to go off every 60s but only start in 1h. 1093da5c369Sopenharmony_ci /// The alarm will first trigger 1h after I set it and then every 60s 1103da5c369Sopenharmony_ci /// after that. The alarm will not disable itself. 1113da5c369Sopenharmony_ci /// 1123da5c369Sopenharmony_ci /// # Relative vs absolute alarm 1133da5c369Sopenharmony_ci /// 1143da5c369Sopenharmony_ci /// If you do not set any `TimerSetTimeFlags`, then the `TimeSpec` you pass 1153da5c369Sopenharmony_ci /// to the `Expiration` you want is relative. If however you want an alarm 1163da5c369Sopenharmony_ci /// to go off at a certain point in time, you can set `TFD_TIMER_ABSTIME`. 1173da5c369Sopenharmony_ci /// Then the one shot TimeSpec and the delay TimeSpec of the delayed 1183da5c369Sopenharmony_ci /// interval are going to be interpreted as absolute. 1193da5c369Sopenharmony_ci /// 1203da5c369Sopenharmony_ci /// # Disabling alarms 1213da5c369Sopenharmony_ci /// 1223da5c369Sopenharmony_ci /// Note: Only one alarm can be set for any given timer. Setting a new alarm 1233da5c369Sopenharmony_ci /// actually removes the previous one. 1243da5c369Sopenharmony_ci /// 1253da5c369Sopenharmony_ci /// Note: Setting a one shot alarm with a 0s TimeSpec disable the alarm 1263da5c369Sopenharmony_ci /// altogether. 1273da5c369Sopenharmony_ci #[doc(alias("timer_settime"))] 1283da5c369Sopenharmony_ci pub fn set( 1293da5c369Sopenharmony_ci &mut self, 1303da5c369Sopenharmony_ci expiration: Expiration, 1313da5c369Sopenharmony_ci flags: TimerSetTimeFlags, 1323da5c369Sopenharmony_ci ) -> Result<()> { 1333da5c369Sopenharmony_ci let timerspec: TimerSpec = expiration.into(); 1343da5c369Sopenharmony_ci Errno::result(unsafe { 1353da5c369Sopenharmony_ci libc::timer_settime( 1363da5c369Sopenharmony_ci self.0, 1373da5c369Sopenharmony_ci flags.bits(), 1383da5c369Sopenharmony_ci timerspec.as_ref(), 1393da5c369Sopenharmony_ci core::ptr::null_mut(), 1403da5c369Sopenharmony_ci ) 1413da5c369Sopenharmony_ci }) 1423da5c369Sopenharmony_ci .map(drop) 1433da5c369Sopenharmony_ci } 1443da5c369Sopenharmony_ci 1453da5c369Sopenharmony_ci /// Get the parameters for the alarm currently set, if any. 1463da5c369Sopenharmony_ci #[doc(alias("timer_gettime"))] 1473da5c369Sopenharmony_ci pub fn get(&self) -> Result<Option<Expiration>> { 1483da5c369Sopenharmony_ci let mut timerspec = TimerSpec::none(); 1493da5c369Sopenharmony_ci Errno::result(unsafe { 1503da5c369Sopenharmony_ci libc::timer_gettime(self.0, timerspec.as_mut()) 1513da5c369Sopenharmony_ci }) 1523da5c369Sopenharmony_ci .map(|_| { 1533da5c369Sopenharmony_ci if timerspec.as_ref().it_interval.tv_sec == 0 1543da5c369Sopenharmony_ci && timerspec.as_ref().it_interval.tv_nsec == 0 1553da5c369Sopenharmony_ci && timerspec.as_ref().it_value.tv_sec == 0 1563da5c369Sopenharmony_ci && timerspec.as_ref().it_value.tv_nsec == 0 1573da5c369Sopenharmony_ci { 1583da5c369Sopenharmony_ci None 1593da5c369Sopenharmony_ci } else { 1603da5c369Sopenharmony_ci Some(timerspec.into()) 1613da5c369Sopenharmony_ci } 1623da5c369Sopenharmony_ci }) 1633da5c369Sopenharmony_ci } 1643da5c369Sopenharmony_ci 1653da5c369Sopenharmony_ci /// Return the number of timers that have overrun 1663da5c369Sopenharmony_ci /// 1673da5c369Sopenharmony_ci /// Each timer is able to queue one signal to the process at a time, meaning 1683da5c369Sopenharmony_ci /// if the signal is not handled before the next expiration the timer has 1693da5c369Sopenharmony_ci /// 'overrun'. This function returns how many times that has happened to 1703da5c369Sopenharmony_ci /// this timer, up to `libc::DELAYTIMER_MAX`. If more than the maximum 1713da5c369Sopenharmony_ci /// number of overruns have happened the return is capped to the maximum. 1723da5c369Sopenharmony_ci #[doc(alias("timer_getoverrun"))] 1733da5c369Sopenharmony_ci pub fn overruns(&self) -> i32 { 1743da5c369Sopenharmony_ci unsafe { libc::timer_getoverrun(self.0) } 1753da5c369Sopenharmony_ci } 1763da5c369Sopenharmony_ci} 1773da5c369Sopenharmony_ci 1783da5c369Sopenharmony_ciimpl Drop for Timer { 1793da5c369Sopenharmony_ci fn drop(&mut self) { 1803da5c369Sopenharmony_ci if !std::thread::panicking() { 1813da5c369Sopenharmony_ci let result = Errno::result(unsafe { libc::timer_delete(self.0) }); 1823da5c369Sopenharmony_ci if let Err(Errno::EINVAL) = result { 1833da5c369Sopenharmony_ci panic!("close of Timer encountered EINVAL"); 1843da5c369Sopenharmony_ci } 1853da5c369Sopenharmony_ci } 1863da5c369Sopenharmony_ci } 1873da5c369Sopenharmony_ci} 188