1 // Copyright 2020 The Chromium OS Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 use std::time::Duration; 6 7 use sys_util::{Result as SysResult, TimerFd}; 8 9 use super::{AsyncResult, Error, Executor, IntoAsync, IoSourceExt}; 10 11 #[cfg(test)] 12 use super::{FdExecutor, URingExecutor}; 13 14 /// An async version of sys_util::TimerFd. 15 pub struct TimerAsync { 16 io_source: Box<dyn IoSourceExt<TimerFd>>, 17 } 18 19 impl TimerAsync { new(timer: TimerFd, ex: &Executor) -> AsyncResult<TimerAsync>20 pub fn new(timer: TimerFd, ex: &Executor) -> AsyncResult<TimerAsync> { 21 ex.async_from(timer) 22 .map(|io_source| TimerAsync { io_source }) 23 } 24 25 #[cfg(test)] new_poll(timer: TimerFd, ex: &FdExecutor) -> AsyncResult<TimerAsync>26 pub(crate) fn new_poll(timer: TimerFd, ex: &FdExecutor) -> AsyncResult<TimerAsync> { 27 super::executor::async_poll_from(timer, ex).map(|io_source| TimerAsync { io_source }) 28 } 29 30 #[cfg(test)] new_uring(timer: TimerFd, ex: &URingExecutor) -> AsyncResult<TimerAsync>31 pub(crate) fn new_uring(timer: TimerFd, ex: &URingExecutor) -> AsyncResult<TimerAsync> { 32 super::executor::async_uring_from(timer, ex).map(|io_source| TimerAsync { io_source }) 33 } 34 35 /// Gets the next value from the timer. next_val(&self) -> AsyncResult<u64>36 pub async fn next_val(&self) -> AsyncResult<u64> { 37 self.io_source.read_u64().await 38 } 39 40 /// Async sleep for the given duration sleep(ex: &Executor, dur: Duration) -> std::result::Result<(), Error>41 pub async fn sleep(ex: &Executor, dur: Duration) -> std::result::Result<(), Error> { 42 let tfd = TimerFd::new().map_err(Error::TimerFd)?; 43 tfd.reset(dur, None).map_err(Error::TimerFd)?; 44 let t = TimerAsync::new(tfd, ex).map_err(Error::TimerAsync)?; 45 t.next_val().await.map_err(Error::TimerAsync)?; 46 Ok(()) 47 } 48 49 /// Sets the timer to expire after `dur`. If `interval` is not `None` it represents 50 /// the period for repeated expirations after the initial expiration. Otherwise 51 /// the timer will expire just once. Cancels any existing duration and repeating interval. reset(&mut self, dur: Duration, interval: Option<Duration>) -> SysResult<()>52 pub fn reset(&mut self, dur: Duration, interval: Option<Duration>) -> SysResult<()> { 53 self.io_source.as_source_mut().reset(dur, interval) 54 } 55 } 56 57 impl IntoAsync for TimerFd {} 58 59 #[cfg(test)] 60 mod tests { 61 use super::{super::uring_executor::use_uring, *}; 62 use std::time::{Duration, Instant}; 63 64 #[test] one_shot()65 fn one_shot() { 66 if !use_uring() { 67 return; 68 } 69 70 async fn this_test(ex: &URingExecutor) { 71 let tfd = TimerFd::new().expect("failed to create timerfd"); 72 73 let dur = Duration::from_millis(200); 74 let now = Instant::now(); 75 tfd.reset(dur, None).expect("failed to arm timer"); 76 77 let t = TimerAsync::new_uring(tfd, ex).unwrap(); 78 let count = t.next_val().await.expect("unable to wait for timer"); 79 80 assert_eq!(count, 1); 81 assert!(now.elapsed() >= dur); 82 } 83 84 let ex = URingExecutor::new().unwrap(); 85 ex.run_until(this_test(&ex)).unwrap(); 86 } 87 88 #[test] one_shot_fd()89 fn one_shot_fd() { 90 async fn this_test(ex: &FdExecutor) { 91 let tfd = TimerFd::new().expect("failed to create timerfd"); 92 93 let dur = Duration::from_millis(200); 94 let now = Instant::now(); 95 tfd.reset(dur, None).expect("failed to arm timer"); 96 97 let t = TimerAsync::new_poll(tfd, ex).unwrap(); 98 let count = t.next_val().await.expect("unable to wait for timer"); 99 100 assert_eq!(count, 1); 101 assert!(now.elapsed() >= dur); 102 } 103 104 let ex = FdExecutor::new().unwrap(); 105 ex.run_until(this_test(&ex)).unwrap(); 106 } 107 108 #[test] timer()109 fn timer() { 110 async fn this_test(ex: &Executor) { 111 let dur = Duration::from_millis(200); 112 let now = Instant::now(); 113 TimerAsync::sleep(ex, dur).await.expect("unable to sleep"); 114 assert!(now.elapsed() >= dur); 115 } 116 117 let ex = Executor::new().expect("creating an executor failed"); 118 ex.run_until(this_test(&ex)).unwrap(); 119 } 120 } 121