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::TimeSpec; 32 use crate::unistd::read; 33 use crate::{errno::Errno, Result}; 34 use bitflags::bitflags; 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 CLOCK_REALTIME, 64 CLOCK_MONOTONIC, 65 CLOCK_BOOTTIME, 66 CLOCK_REALTIME_ALARM, 67 CLOCK_BOOTTIME_ALARM, 68 } 69 } 70 71 libc_bitflags! { 72 /// Additional flags to change the behaviour of the file descriptor at the 73 /// time of creation. 74 pub struct TimerFlags: c_int { 75 TFD_NONBLOCK; 76 TFD_CLOEXEC; 77 } 78 } 79 80 bitflags! { 81 /// Flags that are used for arming the timer. 82 pub struct TimerSetTimeFlags: libc::c_int { 83 const TFD_TIMER_ABSTIME = libc::TFD_TIMER_ABSTIME; 84 } 85 } 86 87 #[derive(Debug, Clone, Copy)] 88 struct TimerSpec(libc::itimerspec); 89 90 impl TimerSpec { none() -> Self91 pub const fn none() -> Self { 92 Self(libc::itimerspec { 93 it_interval: libc::timespec { 94 tv_sec: 0, 95 tv_nsec: 0, 96 }, 97 it_value: libc::timespec { 98 tv_sec: 0, 99 tv_nsec: 0, 100 }, 101 }) 102 } 103 } 104 105 impl AsRef<libc::itimerspec> for TimerSpec { as_ref(&self) -> &libc::itimerspec106 fn as_ref(&self) -> &libc::itimerspec { 107 &self.0 108 } 109 } 110 111 impl From<Expiration> for TimerSpec { from(expiration: Expiration) -> TimerSpec112 fn from(expiration: Expiration) -> TimerSpec { 113 match expiration { 114 Expiration::OneShot(t) => TimerSpec(libc::itimerspec { 115 it_interval: libc::timespec { 116 tv_sec: 0, 117 tv_nsec: 0, 118 }, 119 it_value: *t.as_ref(), 120 }), 121 Expiration::IntervalDelayed(start, interval) => TimerSpec(libc::itimerspec { 122 it_interval: *interval.as_ref(), 123 it_value: *start.as_ref(), 124 }), 125 Expiration::Interval(t) => TimerSpec(libc::itimerspec { 126 it_interval: *t.as_ref(), 127 it_value: *t.as_ref(), 128 }), 129 } 130 } 131 } 132 133 impl From<TimerSpec> for Expiration { from(timerspec: TimerSpec) -> Expiration134 fn from(timerspec: TimerSpec) -> Expiration { 135 match timerspec { 136 TimerSpec(libc::itimerspec { 137 it_interval: 138 libc::timespec { 139 tv_sec: 0, 140 tv_nsec: 0, 141 }, 142 it_value: ts, 143 }) => Expiration::OneShot(ts.into()), 144 TimerSpec(libc::itimerspec { 145 it_interval: int_ts, 146 it_value: val_ts, 147 }) => { 148 if (int_ts.tv_sec == val_ts.tv_sec) && (int_ts.tv_nsec == val_ts.tv_nsec) { 149 Expiration::Interval(int_ts.into()) 150 } else { 151 Expiration::IntervalDelayed(val_ts.into(), int_ts.into()) 152 } 153 } 154 } 155 } 156 } 157 158 /// An enumeration allowing the definition of the expiration time of an alarm, 159 /// recurring or not. 160 #[derive(Debug, Clone, Copy, PartialEq)] 161 pub enum Expiration { 162 OneShot(TimeSpec), 163 IntervalDelayed(TimeSpec, TimeSpec), 164 Interval(TimeSpec), 165 } 166 167 impl TimerFd { 168 /// Creates a new timer based on the clock defined by `clockid`. The 169 /// underlying fd can be assigned specific flags with `flags` (CLOEXEC, 170 /// NONBLOCK). The underlying fd will be closed on drop. new(clockid: ClockId, flags: TimerFlags) -> Result<Self>171 pub fn new(clockid: ClockId, flags: TimerFlags) -> Result<Self> { 172 Errno::result(unsafe { libc::timerfd_create(clockid as i32, flags.bits()) }) 173 .map(|fd| Self { fd }) 174 } 175 176 /// Sets a new alarm on the timer. 177 /// 178 /// # Types of alarm 179 /// 180 /// There are 3 types of alarms you can set: 181 /// 182 /// - one shot: the alarm will trigger once after the specified amount of 183 /// time. 184 /// Example: I want an alarm to go off in 60s and then disables itself. 185 /// 186 /// - interval: the alarm will trigger every specified interval of time. 187 /// Example: I want an alarm to go off every 60s. The alarm will first 188 /// go off 60s after I set it and every 60s after that. The alarm will 189 /// not disable itself. 190 /// 191 /// - interval delayed: the alarm will trigger after a certain amount of 192 /// time and then trigger at a specified interval. 193 /// Example: I want an alarm to go off every 60s but only start in 1h. 194 /// The alarm will first trigger 1h after I set it and then every 60s 195 /// after that. The alarm will not disable itself. 196 /// 197 /// # Relative vs absolute alarm 198 /// 199 /// If you do not set any `TimerSetTimeFlags`, then the `TimeSpec` you pass 200 /// to the `Expiration` you want is relative. If however you want an alarm 201 /// to go off at a certain point in time, you can set `TFD_TIMER_ABSTIME`. 202 /// Then the one shot TimeSpec and the delay TimeSpec of the delayed 203 /// interval are going to be interpreted as absolute. 204 /// 205 /// # Disabling alarms 206 /// 207 /// Note: Only one alarm can be set for any given timer. Setting a new alarm 208 /// actually removes the previous one. 209 /// 210 /// Note: Setting a one shot alarm with a 0s TimeSpec disables the alarm 211 /// altogether. set(&self, expiration: Expiration, flags: TimerSetTimeFlags) -> Result<()>212 pub fn set(&self, expiration: Expiration, flags: TimerSetTimeFlags) -> Result<()> { 213 let timerspec: TimerSpec = expiration.into(); 214 Errno::result(unsafe { 215 libc::timerfd_settime( 216 self.fd, 217 flags.bits(), 218 timerspec.as_ref(), 219 std::ptr::null_mut(), 220 ) 221 }) 222 .map(drop) 223 } 224 225 /// Get the parameters for the alarm currently set, if any. get(&self) -> Result<Option<Expiration>>226 pub fn get(&self) -> Result<Option<Expiration>> { 227 let mut timerspec = TimerSpec::none(); 228 let timerspec_ptr: *mut libc::itimerspec = &mut timerspec.0; 229 230 Errno::result(unsafe { libc::timerfd_gettime(self.fd, timerspec_ptr) }).map(|_| { 231 if timerspec.0.it_interval.tv_sec == 0 232 && timerspec.0.it_interval.tv_nsec == 0 233 && timerspec.0.it_value.tv_sec == 0 234 && timerspec.0.it_value.tv_nsec == 0 235 { 236 None 237 } else { 238 Some(timerspec.into()) 239 } 240 }) 241 } 242 243 /// Remove the alarm if any is set. unset(&self) -> Result<()>244 pub fn unset(&self) -> Result<()> { 245 Errno::result(unsafe { 246 libc::timerfd_settime( 247 self.fd, 248 TimerSetTimeFlags::empty().bits(), 249 TimerSpec::none().as_ref(), 250 std::ptr::null_mut(), 251 ) 252 }) 253 .map(drop) 254 } 255 256 /// Wait for the configured alarm to expire. 257 /// 258 /// Note: If the alarm is unset, then you will wait forever. wait(&self) -> Result<()>259 pub fn wait(&self) -> Result<()> { 260 while let Err(e) = read(self.fd, &mut [0u8; 8]) { 261 if e != Errno::EINTR { 262 return Err(e) 263 } 264 } 265 266 Ok(()) 267 } 268 } 269 270 impl Drop for TimerFd { drop(&mut self)271 fn drop(&mut self) { 272 if !std::thread::panicking() { 273 let result = Errno::result(unsafe { 274 libc::close(self.fd) 275 }); 276 if let Err(Errno::EBADF) = result { 277 panic!("close of TimerFd encountered EBADF"); 278 } 279 } 280 } 281 } 282