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