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::os::fd::{AsFd, AsRawFd, RawFd};
6 use std::time::Duration;
7 use tokio::io::unix::AsyncFd;
8 
9 /// A wrapper for `TimerFd` which implements `AsRawFd`.
10 #[derive(Debug)]
11 struct TimerFdWrapper(TimerFd);
12 
13 impl TimerFdWrapper {
get(&self) -> nix::Result<Option<Expiration>>14     fn get(&self) -> nix::Result<Option<Expiration>> {
15         self.0.get()
16     }
17 
set(&self, expiration: Expiration, flags: TimerSetTimeFlags) -> nix::Result<()>18     fn set(&self, expiration: Expiration, flags: TimerSetTimeFlags) -> nix::Result<()> {
19         self.0.set(expiration, flags)
20     }
21 
wait(&self) -> nix::Result<()>22     fn wait(&self) -> nix::Result<()> {
23         self.0.wait()
24     }
25 }
26 
27 impl AsRawFd for TimerFdWrapper {
as_raw_fd(&self) -> RawFd28     fn as_raw_fd(&self) -> RawFd {
29         self.0.as_fd().as_raw_fd()
30     }
31 }
32 
33 /// A single shot Alarm
34 pub struct Alarm {
35     fd: AsyncFd<TimerFdWrapper>,
36 }
37 
38 impl Alarm {
39     /// Construct a new alarm
new() -> Self40     pub fn new() -> Self {
41         let timer = TimerFd::new(get_clock(), TimerFlags::empty()).unwrap();
42         Self { fd: AsyncFd::new(TimerFdWrapper(timer)).unwrap() }
43     }
44 
45     /// Reset the alarm to duration, starting from now
reset(&self, duration: Duration)46     pub fn reset(&self, duration: Duration) {
47         self.fd
48             .get_ref()
49             .set(Expiration::OneShot(TimeSpec::from(duration)), TimerSetTimeFlags::empty())
50             .unwrap();
51     }
52 
53     /// Stop the alarm if it is currently started
cancel(&self)54     pub fn cancel(&self) {
55         self.reset(Duration::from_millis(0));
56     }
57 
58     /// Completes when the alarm has expired
expired(&self)59     pub async fn expired(&self) {
60         let mut read_ready = self.fd.readable().await.unwrap();
61         read_ready.clear_ready();
62         drop(read_ready);
63         // Will not block, since we have confirmed it is readable
64         if self.fd.get_ref().get().unwrap().is_some() {
65             self.fd.get_ref().wait().unwrap();
66         }
67     }
68 }
69 
70 impl Default for Alarm {
default() -> Self71     fn default() -> Self {
72         Alarm::new()
73     }
74 }
75 
76 /// Similar to tokio's interval, except the first tick does *not* complete immediately
interval(period: Duration) -> Interval77 pub fn interval(period: Duration) -> Interval {
78     let timer = TimerFd::new(get_clock(), TimerFlags::empty()).unwrap();
79     timer.set(Expiration::Interval(TimeSpec::from(period)), TimerSetTimeFlags::empty()).unwrap();
80 
81     Interval { fd: AsyncFd::new(TimerFdWrapper(timer)).unwrap() }
82 }
83 
84 /// Future returned by interval()
85 pub struct Interval {
86     fd: AsyncFd<TimerFdWrapper>,
87 }
88 
89 impl Interval {
90     /// Call this to get the future for the next tick of the interval
tick(&mut self)91     pub async fn tick(&mut self) {
92         let mut read_ready = self.fd.readable().await.unwrap();
93         read_ready.clear_ready();
94         drop(read_ready);
95         // Will not block, since we have confirmed it is readable
96         if self.fd.get_ref().get().unwrap().is_some() {
97             self.fd.get_ref().wait().unwrap();
98         }
99     }
100 }
101 
get_clock() -> ClockId102 fn get_clock() -> ClockId {
103     if cfg!(target_os = "android") {
104         ClockId::CLOCK_BOOTTIME_ALARM
105     } else {
106         ClockId::CLOCK_BOOTTIME
107     }
108 }
109 
110 #[cfg(test)]
111 mod tests {
112     use super::interval;
113     use super::Alarm;
114     use crate::assert_near;
115     use std::time::{Duration, Instant};
116 
117     #[test]
alarm_cancel_after_expired()118     fn alarm_cancel_after_expired() {
119         let runtime = tokio::runtime::Runtime::new().unwrap();
120         runtime.block_on(async {
121             let alarm = Alarm::new();
122             alarm.reset(Duration::from_millis(10));
123             tokio::time::sleep(Duration::from_millis(30)).await;
124             alarm.cancel();
125 
126             for _ in 0..10 {
127                 let ready_in_10_ms = async {
128                     tokio::time::sleep(Duration::from_millis(10)).await;
129                 };
130 
131                 tokio::select! {
132                     _ = alarm.expired() => (),
133                     _ = ready_in_10_ms => (),
134                 }
135             }
136         });
137     }
138 
139     #[test]
alarm_clear_ready_after_expired()140     fn alarm_clear_ready_after_expired() {
141         // After an alarm expired, we need to make sure we clear ready from AsyncFdReadyGuard.
142         // Otherwise it's still ready and select! won't work.
143         let runtime = tokio::runtime::Runtime::new().unwrap();
144         runtime.block_on(async {
145             let timer = Instant::now();
146             let alarm = Alarm::new();
147             alarm.reset(Duration::from_millis(10));
148             alarm.expired().await;
149             let ready_in_10_ms = async {
150                 tokio::time::sleep(Duration::from_millis(10)).await;
151             };
152             tokio::select! {
153                 _ = alarm.expired() => (),
154                 _ = ready_in_10_ms => (),
155             }
156             assert_near!(timer.elapsed().as_millis(), 20, 3);
157         });
158     }
159 
160     #[test]
interval_schedule_and_then_drop()161     fn interval_schedule_and_then_drop() {
162         let runtime = tokio::runtime::Runtime::new().unwrap();
163         runtime.block_on(async {
164             interval(Duration::from_millis(10));
165         });
166     }
167 }
168