• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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::{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)]
41 pub struct TimerFd {
42     fd: RawFd,
43 }
44 
45 impl AsRawFd for TimerFd {
as_raw_fd(&self) -> RawFd46     fn as_raw_fd(&self) -> RawFd {
47         self.fd
48     }
49 }
50 
51 impl FromRawFd for TimerFd {
from_raw_fd(fd: RawFd) -> Self52     unsafe fn from_raw_fd(fd: RawFd) -> Self {
53         TimerFd { fd }
54     }
55 }
56 
57 libc_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 
80 libc_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 
91 impl 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"))]
new(clockid: ClockId, flags: TimerFlags) -> Result<Self>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"))]
set( &self, expiration: Expiration, flags: TimerSetTimeFlags, ) -> Result<()>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"))]
get(&self) -> Result<Option<Expiration>>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"))]
unset(&self) -> Result<()>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.
wait(&self) -> Result<()>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 
205 impl Drop for TimerFd {
drop(&mut self)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 }
215