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