timer_deque_rs/timer_portable/unix/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::timer_portable::unix::{TimerFlags, TimerType, UnixFd};
35use crate::{map_portable_err, portable_err, AbsoluteTime};
36use crate::timer_portable::
37{
38    AsTimerId, FdTimerCom, TimerExpMode, TimerId
39};
40
41
42/// A `synchronious` timer instance based on the Linux's `timerfd_create`.
43/// 
44/// A `EPoll` or `select` can be used to monitor the instance. Also the 
45/// instance implements [Future] which can be polled from async code. Or 
46/// used with tokio's `AsyncFd` or any other equivalent.
47/// 
48/// It can be used with provided `poll.rs` event notification system wrapper.
49#[derive(Debug)]
50pub struct TimerFdInternal
51{
52    /// A timer's label.
53    label: Cow<'static, str>,
54
55    /// A timer's FD
56    timer_fd: OwnedFd,
57}
58
59impl fmt::Display for TimerFdInternal
60{
61    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result 
62    {
63        write!(f, "{}", self.timer_fd.as_raw_fd())
64    }
65}
66
67impl AsFd for TimerFdInternal
68{
69    fn as_fd(&self) -> BorrowedFd<'_> 
70    {
71        return self.timer_fd.as_fd();
72    }
73}
74
75impl AsRawFd for TimerFdInternal
76{
77    fn as_raw_fd(&self) -> RawFd 
78    {
79        return self.timer_fd.as_raw_fd();
80    }
81}
82
83impl Eq for TimerFdInternal {}
84
85impl PartialEq for TimerFdInternal 
86{
87    fn eq(&self, other: &Self) -> bool 
88    {
89        return self.timer_fd.as_raw_fd() == other.timer_fd.as_raw_fd();
90    }
91}
92
93impl PartialEq<RawFd> for TimerFdInternal
94{
95    fn eq(&self, other: &RawFd) -> bool 
96    {
97        return self.timer_fd.as_raw_fd() == *other;
98    }
99}
100
101impl PartialEq<str> for TimerFdInternal
102{
103    fn eq(&self, other: &str) -> bool 
104    {
105        return self.label == other;
106    }
107}
108
109impl AsRef<str> for TimerFdInternal
110{
111    fn as_ref(&self) -> &str 
112    {
113        return &self.label;
114    }
115}
116
117impl UnixFd for TimerFdInternal
118{
119
120}
121
122impl AsTimerId for TimerFdInternal
123{
124    fn as_timer_id(&self) -> TimerId 
125    {
126        return TimerId::from(self.timer_fd.as_raw_fd());
127    }
128}
129
130
131impl Ord for TimerFdInternal
132{
133    fn cmp(&self, other: &Self) -> std::cmp::Ordering 
134    {
135        return self.timer_fd.as_raw_fd().cmp(&other.timer_fd.as_raw_fd());
136    }
137}
138
139impl PartialOrd for TimerFdInternal
140{
141    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> 
142    {
143        return Some(self.cmp(other));
144    }
145}
146
147impl FdTimerRead for TimerFdInternal
148{
149    #[inline]
150    fn read(&self) -> TimerPortResult<TimerReadRes<u64>>
151    {
152        let mut timer_overfl = [0u8; size_of::<u64>()];
153        
154        loop
155        {
156            let ret = 
157                nix::unistd::read(&self.timer_fd, &mut timer_overfl);
158
159            if let Ok(_) = ret
160            {
161                let overl: u64 = u64::from_ne_bytes(timer_overfl);
162
163                return Ok(TimerReadRes::Ok(overl));
164            }
165            else if let Err(Errno::EINTR) = ret
166            {
167                continue;
168            }
169            else if let Err(Errno::EAGAIN) = ret
170            {
171                // would block
172                return Ok(TimerReadRes::WouldBlock);
173            }
174            else if let Err(Errno::ECANCELED) = ret
175            {
176                return Ok(TimerReadRes::Cancelled);
177            }
178            else if let Err(e) = ret
179            {
180                portable_err!(e, "read timer overflow error for timer: '{}'", self.label)
181            }
182        }
183    }
184}
185
186impl FdTimerCom for TimerFdInternal
187{
188    fn new(label: Cow<'static, str>, timer_type: TimerType, timer_flags: TimerFlags) -> TimerPortResult<Self>
189    where 
190        Self: Sized 
191    {
192        let timer_fd = 
193            unsafe { libc::timerfd_create(timer_type.into(), timer_flags.bits()) };
194
195        if timer_fd == -1
196        {
197            portable_err!(Errno::last(), "timer: '{}', timerfd_create failed with error", label);
198        }
199
200        return Ok(
201            Self
202            { 
203                label: 
204                    label,
205                timer_fd: 
206                    unsafe { OwnedFd::from_raw_fd(timer_fd) }, 
207                /*poll_bind: 
208                    RwLock::new(None),*/
209            }
210        );
211    }
212
213    fn set_time<TIMERTYPE: ModeTimeType>(&self, timer_exp: TimerExpMode<TIMERTYPE>) -> TimerPortResult<()>
214    {
215        // the `timer_exp` is in the [TimerExpMode] so there is no need to check if it is
216        // valid
217
218        let flags = TIMERTYPE::get_flags().bits();
219        let timer_value: itimerspec = timer_exp.into();
220
221        let res = 
222            unsafe 
223            { 
224                libc::timerfd_settime(self.timer_fd.as_raw_fd(), flags,
225                    &timer_value, core::ptr::null_mut()) 
226            };
227
228        if res == -1
229        {
230            portable_err!(Errno::last(), "can not init timer: '{}'", self.label);
231        }
232
233        return Ok(());
234    }
235
236    fn unset_time(&self) -> TimerPortResult<()>
237    {
238        // this is safe because it is primitive without Rust's std
239        let mut timer_value: itimerspec = unsafe { std::mem::zeroed() };
240
241        let res = 
242            unsafe 
243            { 
244                libc::timerfd_gettime(self.timer_fd.as_raw_fd(),
245                    &mut timer_value as *mut _ as *mut itimerspec) 
246            };
247
248        if res == -1
249        {
250            portable_err!(Errno::last(), "can not timerfd_gettime for timer: '{}'", self.label);
251        }
252
253        let timer_mode = TimerExpMode::<AbsoluteTime>::from(timer_value);
254
255        if TimerExpMode::<AbsoluteTime>::reset() == timer_mode
256        {
257            // already disarmed
258            return Ok(());
259        }
260
261        // disarm timer
262
263        let timer_value: itimerspec = TimerExpMode::<AbsoluteTime>::reset().into();
264
265        let res = 
266            unsafe 
267            { 
268                libc::timerfd_settime(self.timer_fd.as_raw_fd(), 0, 
269                    &timer_value, core::ptr::null_mut()) 
270            };
271
272        if res == -1
273        {
274            portable_err!(Errno::last(), "can not unset timer: '{}'", self.label);
275        }
276
277        return Ok(());
278    }
279
280    fn set_nonblocking(&self, flag: bool) -> TimerPortResult<()>
281    {
282        let mut fl = 
283            OFlag::from_bits_retain(
284                fcntl::fcntl(&self.timer_fd, FcntlArg::F_GETFL)
285                    .map_err(|e|
286                        map_portable_err!(e, "timer: '{}', fcntl F_GETFL failed", self.label)
287                    )?
288            );
289
290        fl.set(OFlag::O_NONBLOCK, flag);
291
292        fcntl::fcntl(&self.timer_fd, FcntlArg::F_SETFL(fl))
293            .map_err(|e|
294                map_portable_err!(e, "timer: '{}', fcntl F_SETFL failed", self.label)
295            )?;
296
297        return Ok(());
298    }
299
300    fn is_nonblocking(&self) -> TimerPortResult<bool>
301    {
302        let fl = 
303            OFlag::from_bits_retain(
304                fcntl::fcntl(&self.timer_fd, FcntlArg::F_GETFL)
305                    .map_err(|e|
306                        map_portable_err!(e, "timer: '{}', fcntl F_GETFL failed", self.label)
307                    )?
308            );
309
310        return Ok(fl.intersects(OFlag::O_NONBLOCK));
311    }
312}
313
314
315impl TimerFdInternal
316{
317
318}
319
320impl Future for &TimerFdInternal
321{
322    type Output = TimerPortResult<TimerReadRes<u64>>;
323
324    fn poll(self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> std::task::Poll<Self::Output> 
325    {
326        let res = self.read();
327
328        if let Ok(TimerReadRes::WouldBlock) = res
329        {
330            cx.waker().wake_by_ref();
331
332            return Poll::Pending;
333        }
334        else
335        {
336            return Poll::Ready(res);
337        } 
338    }
339}
340
341impl Future for TimerFdInternal
342{
343    type Output = TimerPortResult<TimerReadRes<u64>>;
344
345    fn poll(self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> std::task::Poll<Self::Output> 
346    {
347        let res = self.read();
348
349        if let Ok(TimerReadRes::WouldBlock) = res
350        {
351            cx.waker().wake_by_ref();
352
353            return Poll::Pending;
354        }
355        else
356        {
357            return Poll::Ready(res);
358        } 
359    }
360}
361
362