• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Waking timers for Bluetooth. Implemented using timerfd, but supposed to feel similar to
2 ///Tokio's time
3 use nix::sys::time::TimeSpec;
4 use nix::sys::timerfd::{ClockId, Expiration, TimerFd, TimerFlags, TimerSetTimeFlags};
5 use std::time::Duration;
6 use tokio::io::unix::AsyncFd;
7 
8 /// A single shot Alarm
9 pub struct Alarm {
10     fd: AsyncFd<TimerFd>,
11 }
12 
13 impl Alarm {
14     /// Construct a new alarm
new() -> Self15     pub fn new() -> Self {
16         let timer = TimerFd::new(get_clock(), TimerFlags::empty()).unwrap();
17         Self { fd: AsyncFd::new(timer).unwrap() }
18     }
19 
20     /// Reset the alarm to duration, starting from now
reset(&self, duration: Duration)21     pub fn reset(&self, duration: Duration) {
22         self.fd
23             .get_ref()
24             .set(Expiration::OneShot(TimeSpec::from(duration)), TimerSetTimeFlags::empty())
25             .unwrap();
26     }
27 
28     /// Completes when the alarm has expired
expired(&self)29     pub async fn expired(&self) {
30         let mut read_ready =
31             self.fd.readable().await.expect("TimerFd is never expected to fail to be readable");
32         read_ready.clear_ready();
33         drop(read_ready);
34         // Will not block, since we have confirmed it is readable
35         if self.fd.get_ref().get().unwrap().is_some() {
36             self.fd.get_ref().wait().unwrap();
37         }
38     }
39 }
40 
41 impl Default for Alarm {
default() -> Self42     fn default() -> Self {
43         Alarm::new()
44     }
45 }
46 
get_clock() -> ClockId47 fn get_clock() -> ClockId {
48     if cfg!(target_os = "android") {
49         ClockId::CLOCK_BOOTTIME_ALARM
50     } else {
51         ClockId::CLOCK_BOOTTIME
52     }
53 }
54 
55 #[cfg(test)]
56 mod tests {
57     use super::Alarm;
58     use std::time::Duration;
59 
60     #[test]
alarm_cancel_after_expired()61     fn alarm_cancel_after_expired() {
62         let runtime = tokio::runtime::Runtime::new().unwrap();
63         runtime.block_on(async {
64             let alarm = Alarm::new();
65             alarm.reset(Duration::from_millis(10));
66             tokio::time::sleep(Duration::from_millis(30)).await;
67             alarm.reset(Duration::from_millis(0));
68 
69             for _ in 0..10 {
70                 let ready_in_10_ms = async {
71                     tokio::time::sleep(Duration::from_millis(10)).await;
72                 };
73 
74                 tokio::select! {
75                     _ = alarm.expired() => (),
76                     _ = ready_in_10_ms => (),
77                 }
78             }
79         });
80     }
81 
82     #[test]
alarm_clear_ready_after_expired()83     fn alarm_clear_ready_after_expired() {
84         // After an alarm expired, we need to make sure we clear ready from AsyncFdReadyGuard.
85         // Otherwise it's still ready and select! won't work.
86         let runtime = tokio::runtime::Runtime::new().unwrap();
87         runtime.block_on(async {
88             let alarm = Alarm::new();
89             alarm.reset(Duration::from_millis(10));
90             alarm.expired().await;
91             let ready_in_10_ms = async {
92                 tokio::time::sleep(Duration::from_millis(10)).await;
93             };
94             tokio::select! {
95                 _ = alarm.expired() => (),
96                 _ = ready_in_10_ms => (),
97             }
98         });
99     }
100 }
101