vmm_sys_util/linux/
timerfd.rs

1// Copyright 2019 Intel Corporation. All Rights Reserved.
2//
3// Copyright 2018 The Chromium OS Authors. All rights reserved.
4//
5// SPDX-License-Identifier: BSD-3-Clause
6
7//! Structure and functions for working with
8//! [`timerfd`](http://man7.org/linux/man-pages/man2/timerfd_create.2.html).
9
10use std::fs::File;
11use std::mem;
12use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
13use std::ptr;
14use std::time::Duration;
15
16use libc::{self, timerfd_create, timerfd_gettime, timerfd_settime, CLOCK_MONOTONIC, TFD_CLOEXEC};
17
18use crate::errno::{errno_result, Result};
19
20/// A safe wrapper around a Linux
21/// [`timerfd`](http://man7.org/linux/man-pages/man2/timerfd_create.2.html).
22#[derive(Debug)]
23pub struct TimerFd(File);
24
25impl TimerFd {
26    /// Create a new [`TimerFd`](struct.TimerFd.html).
27    ///
28    /// This creates a nonsettable monotonically increasing clock that does not
29    /// change after system startup. The timer is initally disarmed and must be
30    /// armed by calling [`reset`](fn.reset.html).
31    pub fn new() -> Result<TimerFd> {
32        // SAFETY: Safe because this doesn't modify any memory and we check the return value.
33        let ret = unsafe { timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC) };
34        if ret < 0 {
35            return errno_result();
36        }
37
38        // SAFETY: Safe because we uniquely own the file descriptor.
39        Ok(TimerFd(unsafe { File::from_raw_fd(ret) }))
40    }
41
42    /// Arm the [`TimerFd`](struct.TimerFd.html).
43    ///
44    /// Set the timer to expire after `dur`.
45    ///
46    /// # Arguments
47    ///
48    /// * `dur`: Specify the initial expiration of the timer.
49    /// * `interval`: Specify the period for repeated expirations, depending on the
50    /// value passed. If `interval` is not `None`, it represents the period after
51    /// the initial expiration. Otherwise the timer will expire just once. Cancels
52    /// any existing duration and repeating interval.
53    ///
54    /// # Examples
55    ///
56    /// ```
57    /// extern crate vmm_sys_util;
58    /// # use std::time::Duration;
59    /// use vmm_sys_util::timerfd::TimerFd;
60    ///
61    /// let mut timer = TimerFd::new().unwrap();
62    /// let dur = Duration::from_millis(100);
63    /// let interval = Duration::from_millis(100);
64    ///
65    /// timer.reset(dur, Some(interval)).unwrap();
66    /// ```
67    pub fn reset(&mut self, dur: Duration, interval: Option<Duration>) -> Result<()> {
68        // SAFETY: Safe because we are zero-initializing a struct with only primitive member fields.
69        let mut spec: libc::itimerspec = unsafe { mem::zeroed() };
70        // https://github.com/rust-lang/libc/issues/1848
71        #[cfg_attr(target_env = "musl", allow(deprecated))]
72        {
73            spec.it_value.tv_sec = dur.as_secs() as libc::time_t;
74        }
75        // nsec always fits in i32 because subsec_nanos is defined to be less than one billion.
76        let nsec = dur.subsec_nanos() as i32;
77        spec.it_value.tv_nsec = libc::c_long::from(nsec);
78
79        if let Some(int) = interval {
80            // https://github.com/rust-lang/libc/issues/1848
81            #[cfg_attr(target_env = "musl", allow(deprecated))]
82            {
83                spec.it_interval.tv_sec = int.as_secs() as libc::time_t;
84            }
85            // nsec always fits in i32 because subsec_nanos is defined to be less than one billion.
86            let nsec = int.subsec_nanos() as i32;
87            spec.it_interval.tv_nsec = libc::c_long::from(nsec);
88        }
89
90        // SAFETY: Safe because this doesn't modify any memory and we check the return value.
91        let ret = unsafe { timerfd_settime(self.as_raw_fd(), 0, &spec, ptr::null_mut()) };
92        if ret < 0 {
93            return errno_result();
94        }
95
96        Ok(())
97    }
98
99    /// Wait until the timer expires.
100    ///
101    /// The return value represents the number of times the timer has expired since
102    /// the last time `wait` was called. If the timer has not yet expired once,
103    /// this call will block until it does.
104    ///
105    /// # Examples
106    ///
107    /// ```
108    /// extern crate vmm_sys_util;
109    /// # use std::time::Duration;
110    /// # use std::thread::sleep;
111    /// use vmm_sys_util::timerfd::TimerFd;
112    ///
113    /// let mut timer = TimerFd::new().unwrap();
114    /// let dur = Duration::from_millis(100);
115    /// let interval = Duration::from_millis(100);
116    /// timer.reset(dur, Some(interval)).unwrap();
117    ///
118    /// sleep(dur * 3);
119    /// let count = timer.wait().unwrap();
120    /// assert!(count >= 3);
121    /// ```
122    pub fn wait(&mut self) -> Result<u64> {
123        let mut count = 0u64;
124
125        // SAFETY: Safe because this will only modify |buf| and we check the return value.
126        let ret = unsafe {
127            libc::read(
128                self.as_raw_fd(),
129                &mut count as *mut _ as *mut libc::c_void,
130                mem::size_of_val(&count),
131            )
132        };
133        if ret < 0 {
134            return errno_result();
135        }
136
137        // The bytes in the buffer are guaranteed to be in native byte-order so we don't need to
138        // use from_le or from_be.
139        Ok(count)
140    }
141
142    /// Tell if the timer is armed.
143    ///
144    /// Returns `Ok(true)` if the timer is currently armed, otherwise the errno set by
145    /// [`timerfd_gettime`](http://man7.org/linux/man-pages/man2/timerfd_create.2.html).
146    ///
147    /// # Examples
148    ///
149    /// ```
150    /// extern crate vmm_sys_util;
151    /// # use std::time::Duration;
152    /// use vmm_sys_util::timerfd::TimerFd;
153    ///
154    /// let mut timer = TimerFd::new().unwrap();
155    /// let dur = Duration::from_millis(100);
156    ///
157    /// timer.reset(dur, None).unwrap();
158    /// assert!(timer.is_armed().unwrap());
159    /// ```
160    pub fn is_armed(&self) -> Result<bool> {
161        // SAFETY: Safe because we are zero-initializing a struct with only primitive member fields.
162        let mut spec: libc::itimerspec = unsafe { mem::zeroed() };
163
164        // SAFETY: Safe because timerfd_gettime is trusted to only modify `spec`.
165        let ret = unsafe { timerfd_gettime(self.as_raw_fd(), &mut spec) };
166        if ret < 0 {
167            return errno_result();
168        }
169
170        Ok(spec.it_value.tv_sec != 0 || spec.it_value.tv_nsec != 0)
171    }
172
173    /// Disarm the timer.
174    ///
175    /// Set zero to disarm the timer, referring to
176    /// [`timerfd_settime`](http://man7.org/linux/man-pages/man2/timerfd_create.2.html).
177    ///
178    /// # Examples
179    ///
180    /// ```
181    /// extern crate vmm_sys_util;
182    /// # use std::time::Duration;
183    /// use vmm_sys_util::timerfd::TimerFd;
184    ///
185    /// let mut timer = TimerFd::new().unwrap();
186    /// let dur = Duration::from_millis(100);
187    ///
188    /// timer.reset(dur, None).unwrap();
189    /// timer.clear().unwrap();
190    /// ```
191    pub fn clear(&mut self) -> Result<()> {
192        // SAFETY: Safe because we are zero-initializing a struct with only primitive member fields.
193        let spec: libc::itimerspec = unsafe { mem::zeroed() };
194
195        // SAFETY: Safe because this doesn't modify any memory and we check the return value.
196        let ret = unsafe { timerfd_settime(self.as_raw_fd(), 0, &spec, ptr::null_mut()) };
197        if ret < 0 {
198            return errno_result();
199        }
200
201        Ok(())
202    }
203}
204
205impl AsRawFd for TimerFd {
206    fn as_raw_fd(&self) -> RawFd {
207        self.0.as_raw_fd()
208    }
209}
210
211impl FromRawFd for TimerFd {
212    /// This function is unsafe as the primitives currently returned
213    /// have the contract that they are the sole owner of the file
214    /// descriptor they are wrapping. Usage of this function could
215    /// accidentally allow violating this contract which can cause memory
216    /// unsafety in code that relies on it being true.
217    unsafe fn from_raw_fd(fd: RawFd) -> Self {
218        TimerFd(File::from_raw_fd(fd))
219    }
220}
221
222impl IntoRawFd for TimerFd {
223    fn into_raw_fd(self) -> RawFd {
224        self.0.into_raw_fd()
225    }
226}
227
228#[cfg(test)]
229mod tests {
230    #![allow(clippy::undocumented_unsafe_blocks)]
231    use super::*;
232    use std::thread::sleep;
233    use std::time::{Duration, Instant};
234
235    #[test]
236    fn test_from_raw_fd() {
237        let ret = unsafe { timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC) };
238        let tfd = unsafe { TimerFd::from_raw_fd(ret) };
239        assert!(!tfd.is_armed().unwrap());
240    }
241
242    #[test]
243    fn test_into_raw_fd() {
244        let tfd = TimerFd::new().expect("failed to create timerfd");
245        let fd = tfd.into_raw_fd();
246        assert!(fd > 0);
247    }
248    #[test]
249    fn test_one_shot() {
250        let mut tfd = TimerFd::new().expect("failed to create timerfd");
251        assert!(!tfd.is_armed().unwrap());
252
253        let dur = Duration::from_millis(200);
254        let now = Instant::now();
255        tfd.reset(dur, None).expect("failed to arm timer");
256
257        assert!(tfd.is_armed().unwrap());
258
259        let count = tfd.wait().expect("unable to wait for timer");
260
261        assert_eq!(count, 1);
262        assert!(now.elapsed() >= dur);
263        tfd.clear().expect("unable to clear the timer");
264        assert!(!tfd.is_armed().unwrap());
265    }
266
267    #[test]
268    fn test_repeating() {
269        let mut tfd = TimerFd::new().expect("failed to create timerfd");
270
271        let dur = Duration::from_millis(200);
272        let interval = Duration::from_millis(100);
273        tfd.reset(dur, Some(interval)).expect("failed to arm timer");
274
275        sleep(dur * 3);
276
277        let count = tfd.wait().expect("unable to wait for timer");
278        assert!(count >= 5, "count = {}", count);
279        tfd.clear().expect("unable to clear the timer");
280        assert!(!tfd.is_armed().unwrap());
281    }
282}