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 //! ```
31 use crate::sys::time::timer::TimerSpec;
32 pub use crate::sys::time::timer::{Expiration, TimerSetTimeFlags};
33 use crate::unistd::read;
34 use crate::{errno::Errno, Result};
35 use libc::c_int;
36 use 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)]
43 pub struct TimerFd {
44     fd: OwnedFd,
45 }
46 
47 impl AsFd for TimerFd {
as_fd(&self) -> BorrowedFd<'_>48     fn as_fd(&self) -> BorrowedFd<'_> {
49         self.fd.as_fd()
50     }
51 }
52 
53 impl FromRawFd for TimerFd {
from_raw_fd(fd: RawFd) -> Self54     unsafe fn from_raw_fd(fd: RawFd) -> Self {
55         TimerFd {
56             fd: unsafe { OwnedFd::from_raw_fd(fd) },
57         }
58     }
59 }
60 
61 libc_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 
84 libc_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 
95 impl 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"))]
new(clockid: ClockId, flags: TimerFlags) -> Result<Self>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"))]
set( &self, expiration: Expiration, flags: TimerSetTimeFlags, ) -> Result<()>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"))]
get(&self) -> Result<Option<Expiration>>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"))]
unset(&self) -> Result<()>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.
wait(&self) -> Result<()>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 }
223