mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2025-02-06 10:32:10 +00:00
a83356452c
Use X::from() instead of as X. Signed-off-by: Dylan Reid <dgreid@chromium.org> Change-Id: Iefd412a4846d7f9bede57b68807e09cb43a5c579 Reviewed-on: https://chromium-review.googlesource.com/1510066 Tested-by: kokoro <noreply+kokoro@google.com>
292 lines
9.6 KiB
Rust
292 lines
9.6 KiB
Rust
// Copyright 2018 The Chromium OS Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
use std::fs::File;
|
|
use std::mem;
|
|
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
|
|
use std::ptr;
|
|
use std::sync::Arc;
|
|
use std::time::Duration;
|
|
use sync::Mutex;
|
|
|
|
use libc::{self, timerfd_create, timerfd_gettime, timerfd_settime, CLOCK_MONOTONIC, TFD_CLOEXEC};
|
|
|
|
use crate::{errno_result, EventFd, FakeClock, Result};
|
|
|
|
/// A safe wrapper around a Linux timerfd (man 2 timerfd_create).
|
|
pub struct TimerFd(File);
|
|
|
|
impl TimerFd {
|
|
/// Creates a new timerfd. The timer is initally disarmed and must be armed by calling
|
|
/// `reset`.
|
|
pub fn new() -> Result<TimerFd> {
|
|
// Safe because this doesn't modify any memory and we check the return value.
|
|
let ret = unsafe { timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC) };
|
|
if ret < 0 {
|
|
return errno_result();
|
|
}
|
|
|
|
// Safe because we uniquely own the file descriptor.
|
|
Ok(TimerFd(unsafe { File::from_raw_fd(ret) }))
|
|
}
|
|
|
|
/// Sets the timer to expire after `dur`. If `interval` is not `None` it represents
|
|
/// the period for repeated expirations after the initial expiration. Otherwise
|
|
/// the timer will expire just once. Cancels any existing duration and repeating interval.
|
|
pub fn reset(&mut self, dur: Duration, interval: Option<Duration>) -> Result<()> {
|
|
// Safe because we are zero-initializing a struct with only primitive member fields.
|
|
let mut spec: libc::itimerspec = unsafe { mem::zeroed() };
|
|
spec.it_value.tv_sec = dur.as_secs() as libc::time_t;
|
|
// nsec always fits in i32 because subsec_nanos is defined to be less than one billion.
|
|
let nsec = dur.subsec_nanos() as i32;
|
|
spec.it_value.tv_nsec = libc::c_long::from(nsec);
|
|
|
|
if let Some(int) = interval {
|
|
spec.it_interval.tv_sec = int.as_secs() as libc::time_t;
|
|
// nsec always fits in i32 because subsec_nanos is defined to be less than one billion.
|
|
let nsec = int.subsec_nanos() as i32;
|
|
spec.it_interval.tv_nsec = libc::c_long::from(nsec);
|
|
}
|
|
|
|
// Safe because this doesn't modify any memory and we check the return value.
|
|
let ret = unsafe { timerfd_settime(self.as_raw_fd(), 0, &spec, ptr::null_mut()) };
|
|
if ret < 0 {
|
|
return errno_result();
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Waits until the timer expires. The return value represents the number of times the timer
|
|
/// has expired since the last time `wait` was called. If the timer has not yet expired once
|
|
/// this call will block until it does.
|
|
pub fn wait(&mut self) -> Result<u64> {
|
|
let mut count = 0u64;
|
|
|
|
// Safe because this will only modify |buf| and we check the return value.
|
|
let ret = unsafe {
|
|
libc::read(
|
|
self.as_raw_fd(),
|
|
&mut count as *mut _ as *mut libc::c_void,
|
|
mem::size_of_val(&count),
|
|
)
|
|
};
|
|
if ret < 0 {
|
|
return errno_result();
|
|
}
|
|
|
|
// The bytes in the buffer are guaranteed to be in native byte-order so we don't need to
|
|
// use from_le or from_be.
|
|
Ok(count)
|
|
}
|
|
|
|
/// Returns `true` if the timer is currently armed.
|
|
pub fn is_armed(&self) -> Result<bool> {
|
|
// Safe because we are zero-initializing a struct with only primitive member fields.
|
|
let mut spec: libc::itimerspec = unsafe { mem::zeroed() };
|
|
|
|
// Safe because timerfd_gettime is trusted to only modify `spec`.
|
|
let ret = unsafe { timerfd_gettime(self.as_raw_fd(), &mut spec) };
|
|
if ret < 0 {
|
|
return errno_result();
|
|
}
|
|
|
|
Ok(spec.it_value.tv_sec != 0 || spec.it_value.tv_nsec != 0)
|
|
}
|
|
|
|
/// Disarms the timer.
|
|
pub fn clear(&mut self) -> Result<()> {
|
|
// Safe because we are zero-initializing a struct with only primitive member fields.
|
|
let spec: libc::itimerspec = unsafe { mem::zeroed() };
|
|
|
|
// Safe because this doesn't modify any memory and we check the return value.
|
|
let ret = unsafe { timerfd_settime(self.as_raw_fd(), 0, &spec, ptr::null_mut()) };
|
|
if ret < 0 {
|
|
return errno_result();
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl AsRawFd for TimerFd {
|
|
fn as_raw_fd(&self) -> RawFd {
|
|
self.0.as_raw_fd()
|
|
}
|
|
}
|
|
|
|
impl FromRawFd for TimerFd {
|
|
unsafe fn from_raw_fd(fd: RawFd) -> Self {
|
|
TimerFd(File::from_raw_fd(fd))
|
|
}
|
|
}
|
|
|
|
impl IntoRawFd for TimerFd {
|
|
fn into_raw_fd(self) -> RawFd {
|
|
self.0.into_raw_fd()
|
|
}
|
|
}
|
|
|
|
/// FakeTimerFd: For use in tests.
|
|
pub struct FakeTimerFd {
|
|
clock: Arc<Mutex<FakeClock>>,
|
|
deadline_ns: Option<u64>,
|
|
interval: Option<Duration>,
|
|
fd: EventFd,
|
|
}
|
|
|
|
impl FakeTimerFd {
|
|
/// Creates a new fake timerfd. The timer is initally disarmed and must be armed by calling
|
|
/// `reset`.
|
|
pub fn new(clock: Arc<Mutex<FakeClock>>) -> Self {
|
|
FakeTimerFd {
|
|
clock,
|
|
deadline_ns: None,
|
|
interval: None,
|
|
fd: EventFd::new().unwrap(),
|
|
}
|
|
}
|
|
|
|
fn duration_to_nanos(d: Duration) -> u64 {
|
|
d.as_secs() * 1_000_000_000 + u64::from(d.subsec_nanos())
|
|
}
|
|
|
|
/// Sets the timer to expire after `dur`. If `interval` is not `None` it represents
|
|
/// the period for repeated expirations after the initial expiration. Otherwise
|
|
/// the timer will expire just once. Cancels any existing duration and repeating interval.
|
|
pub fn reset(&mut self, dur: Duration, interval: Option<Duration>) -> Result<()> {
|
|
let mut guard = self.clock.lock();
|
|
let deadline = guard.nanos() + FakeTimerFd::duration_to_nanos(dur);
|
|
self.deadline_ns = Some(deadline);
|
|
self.interval = interval;
|
|
guard.add_event_fd(deadline, self.fd.try_clone()?);
|
|
Ok(())
|
|
}
|
|
|
|
/// Waits until the timer expires. The return value represents the number of times the timer
|
|
/// has expired since the last time `wait` was called. If the timer has not yet expired once
|
|
/// this call will block until it does.
|
|
pub fn wait(&mut self) -> Result<u64> {
|
|
loop {
|
|
self.fd.read()?;
|
|
if let Some(ref mut deadline_ns) = self.deadline_ns {
|
|
let mut guard = self.clock.lock();
|
|
let now = guard.nanos();
|
|
if now >= *deadline_ns {
|
|
let mut expirys = 0;
|
|
if let Some(interval) = self.interval {
|
|
let interval_ns = FakeTimerFd::duration_to_nanos(interval);
|
|
if interval_ns > 0 {
|
|
expirys += (now - *deadline_ns) / interval_ns;
|
|
*deadline_ns += (expirys + 1) * interval_ns;
|
|
guard.add_event_fd(*deadline_ns, self.fd.try_clone()?);
|
|
}
|
|
}
|
|
return Ok(expirys + 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Returns `true` if the timer is currently armed.
|
|
pub fn is_armed(&self) -> Result<bool> {
|
|
Ok(self.deadline_ns.is_some())
|
|
}
|
|
|
|
/// Disarms the timer.
|
|
pub fn clear(&mut self) -> Result<()> {
|
|
self.deadline_ns = None;
|
|
self.interval = None;
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl AsRawFd for FakeTimerFd {
|
|
fn as_raw_fd(&self) -> RawFd {
|
|
self.fd.as_raw_fd()
|
|
}
|
|
}
|
|
|
|
impl IntoRawFd for FakeTimerFd {
|
|
fn into_raw_fd(self) -> RawFd {
|
|
self.fd.into_raw_fd()
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use std::thread::sleep;
|
|
use std::time::{Duration, Instant};
|
|
|
|
#[test]
|
|
fn one_shot() {
|
|
let mut tfd = TimerFd::new().expect("failed to create timerfd");
|
|
assert_eq!(tfd.is_armed().unwrap(), false);
|
|
|
|
let dur = Duration::from_millis(200);
|
|
let now = Instant::now();
|
|
tfd.reset(dur.clone(), None).expect("failed to arm timer");
|
|
|
|
assert_eq!(tfd.is_armed().unwrap(), true);
|
|
|
|
let count = tfd.wait().expect("unable to wait for timer");
|
|
|
|
assert_eq!(count, 1);
|
|
assert!(now.elapsed() >= dur);
|
|
}
|
|
|
|
#[test]
|
|
fn repeating() {
|
|
let mut tfd = TimerFd::new().expect("failed to create timerfd");
|
|
|
|
let dur = Duration::from_millis(200);
|
|
let interval = Duration::from_millis(100);
|
|
tfd.reset(dur.clone(), Some(interval))
|
|
.expect("failed to arm timer");
|
|
|
|
sleep(dur * 3);
|
|
|
|
let count = tfd.wait().expect("unable to wait for timer");
|
|
assert!(count >= 5, "count = {}", count);
|
|
}
|
|
|
|
#[test]
|
|
fn fake_one_shot() {
|
|
let clock = Arc::new(Mutex::new(FakeClock::new()));
|
|
let mut tfd = FakeTimerFd::new(clock.clone());
|
|
assert_eq!(tfd.is_armed().unwrap(), false);
|
|
|
|
let dur = Duration::from_nanos(200);
|
|
tfd.reset(dur.clone(), None).expect("failed to arm timer");
|
|
|
|
assert_eq!(tfd.is_armed().unwrap(), true);
|
|
clock.lock().add_ns(200);
|
|
|
|
let count = tfd.wait().expect("unable to wait for timer");
|
|
|
|
assert_eq!(count, 1);
|
|
}
|
|
|
|
#[test]
|
|
fn fake_repeating() {
|
|
let clock = Arc::new(Mutex::new(FakeClock::new()));
|
|
let mut tfd = FakeTimerFd::new(clock.clone());
|
|
|
|
let dur = Duration::from_nanos(200);
|
|
let interval = Duration::from_nanos(100);
|
|
tfd.reset(dur.clone(), Some(interval))
|
|
.expect("failed to arm timer");
|
|
|
|
clock.lock().add_ns(300);
|
|
|
|
let mut count = tfd.wait().expect("unable to wait for timer");
|
|
// An expiration from the initial expiry and from 1 repeat.
|
|
assert_eq!(count, 2);
|
|
|
|
clock.lock().add_ns(300);
|
|
count = tfd.wait().expect("unable to wait for timer");
|
|
assert_eq!(count, 3);
|
|
}
|
|
}
|