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