timer_deque_rs/timer_portable/linux/
timer_fd_linux.rs

1/*-
2 * timer-deque-rs - a Rust crate which provides timer and timer queues based on target OS
3 *  functionality.
4 * 
5 * Copyright (C) 2025 Aleksandr Morozov alex@nixd.org
6 *  4neko.org alex@4neko.org
7 * 
8 * The timer-rs crate can be redistributed and/or modified
9 * under the terms of either of the following licenses:
10 *
11 *   1. the Mozilla Public License Version 2.0 (the “MPL”) OR
12 *                     
13 *   2. The MIT License (MIT)
14 *                     
15 *   3. EUROPEAN UNION PUBLIC LICENCE v. 1.2 EUPL © the European Union 2007, 2016
16 */
17 
18
19
20use std::borrow::Cow;
21use std::{fmt};
22use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd};
23use std::os::unix::prelude::RawFd;
24use std::task::Poll;
25
26use nix::fcntl::{FcntlArg, OFlag};
27use nix::{fcntl, libc};
28use nix::libc::itimerspec;
29
30
31use crate::nix::errno::Errno;
32use crate::timer_portable::portable_error::TimerPortResult;
33use crate::timer_portable::timer::{FdTimerRead, ModeTimeType, TimerReadRes};
34use crate::{map_portable_err, portable_err, AbsoluteTime};
35use crate::timer_portable::
36{
37    FdTimerCom, TimerExpMode, TimerFlags, TimerType 
38};
39
40
41/// A `synchronious` timer instance based on the Linux's `timerfd_create`.
42/// 
43/// A `EPoll` or `select` can be used to monitor the instance. Also the 
44/// instance implements [Future] which can be polled from async code. Or 
45/// used with tokio's `AsyncFd` or any other equivalent.
46/// 
47/// It can be used with provided `poll.rs` event notification system wrapper.
48#[derive(Debug)]
49pub struct TimerFdInternal
50{
51    /// A timer's label.
52    label: Cow<'static, str>,
53
54    /// A timer's FD
55    timer_fd: OwnedFd,
56}
57
58impl fmt::Display for TimerFdInternal
59{
60    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result 
61    {
62        write!(f, "{}", self.timer_fd.as_raw_fd())
63    }
64}
65
66impl AsFd for TimerFdInternal
67{
68    fn as_fd(&self) -> BorrowedFd<'_> 
69    {
70        return self.timer_fd.as_fd();
71    }
72}
73
74impl AsRawFd for TimerFdInternal
75{
76    fn as_raw_fd(&self) -> RawFd 
77    {
78        return self.timer_fd.as_raw_fd();
79    }
80}
81
82impl Eq for TimerFdInternal {}
83
84impl PartialEq for TimerFdInternal 
85{
86    fn eq(&self, other: &Self) -> bool 
87    {
88        return self.timer_fd.as_raw_fd() == other.timer_fd.as_raw_fd();
89    }
90}
91
92impl PartialEq<RawFd> for TimerFdInternal
93{
94    fn eq(&self, other: &RawFd) -> bool 
95    {
96        return self.timer_fd.as_raw_fd() == *other;
97    }
98}
99
100impl PartialEq<str> for TimerFdInternal
101{
102    fn eq(&self, other: &str) -> bool 
103    {
104        return self.label == other;
105    }
106}
107
108impl AsRef<str> for TimerFdInternal
109{
110    fn as_ref(&self) -> &str 
111    {
112        return &self.label;
113    }
114}
115
116impl Ord for TimerFdInternal
117{
118    fn cmp(&self, other: &Self) -> std::cmp::Ordering 
119    {
120        return self.timer_fd.as_raw_fd().cmp(&other.timer_fd.as_raw_fd());
121    }
122}
123
124impl PartialOrd for TimerFdInternal
125{
126    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> 
127    {
128        return Some(self.cmp(other));
129    }
130}
131
132impl FdTimerRead for TimerFdInternal
133{
134    #[inline]
135    fn read(&self) -> TimerPortResult<TimerReadRes<u64>>
136    {
137        let mut timer_overfl = [0u8; size_of::<u64>()];
138        
139        loop
140        {
141            let ret = 
142                nix::unistd::read(&self.timer_fd, &mut timer_overfl);
143
144            if let Ok(_) = ret
145            {
146                let overl: u64 = u64::from_ne_bytes(timer_overfl);
147
148                return Ok(TimerReadRes::Ok(overl));
149            }
150            else if let Err(Errno::EINTR) = ret
151            {
152                continue;
153            }
154            else if let Err(Errno::EAGAIN) = ret
155            {
156                // would block
157                return Ok(TimerReadRes::WouldBlock);
158            }
159            else if let Err(Errno::ECANCELED) = ret
160            {
161                return Ok(TimerReadRes::Cancelled);
162            }
163            else if let Err(e) = ret
164            {
165                portable_err!(e, "read timer overflow error for timer: '{}'", self.label)
166            }
167        }
168    }
169}
170
171impl FdTimerCom for TimerFdInternal
172{
173    fn new(label: Cow<'static, str>, timer_type: TimerType, timer_flags: TimerFlags) -> TimerPortResult<Self>
174    {
175        let timer_fd = 
176            unsafe { libc::timerfd_create(timer_type.into(), timer_flags.bits()) };
177
178        if timer_fd == -1
179        {
180            portable_err!(Errno::last(), "timer: '{}', timerfd_create failed with error", label);
181        }
182
183        return Ok(
184            Self
185            { 
186                label: 
187                    label,
188                timer_fd: 
189                    unsafe { OwnedFd::from_raw_fd(timer_fd) }, 
190                /*poll_bind: 
191                    RwLock::new(None),*/
192            }
193        );
194    }
195
196    fn set_time<TIMERTYPE: ModeTimeType>(&self, timer_exp: TimerExpMode<TIMERTYPE>) -> TimerPortResult<()>
197    {
198        // the `timer_exp` is in the [TimerExpMode] so there is no need to check if it is
199        // valid
200
201        let flags = TIMERTYPE::get_flags().bits();
202        let timer_value: itimerspec = timer_exp.into();
203
204        let res = 
205            unsafe 
206            { 
207                libc::timerfd_settime(self.timer_fd.as_raw_fd(), flags,
208                    &timer_value, core::ptr::null_mut()) 
209            };
210
211        if res == -1
212        {
213            portable_err!(Errno::last(), "can not init timer: '{}'", self.label);
214        }
215
216        return Ok(());
217    }
218
219    fn unset_time(&self) -> TimerPortResult<()>
220    {
221        // this is safe because it is primitive without Rust's std
222        let mut timer_value: itimerspec = unsafe { std::mem::zeroed() };
223
224        let res = 
225            unsafe 
226            { 
227                libc::timerfd_gettime(self.timer_fd.as_raw_fd(),
228                    &mut timer_value as *mut _ as *mut itimerspec) 
229            };
230
231        if res == -1
232        {
233            portable_err!(Errno::last(), "can not timerfd_gettime for timer: '{}'", self.label);
234        }
235
236        let timer_mode = TimerExpMode::<AbsoluteTime>::from(timer_value);
237
238        if TimerExpMode::<AbsoluteTime>::reset() == timer_mode
239        {
240            // already disarmed
241            return Ok(());
242        }
243
244        // disarm timer
245
246        let timer_value: itimerspec = TimerExpMode::<AbsoluteTime>::reset().into();
247
248        let res = 
249            unsafe 
250            { 
251                libc::timerfd_settime(self.timer_fd.as_raw_fd(), 0, 
252                    &timer_value, core::ptr::null_mut()) 
253            };
254
255        if res == -1
256        {
257            portable_err!(Errno::last(), "can not unset timer: '{}'", self.label);
258        }
259
260        return Ok(());
261    }
262
263    fn set_nonblocking(&self, flag: bool) -> TimerPortResult<()>
264    {
265        let mut fl = 
266            OFlag::from_bits_retain(
267                fcntl::fcntl(&self.timer_fd, FcntlArg::F_GETFL)
268                    .map_err(|e|
269                        map_portable_err!(e, "timer: '{}', fcntl F_GETFL failed", self.label)
270                    )?
271            );
272
273        fl.set(OFlag::O_NONBLOCK, flag);
274
275        fcntl::fcntl(&self.timer_fd, FcntlArg::F_SETFL(fl))
276            .map_err(|e|
277                map_portable_err!(e, "timer: '{}', fcntl F_SETFL failed", self.label)
278            )?;
279
280        return Ok(());
281    }
282
283    fn is_nonblocking(&self) -> TimerPortResult<bool>
284    {
285        let fl = 
286            OFlag::from_bits_retain(
287                fcntl::fcntl(&self.timer_fd, FcntlArg::F_GETFL)
288                    .map_err(|e|
289                        map_portable_err!(e, "timer: '{}', fcntl F_GETFL failed", self.label)
290                    )?
291            );
292
293        return Ok(fl.intersects(OFlag::O_NONBLOCK));
294    }
295}
296
297
298impl TimerFdInternal
299{
300
301}
302
303impl Future for &TimerFdInternal
304{
305    type Output = TimerPortResult<TimerReadRes<u64>>;
306
307    fn poll(self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> std::task::Poll<Self::Output> 
308    {
309        let res = self.read();
310
311        if let Ok(TimerReadRes::WouldBlock) = res
312        {
313            cx.waker().wake_by_ref();
314
315            return Poll::Pending;
316        }
317        else
318        {
319            return Poll::Ready(res);
320        } 
321    }
322}
323
324impl Future for TimerFdInternal
325{
326    type Output = TimerPortResult<TimerReadRes<u64>>;
327
328    fn poll(self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> std::task::Poll<Self::Output> 
329    {
330        let res = self.read();
331
332        if let Ok(TimerReadRes::WouldBlock) = res
333        {
334            cx.waker().wake_by_ref();
335
336            return Poll::Pending;
337        }
338        else
339        {
340            return Poll::Ready(res);
341        } 
342    }
343}
344
345
346#[cfg(test)]
347mod tests
348{
349    use std::time::Instant;
350
351    use tokio::io::{unix::AsyncFd, Interest};
352
353    use crate::timer_portable::timer::AbsoluteTime;
354
355    use super::*;
356
357    #[test]
358    fn test1()
359    {
360        let timer = 
361            TimerFdInternal::new(Cow::Borrowed("test"), TimerType::CLOCK_REALTIME, 
362                TimerFlags::empty()).unwrap();
363
364        let now = chrono::offset::Local::now().timestamp();
365        let snow = now + 3;
366        let s = Instant::now();
367
368        let timer_mode1 = 
369            TimerExpMode::<AbsoluteTime>::new_oneshot(
370                AbsoluteTime::new_time(snow, 0).unwrap()
371            );
372
373        let res = 
374            timer
375                .set_time(timer_mode1);
376        println!("timer was set: '{}' '{}'", now, snow);
377
378        assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
379
380        
381
382        let ovf = timer.read().unwrap().unwrap();
383
384        let ts = chrono::offset::Local::now().timestamp();
385        let e = s.elapsed();
386
387        assert_eq!(ovf, 1);
388        assert_eq!(ts, snow);
389
390        println!("elapsed: {:?}, ts: {}", e, ts);
391
392        assert_eq!((e.as_millis() <= 3100), true);
393
394        println!("Success");
395        return;
396    }
397
398
399    #[tokio::test]
400    async fn test2_fut()
401    {
402        let timer = 
403            TimerFdInternal::new(Cow::Borrowed("test"), TimerType::CLOCK_REALTIME, 
404                TimerFlags::empty()).unwrap();
405
406        let now = chrono::offset::Local::now().timestamp();
407        let snow = now + 3;
408        let s = Instant::now();
409
410        let timer_mode1 = 
411            TimerExpMode::<AbsoluteTime>::new_oneshot(
412                AbsoluteTime::new_time(snow, 0).unwrap()
413            );
414
415
416        let res = 
417            timer
418                .set_time(timer_mode1);
419
420        println!("timer was set: '{}' '{}'", now, snow);
421
422        assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
423
424        //let res = timer.await;
425
426        //println!("{:?}", res);
427        tokio::select! {
428            ovf = timer => {
429                let ts = chrono::offset::Local::now().timestamp();
430                let e = s.elapsed();
431
432                assert_eq!(ovf, Ok(TimerReadRes::Ok(1)));
433                assert_eq!(ts, snow);
434
435                println!("timeout e: {:?}, ts:{} snow:{}", e, ts, snow);
436            }
437        }
438    }
439
440    #[tokio::test]
441    async fn test3_tokio()
442    {
443        let mut timer: AsyncFd<TimerFdInternal> = 
444            AsyncFd::with_interest(
445                TimerFdInternal::new(Cow::Borrowed("test"), TimerType::CLOCK_REALTIME, 
446                        TimerFlags::empty()).unwrap(), 
447                Interest::READABLE
448            ).unwrap();
449            
450
451        let now = chrono::offset::Local::now().timestamp();
452        let snow = now + 3;
453        let s = Instant::now();
454
455        let timer_mode1 = 
456            TimerExpMode::<AbsoluteTime>::new_oneshot(
457                AbsoluteTime::new_time(snow, 0).unwrap()
458            );
459
460        let res = 
461            timer
462                .get_mut()
463                .set_time(timer_mode1);
464
465        println!("timer was set: '{}' '{}'", now, snow);
466
467        assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
468
469        tokio::select! {
470            read_guard_res = timer.ready(Interest::READABLE) => 
471            {
472                let read_guard = read_guard_res.unwrap();
473
474                let res = read_guard.get_inner().read();
475
476                let ts = chrono::offset::Local::now().timestamp();
477                let e = s.elapsed();
478
479                assert_eq!(res, Ok(TimerReadRes::Ok(1)));
480                assert_eq!(ts, snow);
481
482                println!("timeout e: {:?}, ts:{} snow:{}", e, ts, snow);
483            }
484        }
485    }
486
487    #[test]
488    fn test4_cancel()
489    {
490        let timer = 
491            TimerFdInternal::new(Cow::Borrowed("test"), TimerType::CLOCK_REALTIME,
492                TimerFlags::TFD_NONBLOCK).unwrap();
493
494        let now = chrono::offset::Local::now().timestamp();
495        let snow = now + 3;
496        let s = Instant::now();
497
498        let timer_mode1 = 
499            TimerExpMode::<AbsoluteTime>::new_oneshot(
500                AbsoluteTime::new_time(snow, 0).unwrap()
501            );
502
503        let res = 
504            timer
505                .set_time(timer_mode1);
506        println!("timer was set: '{}' '{}'", now, snow);
507
508        assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
509
510       
511
512        let ovf = timer.read().unwrap();
513
514        println!("{}", ovf);
515
516        timer.unset_time().unwrap();
517
518        let ovf = timer.read().unwrap();
519
520         println!("{}", ovf);
521        /*let ts = chrono::offset::Local::now().timestamp();
522        let e = s.elapsed();
523
524        assert_eq!(ovf, 1);
525        assert_eq!(ts, snow);
526
527        println!("elapsed: {:?}, ts: {}", e, ts);
528
529        assert_eq!((e.as_millis() <= 3100), true);
530
531        println!("Success");
532        return;*/
533    }
534    
535}