• 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(&mut self, duration: Duration)21     pub fn reset(&mut self, duration: Duration) {
22         self.fd
23             .get_ref()
24             .set(Expiration::OneShot(TimeSpec::from(duration)), TimerSetTimeFlags::empty())
25             .unwrap();
26     }
27 
28     /// Stop the alarm if it is currently started
cancel(&mut self)29     pub fn cancel(&mut self) {
30         self.reset(Duration::from_millis(0));
31     }
32 
33     /// Completes when the alarm has expired
expired(&mut self)34     pub async fn expired(&mut self) {
35         let mut read_ready = self.fd.readable().await.unwrap();
36         read_ready.clear_ready();
37         drop(read_ready);
38         // Will not block, since we have confirmed it is readable
39         self.fd.get_ref().wait().unwrap();
40     }
41 }
42 
43 impl Default for Alarm {
default() -> Self44     fn default() -> Self {
45         Alarm::new()
46     }
47 }
48 
49 /// Similar to tokio's interval, except the first tick does *not* complete immediately
interval(period: Duration) -> Interval50 pub fn interval(period: Duration) -> Interval {
51     let timer = TimerFd::new(get_clock(), TimerFlags::empty()).unwrap();
52     timer.set(Expiration::Interval(TimeSpec::from(period)), TimerSetTimeFlags::empty()).unwrap();
53 
54     Interval { fd: AsyncFd::new(timer).unwrap() }
55 }
56 
57 /// Future returned by interval()
58 pub struct Interval {
59     fd: AsyncFd<TimerFd>,
60 }
61 
62 impl Interval {
63     /// Call this to get the future for the next tick of the interval
tick(&mut self)64     pub async fn tick(&mut self) {
65         let mut read_ready = self.fd.readable().await.unwrap();
66         read_ready.clear_ready();
67         drop(read_ready);
68         // Will not block, since we have confirmed it is readable
69         self.fd.get_ref().wait().unwrap();
70     }
71 }
72 
get_clock() -> ClockId73 fn get_clock() -> ClockId {
74     if cfg!(target_os = "android") {
75         ClockId::CLOCK_BOOTTIME_ALARM
76     } else {
77         ClockId::CLOCK_BOOTTIME
78     }
79 }
80 
81 #[cfg(test)]
82 mod tests {
83     use super::interval;
84     use super::Alarm;
85     use crate::assert_near;
86     use std::time::{Duration, Instant};
87 
88     #[test]
alarm_simple_case()89     fn alarm_simple_case() {
90         let runtime = tokio::runtime::Runtime::new().unwrap();
91         runtime.block_on(async {
92             let timer = Instant::now();
93             let mut alarm = Alarm::new();
94             alarm.reset(Duration::from_millis(10));
95             alarm.expired().await;
96 
97             assert_near!(timer.elapsed().as_millis(), 10, 3);
98         });
99     }
100 
101     #[test]
alarm_clear_ready_after_expired()102     fn alarm_clear_ready_after_expired() {
103         // After an alarm expired, we need to make sure we clear ready from AsyncFdReadyGuard.
104         // Otherwise it's still ready and select! won't work.
105         let runtime = tokio::runtime::Runtime::new().unwrap();
106         runtime.block_on(async {
107             let timer = Instant::now();
108             let mut alarm = Alarm::new();
109             alarm.reset(Duration::from_millis(10));
110             alarm.expired().await;
111             let ready_in_10_ms = async {
112                 tokio::time::sleep(Duration::from_millis(10)).await;
113             };
114             tokio::select! {
115                 _ = alarm.expired() => (),
116                 _ = ready_in_10_ms => (),
117             }
118             assert_near!(timer.elapsed().as_millis(), 20, 3);
119         });
120     }
121 
122     #[test]
interval_schedule_and_then_drop()123     fn interval_schedule_and_then_drop() {
124         let runtime = tokio::runtime::Runtime::new().unwrap();
125         runtime.block_on(async {
126             interval(Duration::from_millis(10));
127         });
128     }
129 
130     #[test]
interval_simple_case()131     fn interval_simple_case() {
132         let runtime = tokio::runtime::Runtime::new().unwrap();
133         runtime.block_on(async {
134             let timer = Instant::now();
135             let mut interval = interval(Duration::from_millis(10));
136 
137             for n in 1..10 {
138                 interval.tick().await;
139                 println!("{}", n);
140                 assert_near!(timer.elapsed().as_millis(), 10 * n, 3);
141             }
142         });
143     }
144 }
145