nix/sys/
timerfd.rs

1//! Timer API via file descriptors.
2//!
3//! Timer FD is a Linux-only API to create timers and get expiration
4//! notifications through file descriptors.
5//!
6//! For more documentation, please read [timerfd_create(2)](https://man7.org/linux/man-pages/man2/timerfd_create.2.html).
7//!
8//! # Examples
9//!
10//! Create a new one-shot timer that expires after 1 second.
11//! ```
12//! # use std::os::unix::io::AsRawFd;
13//! # use nix::sys::timerfd::{TimerFd, ClockId, TimerFlags, TimerSetTimeFlags,
14//! #    Expiration};
15//! # use nix::sys::time::{TimeSpec, TimeValLike};
16//! # use nix::unistd::read;
17//! #
18//! // We create a new monotonic timer.
19//! let timer = TimerFd::new(ClockId::CLOCK_MONOTONIC, TimerFlags::empty())
20//!     .unwrap();
21//!
22//! // We set a new one-shot timer in 1 seconds.
23//! timer.set(
24//!     Expiration::OneShot(TimeSpec::seconds(1)),
25//!     TimerSetTimeFlags::empty()
26//! ).unwrap();
27//!
28//! // We wait for the timer to expire.
29//! timer.wait().unwrap();
30//! ```
31use crate::sys::time::timer::TimerSpec;
32pub use crate::sys::time::timer::{Expiration, TimerSetTimeFlags};
33use crate::unistd::read;
34use crate::{errno::Errno, Result};
35use libc::c_int;
36use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd};
37
38/// A timerfd instance. This is also a file descriptor, you can feed it to
39/// other interfaces taking file descriptors as arguments, [`epoll`] for example.
40///
41/// [`epoll`]: crate::sys::epoll
42#[derive(Debug)]
43pub struct TimerFd {
44    fd: OwnedFd,
45}
46
47impl AsFd for TimerFd {
48    fn as_fd(&self) -> BorrowedFd<'_> {
49        self.fd.as_fd()
50    }
51}
52
53impl FromRawFd for TimerFd {
54    unsafe fn from_raw_fd(fd: RawFd) -> Self {
55        TimerFd {
56            fd: unsafe { OwnedFd::from_raw_fd(fd) },
57        }
58    }
59}
60
61libc_enum! {
62    /// The type of the clock used to mark the progress of the timer. For more
63    /// details on each kind of clock, please refer to [timerfd_create(2)](https://man7.org/linux/man-pages/man2/timerfd_create.2.html).
64    #[repr(i32)]
65    #[non_exhaustive]
66    pub enum ClockId {
67        /// A settable system-wide real-time clock.
68        CLOCK_REALTIME,
69        /// A non-settable monotonically increasing clock.
70        ///
71        /// Does not change after system startup.
72        /// Does not measure time while the system is suspended.
73        CLOCK_MONOTONIC,
74        /// Like `CLOCK_MONOTONIC`, except that `CLOCK_BOOTTIME` includes the time
75        /// that the system was suspended.
76        CLOCK_BOOTTIME,
77        /// Like `CLOCK_REALTIME`, but will wake the system if it is suspended.
78        CLOCK_REALTIME_ALARM,
79        /// Like `CLOCK_BOOTTIME`, but will wake the system if it is suspended.
80        CLOCK_BOOTTIME_ALARM,
81    }
82}
83
84libc_bitflags! {
85    /// Additional flags to change the behaviour of the file descriptor at the
86    /// time of creation.
87    pub struct TimerFlags: c_int {
88        /// Set the `O_NONBLOCK` flag on the open file description referred to by the new file descriptor.
89        TFD_NONBLOCK;
90        /// Set the `FD_CLOEXEC` flag on the file descriptor.
91        TFD_CLOEXEC;
92    }
93}
94
95impl TimerFd {
96    /// Creates a new timer based on the clock defined by `clockid`. The
97    /// underlying fd can be assigned specific flags with `flags` (CLOEXEC,
98    /// NONBLOCK). The underlying fd will be closed on drop.
99    #[doc(alias("timerfd_create"))]
100    pub fn new(clockid: ClockId, flags: TimerFlags) -> Result<Self> {
101        Errno::result(unsafe {
102            libc::timerfd_create(clockid as i32, flags.bits())
103        })
104        .map(|fd| Self {
105            fd: unsafe { OwnedFd::from_raw_fd(fd) },
106        })
107    }
108
109    /// Sets a new alarm on the timer.
110    ///
111    /// # Types of alarm
112    ///
113    /// There are 3 types of alarms you can set:
114    ///
115    ///   - one shot: the alarm will trigger once after the specified amount of
116    /// time.
117    ///     Example: I want an alarm to go off in 60s and then disable itself.
118    ///
119    ///   - interval: the alarm will trigger every specified interval of time.
120    ///     Example: I want an alarm to go off every 60s. The alarm will first
121    ///     go off 60s after I set it and every 60s after that. The alarm will
122    ///     not disable itself.
123    ///
124    ///   - interval delayed: the alarm will trigger after a certain amount of
125    ///     time and then trigger at a specified interval.
126    ///     Example: I want an alarm to go off every 60s but only start in 1h.
127    ///     The alarm will first trigger 1h after I set it and then every 60s
128    ///     after that. The alarm will not disable itself.
129    ///
130    /// # Relative vs absolute alarm
131    ///
132    /// If you do not set any `TimerSetTimeFlags`, then the `TimeSpec` you pass
133    /// to the `Expiration` you want is relative. If however you want an alarm
134    /// to go off at a certain point in time, you can set `TFD_TIMER_ABSTIME`.
135    /// Then the one shot TimeSpec and the delay TimeSpec of the delayed
136    /// interval are going to be interpreted as absolute.
137    ///
138    /// # Cancel on a clock change
139    ///
140    /// If you set a `TFD_TIMER_CANCEL_ON_SET` alongside `TFD_TIMER_ABSTIME`
141    /// and the clock for this timer is `CLOCK_REALTIME` or `CLOCK_REALTIME_ALARM`,
142    /// then this timer is marked as cancelable if the real-time clock undergoes
143    /// a discontinuous change.
144    ///
145    /// # Disabling alarms
146    ///
147    /// Note: Only one alarm can be set for any given timer. Setting a new alarm
148    /// actually removes the previous one.
149    ///
150    /// Note: Setting a one shot alarm with a 0s TimeSpec disables the alarm
151    /// altogether.
152    #[doc(alias("timerfd_settime"))]
153    pub fn set(
154        &self,
155        expiration: Expiration,
156        flags: TimerSetTimeFlags,
157    ) -> Result<()> {
158        let timerspec: TimerSpec = expiration.into();
159        Errno::result(unsafe {
160            libc::timerfd_settime(
161                self.fd.as_fd().as_raw_fd(),
162                flags.bits(),
163                timerspec.as_ref(),
164                std::ptr::null_mut(),
165            )
166        })
167        .map(drop)
168    }
169
170    /// Get the parameters for the alarm currently set, if any.
171    #[doc(alias("timerfd_gettime"))]
172    pub fn get(&self) -> Result<Option<Expiration>> {
173        let mut timerspec = TimerSpec::none();
174        Errno::result(unsafe {
175            libc::timerfd_gettime(
176                self.fd.as_fd().as_raw_fd(),
177                timerspec.as_mut(),
178            )
179        })
180        .map(|_| {
181            if timerspec.as_ref().it_interval.tv_sec == 0
182                && timerspec.as_ref().it_interval.tv_nsec == 0
183                && timerspec.as_ref().it_value.tv_sec == 0
184                && timerspec.as_ref().it_value.tv_nsec == 0
185            {
186                None
187            } else {
188                Some(timerspec.into())
189            }
190        })
191    }
192
193    /// Remove the alarm if any is set.
194    #[doc(alias("timerfd_settime"))]
195    pub fn unset(&self) -> Result<()> {
196        Errno::result(unsafe {
197            libc::timerfd_settime(
198                self.fd.as_fd().as_raw_fd(),
199                TimerSetTimeFlags::empty().bits(),
200                TimerSpec::none().as_ref(),
201                std::ptr::null_mut(),
202            )
203        })
204        .map(drop)
205    }
206
207    /// Wait for the configured alarm to expire.
208    ///
209    /// Note: If the alarm is unset, then you will wait forever.
210    pub fn wait(&self) -> Result<()> {
211        while let Err(e) = read(self.fd.as_fd().as_raw_fd(), &mut [0u8; 8]) {
212            if e == Errno::ECANCELED {
213                break;
214            }
215            if e != Errno::EINTR {
216                return Err(e);
217            }
218        }
219
220        Ok(())
221    }
222}