queen_io/sys/
timerfd.rs

1use std::os::unix::io::{RawFd, AsRawFd, FromRawFd, IntoRawFd};
2use std::time::Duration;
3use std::mem;
4use std::io::{self, Read};
5use std::convert::TryInto;
6use std::fmt;
7
8use crate::epoll::{Epoll, Token, Ready, EpollOpt, Source};
9
10use super::fd::FileDesc;
11
12#[derive(Clone, Copy)]
13#[repr(i32)]
14pub enum Clock {
15    Realtime = libc::CLOCK_REALTIME,
16    Monotonic = libc::CLOCK_MONOTONIC,
17    Boottime = libc::CLOCK_BOOTTIME,
18    RealtimeAlarm = libc::CLOCK_REALTIME_ALARM,
19    BoottimeAlarm = libc::CLOCK_BOOTTIME_ALARM
20}
21
22impl Clock {
23    pub fn clock_name(&self) -> &'static str {
24        match self {
25            Clock::Realtime       => "CLOCK_REALTIME",
26            Clock::RealtimeAlarm  => "CLOCK_REALTIME_ALARM",
27            Clock::Monotonic      => "CLOCK_MONOTONIC",
28            Clock::Boottime       => "CLOCK_BOOTTIME",
29            Clock::BoottimeAlarm  => "CLOCK_BOOTTIME_ALARM",
30        }
31    }
32}
33
34impl fmt::Display for Clock {
35    fn fmt (&self, f: &mut fmt::Formatter) -> fmt::Result {
36        write!(f, "{}", self.clock_name())
37    }
38}
39
40impl fmt::Debug for Clock {
41    fn fmt (&self, f: &mut fmt::Formatter) -> fmt::Result {
42        write!(f, "{} ({})", self.clone() as i32, self.clock_name())
43    }
44}
45
46#[derive(Debug, Clone, PartialEq, Eq)]
47pub enum SetTimeFlags {
48    /// Flags to `timerfd_settime(2)`.
49    ///
50    /// The default is zero, i. e. all bits unset.
51    Default,
52
53    /// Interpret new_value.it_value as an absolute value on the timer's clock. The timer will
54    /// expire when the value of the timer's clock reaches the value specified in
55    /// new_value.it_value.
56    Abstime,
57
58    /// If this flag is specified along with TFD_TIMER_ABSTIME and the clock for this timer is
59    /// CLOCK_REALTIME or CLOCK_REALTIME_ALARM, then mark this timer as cancelable if the
60    /// real-time clock undergoes a discontinuous change (settimeofday(2), clock_settime(2),
61    /// or similar). When such changes occur, a current or future read(2) from the file
62    /// descriptor will fail with the error ECANCELED.
63    ///
64    /// `TFD_TIMER_CANCEL_ON_SET` is useless without `TFD_TIMER_ABSTIME` set, cf. `fs/timerfd.c`.
65    /// Thus `TimerCancelOnSet`` implies `Abstime`.
66    TimerCancelOnSet,
67}
68
69pub const TFD_CLOEXEC: i32 = libc::TFD_CLOEXEC;
70pub const TFD_NONBLOCK: i32 = libc::TFD_NONBLOCK;
71
72const TFD_TIMER_ABSTIME: i32 = libc::TFD_TIMER_ABSTIME;
73const TFD_TIMER_CANCEL_ON_SET: i32 = 0o0000002;
74
75#[derive(Debug)]
76pub struct TimerFd {
77    inner: FileDesc
78}
79
80#[derive(Debug, Clone)]
81pub struct TimerSpec {
82    pub interval: Duration,
83    pub value: Duration
84}
85
86impl TimerFd {
87    /// Create a timerfd with clickid: CLOCK_REALTIME and flags: TFD_CLOEXEC | TFD_NONBLOCK
88    /// http://man7.org/linux/man-pages/man2/timerfd_create.2.html
89    ///
90    /// # Example
91    ///
92    /// ```
93    /// use queen_io::sys::timerfd::TimerFd;
94    ///
95    /// let timerfd = TimerFd::new();
96    /// ```
97    pub fn new() -> io::Result<TimerFd> {
98        let clock = Clock::Realtime;
99        let flags = TFD_CLOEXEC | TFD_NONBLOCK;
100        TimerFd::create(clock, flags)
101    }
102
103    /// Create a timerfd with clock and flags
104    ///
105    /// # Example
106    ///
107    /// ```
108    /// use queen_io::sys::timerfd::{ TimerFd, Clock, TFD_CLOEXEC, TFD_NONBLOCK };
109    ///
110    /// let clock = Clock::Monotonic;
111    /// let flags = TFD_CLOEXEC | TFD_NONBLOCK;
112    /// let timerfd = TimerFd::create(clock, flags);
113    /// ```
114    pub fn create(clock: Clock, flags: i32) -> io::Result<TimerFd> {
115        let timerfd = syscall!(timerfd_create(clock as i32, flags))?;
116        Ok(TimerFd {
117            inner: unsafe { FileDesc::new(timerfd) }
118        })
119    }
120
121    /// Set time to timerfd
122    ///
123    /// # Example
124    ///
125    /// ```
126    /// use std::time::Duration;
127    /// use queen_io::sys::timerfd::{TimerFd, TimerSpec, SetTimeFlags};
128    ///
129    /// let timerfd = TimerFd::new().unwrap();
130    ///
131    /// let timerspec = TimerSpec {
132    ///     interval: Duration::new(0, 0),
133    ///     value: Duration::new(10, 0)
134    /// };
135    ///
136    /// let old_value = timerfd.settime(timerspec, SetTimeFlags::Default);
137    /// ```
138    pub fn settime(&self, value: TimerSpec, flags: SetTimeFlags) -> io::Result<TimerSpec> {
139        let new_value = libc::itimerspec {
140            it_interval: duration_to_timespec(value.interval),
141            it_value: duration_to_timespec(value.value)
142        };
143
144        let mut old_value: libc::itimerspec = unsafe { mem::zeroed() };
145
146        let flags = match flags {
147            SetTimeFlags::Default => 0,
148            SetTimeFlags::Abstime => TFD_TIMER_ABSTIME,
149            SetTimeFlags::TimerCancelOnSet => TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET,
150        };
151
152        syscall!(timerfd_settime(
153            self.inner.as_raw_fd(),
154            flags,
155            &new_value as *const libc::itimerspec,
156            &mut old_value as *mut libc::itimerspec
157        ))?;
158
159        Ok(TimerSpec {
160            interval: timespec_to_duration(old_value.it_interval),
161            value: timespec_to_duration(old_value.it_value)
162        })
163    }
164
165    /// Get time
166    ///
167    /// # Example
168    ///
169    /// ```
170    /// use std::time::Duration;
171    /// use queen_io::sys::timerfd::{TimerFd, TimerSpec, SetTimeFlags};
172    ///
173    /// let timerfd = TimerFd::new().unwrap();
174    ///
175    /// let timerspec = TimerSpec {
176    ///     interval: Duration::new(0, 0),
177    ///     value: Duration::new(10, 0)
178    /// };
179    ///
180    /// let old_value = timerfd.settime(timerspec, SetTimeFlags::Default);
181    ///
182    /// let value = timerfd.gettime();
183    /// ```
184    pub fn gettime(&self) -> io::Result<TimerSpec> {
185        let mut itimerspec: libc::itimerspec = unsafe { mem::zeroed() };
186
187        syscall!(timerfd_gettime(
188            self.inner.as_raw_fd(),
189            &mut itimerspec as *mut libc::itimerspec
190        ))?;
191
192        Ok(TimerSpec {
193            interval: timespec_to_duration(itimerspec.it_interval),
194            value: timespec_to_duration(itimerspec.it_value)
195        })
196    }
197
198    /// read(2) If the timer has already expired one or more times since
199    /// its settings were last modified using timerfd_settime(), or since
200    /// the last successful read(2), then the buffer given to read(2) returns
201    /// an unsigned 8-byte integer (uint64_t) containing the number of
202    /// expirations that have occurred. (The returned value is in host byte
203    /// order, i.e., the native byte order for integers on the host machine.)
204    pub fn read(&self) -> io::Result<u64> {
205        let mut buf = [0u8; 8];
206        (&self.inner).read_exact(&mut buf)?;
207        let temp: u64 = unsafe { mem::transmute(buf) };
208        Ok(temp)
209    }
210}
211
212fn duration_to_timespec(duration: Duration) -> libc::timespec {
213    libc::timespec {
214        tv_sec: duration.as_secs().try_into().unwrap(),
215        tv_nsec: duration.subsec_nanos().try_into().unwrap()
216    }
217}
218
219fn timespec_to_duration(timespec: libc::timespec) -> Duration {
220    Duration::new(timespec.tv_sec as u64, timespec.tv_nsec as u32)
221}
222
223impl FromRawFd for TimerFd {
224    unsafe fn from_raw_fd(fd: RawFd) -> Self {
225        TimerFd {
226            inner: FileDesc::new(fd)
227        }
228    }
229}
230
231impl IntoRawFd for TimerFd {
232    fn into_raw_fd(self) -> RawFd {
233        self.inner.into_raw_fd()
234    }
235}
236
237impl AsRawFd for TimerFd {
238    fn as_raw_fd(&self) -> RawFd {
239        self.inner.as_raw_fd()
240    }
241}
242
243impl Source for TimerFd {
244    fn add(&self, epoll: &Epoll, token: Token, interest: Ready, opts: EpollOpt) -> io::Result<()> {
245        epoll.add(&self.as_raw_fd(), token, interest, opts)
246    }
247
248    fn modify(&self, epoll: &Epoll, token: Token, interest: Ready, opts: EpollOpt) -> io::Result<()> {
249        epoll.modify(&self.as_raw_fd(), token, interest, opts)
250    }
251
252    fn delete(&self, epoll: &Epoll) -> io::Result<()> {
253        epoll.delete(&self.as_raw_fd())
254    }
255}