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