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