188 lines
6.6 KiB
Rust
188 lines
6.6 KiB
Rust
//! 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<Self> {
|
|
let mut timer_id: mem::MaybeUninit<libc::timer_t> =
|
|
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<Option<Expiration>> {
|
|
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");
|
|
}
|
|
}
|
|
}
|
|
}
|