1#![warn(missing_debug_implementations)]
2#[cfg(not(target_os = "linux"))]
3compile_error!("This crate is only compatible with Linux.");
4
5use rustix::fd::OwnedFd;
6use rustix::time::{Itimerspec, TimerfdClockId, Timespec};
7use std::os::unix::prelude::*;
8use rustix::time::{TimerfdFlags, TimerfdTimerFlags};
9use std::time::Duration;
10use mio::event::Source;
11use mio::unix::SourceFd;
12use mio::{Interest, Registry, Token};
13use std::io;
14
15const TS_NULL: Timespec = Timespec {
16 tv_sec: 0,
17 tv_nsec: 0,
18};
19
20#[derive(Debug, Clone, PartialEq, Eq)]
21pub enum TimerFdFlag {
22 Default,
23 Abstime,
24 TimerCancelOnSet,
25}
26
27#[derive(Debug)]
28pub struct TimerFd(OwnedFd);
29impl TimerFd {
30
31 pub fn new_custom(
32 clock: TimerfdClockId,
33 nonblocking: bool,
34 cloexec: bool,
35 ) -> std::io::Result<TimerFd> {
36 let mut flags = TimerfdFlags::empty();
37 if nonblocking {
38 flags |= TimerfdFlags::NONBLOCK;
39 }
40 if cloexec {
41 flags |= TimerfdFlags::CLOEXEC;
42 }
43
44 let fd = rustix::time::timerfd_create(clock, flags)?;
45 Ok(TimerFd(fd))
46 }
47
48 pub fn new() -> std::io::Result<TimerFd> {
49 TimerFd::new_custom(TimerfdClockId::Monotonic, true, true)
50 }
51
52 #[inline]
53 fn duration_to_timespec(duration: Duration) -> Timespec {
54 Timespec { tv_sec: duration.as_secs() as i64, tv_nsec: duration.subsec_nanos() as i64 }
55 }
56
57 #[inline]
58 fn timespec_to_duration(timespec: Timespec) -> Duration {
59 Duration::new(timespec.tv_sec as u64, timespec.tv_nsec as u32)
60 }
61
62 fn convert_timerfd_flags(flags: TimerFdFlag) -> TimerfdTimerFlags {
63 match flags {
64 TimerFdFlag::Default => TimerfdTimerFlags::empty(),
65 TimerFdFlag::Abstime => TimerfdTimerFlags::ABSTIME,
66 TimerFdFlag::TimerCancelOnSet => {
67 TimerfdTimerFlags::ABSTIME | TimerfdTimerFlags::CANCEL_ON_SET
68 }
69 }
70 }
71
72 pub fn set_timeout_interval_and_flags(&mut self, value: Duration, interval: Duration, sflags: TimerFdFlag) -> std::io::Result<()> {
73 let flags = Self::convert_timerfd_flags(sflags);
74
75 let timer_spec: Itimerspec = Itimerspec {
76 it_value: Self::duration_to_timespec(value),
77 it_interval: Self::duration_to_timespec(interval),
78 };
79 rustix::time::timerfd_settime(&self.0, flags, &timer_spec)?;
80 Ok(())
81 }
82
83 pub fn set_timeout_interval(&mut self, value: Duration, interval: Duration) -> std::io::Result<()> {
84 Self::set_timeout_interval_and_flags(self, value, interval, TimerFdFlag::Default)
85 }
86
87 pub fn set_timeout_oneshot_and_flags(&mut self, value: Duration, flags: TimerFdFlag) -> std::io::Result<()> {
88 let flags = Self::convert_timerfd_flags(flags);
89 let timer_spec: Itimerspec = Itimerspec {
90 it_value: Self::duration_to_timespec(value),
91 it_interval: TS_NULL,
92 };
93 rustix::time::timerfd_settime(&self.0, flags, &timer_spec)?;
94 Ok(())
95 }
96
97 pub fn set_timeout_oneshot(&mut self, value: Duration) -> std::io::Result<()> {
98 Self::set_timeout_oneshot_and_flags(self, value, TimerFdFlag::Default)
99 }
100
101 pub fn disarm(&mut self) {
102 let timer_spec: Itimerspec = Itimerspec {
103 it_value: TS_NULL,
104 it_interval: TS_NULL,
105 };
106 rustix::time::timerfd_settime(&self.0, TimerfdTimerFlags::empty(), &timer_spec).unwrap();
107 }
108
109 pub fn get_remaining_time(&self) -> io::Result<Duration> {
110 Ok(Self::timespec_to_duration(rustix::time::timerfd_gettime(&self.0)?.it_value))
111 }
112
113 pub fn get_interval(&self) -> io::Result<Duration> {
114 Ok(Self::timespec_to_duration(rustix::time::timerfd_gettime(&self.0)?.it_interval))
115 }
116
117 pub fn read(&self) -> io::Result<u64> {
118 let mut buffer = [0_u8; 8];
119 loop {
120 match rustix::io::read(&self.0, &mut buffer) {
121
122 Ok(8) => {
124 let value = u64::from_ne_bytes(buffer);
125 assert_ne!(value, 0);
126 return Ok(value);
127 }
128
129 Err(rustix::io::Errno::WOULDBLOCK) => return Ok(0),
131
132 Err(rustix::io::Errno::INTR) => (),
134
135 Err(e) => {
137 return Err(io::Error::new(
138 io::ErrorKind::Other,
139 format!("Unexpected rustix::io::read error: {}", e),
140 ));
141 }
142 _ => unreachable!(),
143 }
144 }
145 }
146
147 pub fn read_and_check_overrun(&self) -> io::Result<bool> {
148 let res = self.read()?;
149 match res {
150 0 => Ok(false), 1 => Ok(true), x =>
153 {
155 Err(io::Error::new(
156 io::ErrorKind::Other,
157 format!("Timer overrun: missed {} timer expirations", x - 1),
158 ))
159 }
160 }
161 }
162}
163
164impl AsRawFd for TimerFd {
165 fn as_raw_fd(&self) -> RawFd {
166 self.0.as_raw_fd()
167 }
168}
169
170impl AsFd for TimerFd {
171 fn as_fd(&self) -> rustix::fd::BorrowedFd<'_> {
172 self.0.as_fd()
173 }
174}
175
176impl Source for TimerFd {
177 fn register(
178 &mut self,
179 registry: &Registry,
180 token: Token,
181 interests: Interest,
182 ) -> io::Result<()> {
183 SourceFd(&self.as_raw_fd()).register(registry, token, interests)
184 }
185
186 fn reregister(
187 &mut self,
188 registry: &Registry,
189 token: Token,
190 interests: Interest,
191 ) -> io::Result<()> {
192 SourceFd(&self.as_raw_fd()).reregister(registry, token, interests)
193 }
194
195 fn deregister(&mut self, registry: &Registry) -> io::Result<()> {
196 SourceFd(&self.as_raw_fd()).deregister(registry)
197 }
198}
199
200#[cfg(test)]
201mod test {
202 use super::*;
203 use mio::{Events, Poll};
204 use std::time::{Duration, Instant};
205
206 fn is_fd_open(fd: RawFd) -> bool {
207 let borrowed_fd = unsafe { BorrowedFd::borrow_raw(fd) };
208 rustix::io::fcntl_getfd(borrowed_fd).is_ok()
209 }
210
211 #[test]
212 fn test_new_timerfd() {
213 let timer_fd = TimerFd::new().expect("Failed to create TimerFd");
214 assert!(timer_fd.as_raw_fd() > 0);
215 }
216
217 #[test]
218 fn test_duration_to_timespec_and_back() {
219 let duration = Duration::new(5, 500_000_000); let timespec = TimerFd::duration_to_timespec(duration);
221 assert_eq!(timespec.tv_sec, 5);
222 assert_eq!(timespec.tv_nsec, 500_000_000);
223
224 let converted_duration = TimerFd::timespec_to_duration(timespec);
225 assert_eq!(duration, converted_duration);
226 }
227
228 #[test]
229 fn test_set_timeout_oneshot() {
230 let mut timer_fd = TimerFd::new().unwrap();
231 let timeout = Duration::from_secs(1);
232 timer_fd.set_timeout_oneshot(timeout).unwrap();
233
234 let remaining_time = timer_fd.get_remaining_time().unwrap();
235 assert!(remaining_time <= timeout);
236 }
237
238 #[test]
239 fn test_set_timeout_interval() {
240 let mut timer_fd = TimerFd::new().unwrap();
241 let timeout = Duration::from_secs(1);
242 let interval = Duration::from_secs(2);
243
244 timer_fd.set_timeout_interval(timeout, interval).unwrap();
245
246 let remaining_time = timer_fd.get_remaining_time().unwrap();
247 let configured_interval = timer_fd.get_interval().unwrap();
248
249 assert!(remaining_time <= timeout);
250 assert_eq!(configured_interval, interval);
251 }
252
253 #[test]
254 fn test_disarm_timer() {
255 let mut timer_fd = TimerFd::new().unwrap();
256 let timeout = Duration::from_secs(1);
257 timer_fd.set_timeout_oneshot(timeout).unwrap();
258
259 timer_fd.disarm();
260
261 let remaining_time = timer_fd.get_remaining_time().unwrap();
262 assert_eq!(remaining_time, Duration::ZERO);
263 }
264
265 #[test]
266 fn test_read_expiration_count() {
267 let mut timer_fd = TimerFd::new().unwrap();
268 let timeout = Duration::from_millis(100);
269 timer_fd.set_timeout_oneshot(timeout).unwrap();
270
271 std::thread::sleep(timeout + Duration::from_millis(50));
273
274 let expirations = timer_fd.read().unwrap();
275 assert_eq!(expirations, 1);
276 }
277
278 #[test]
279 fn test_read_and_check_overrun() {
280 let mut timer_fd = TimerFd::new().unwrap();
281 let timeout = Duration::from_millis(100);
282
283 timer_fd.set_timeout_interval(timeout, timeout).unwrap();
285
286 std::thread::sleep(timeout * 5);
288
289 match timer_fd.read_and_check_overrun() {
291 Err(err) => assert!(err.to_string().contains("Timer overrun")),
292 _ => panic!("Expected timer expirations"),
293 }
294 }
295
296 #[test]
297 fn test_register_with_mio() {
298 use mio::{Events, Poll};
299
300 let mut timer_fd = TimerFd::new().unwrap();
301 let mut poll = Poll::new().unwrap();
302 let mut events = Events::with_capacity(128);
303
304 poll.registry()
305 .register(&mut timer_fd, Token(0), Interest::READABLE)
306 .unwrap();
307
308 let timeout = Duration::from_millis(100);
309 timer_fd.set_timeout_oneshot(timeout).unwrap();
310
311 poll.poll(&mut events, Some(timeout * 2)).unwrap();
312
313 let event = events.iter().next().unwrap();
314 assert_eq!(event.token(), Token(0));
315 assert!(event.is_readable());
316 }
317
318 #[test]
319 fn oneshot_timeout() {
320 const TOKEN: Token = Token(0);
321 const TIMEOUT: Duration = Duration::from_millis(100);
322
323 let mut poll = Poll::new().unwrap();
324 let mut events = Events::with_capacity(1024);
325 let mut timer = TimerFd::new().unwrap();
326
327 timer.set_timeout_oneshot(TIMEOUT).unwrap();
328 poll.registry().register(&mut timer, TOKEN, Interest::READABLE).unwrap();
329
330 poll.poll(&mut events, Some(TIMEOUT / 2)).unwrap();
332 assert!(events.is_empty());
333 assert!(timer.read().unwrap() == 0);
334
335 poll.poll(&mut events, Some(TIMEOUT)).unwrap();
337 assert!(!events.is_empty());
338 assert!(timer.read().unwrap() == 1);
339
340 poll.poll(&mut events, Some(TIMEOUT)).unwrap();
342 assert!(events.is_empty());
343 assert!(timer.read().unwrap() == 0);
344
345 timer.set_timeout_oneshot(TIMEOUT).unwrap();
347 poll.poll(&mut events, Some(TIMEOUT * 2)).unwrap();
348 assert!(!events.is_empty());
349 assert!(timer.read().unwrap() == 1);
350 }
351
352 #[test]
353 fn test_timerfd_closes_on_drop() {
354 let rawfd: i32;
355
356 {
358 let timer_fd = TimerFd::new().expect("Failed to create TimerFd");
359 rawfd = timer_fd.as_raw_fd();
360 assert!(is_fd_open(rawfd));
361
362 }
363 assert!(!is_fd_open(rawfd));
364
365 let timer_fd = TimerFd::new().expect("Failed to create TimerFd");
367 let rawfd = timer_fd.as_raw_fd();
368 assert!(is_fd_open(rawfd));
369 drop(timer_fd);
370 assert!(!is_fd_open(rawfd));
371 }
372
373 #[test]
374 fn test_multiple_timers() {
375 let mut timer1 = TimerFd::new().expect("Failed to create TimerFd 1");
376 let mut timer2 = TimerFd::new().expect("Failed to create TimerFd 2");
377 let mut timer3 = TimerFd::new().expect("Failed to create TimerFd 3");
378
379 let duration1 = Duration::from_millis(100);
380 let duration2 = Duration::from_millis(200);
381 let duration3 = Duration::from_millis(300);
382
383 timer1.set_timeout_oneshot(duration1).expect("Failed to set timer 1");
384 timer2.set_timeout_oneshot(duration2).expect("Failed to set timer 2");
385 timer3.set_timeout_oneshot(duration3).expect("Failed to set timer 3");
386
387 let start = Instant::now();
388
389 while timer1.read().unwrap() == 0 {
390 assert!(start.elapsed() < duration1 + Duration::from_millis(50));
391 }
392 println!("Timer 1 expired!");
393
394 while timer2.read().unwrap() == 0 {
395 assert!(start.elapsed() < duration2 + Duration::from_millis(50));
396 }
397 println!("Timer 2 expired!");
398
399 while timer3.read().unwrap() == 0 {
400 assert!(start.elapsed() < duration3 + Duration::from_millis(50));
401 }
402 println!("Timer 3 expired!");
403 }
404}