alarms/
emu.rs

1// Copyright 2025 The Fuchsia 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//
5use crate::{signal, SawResponseFut, TimerConfig, TimerOps, TimerOpsError};
6use async_trait::async_trait;
7use futures::channel::mpsc;
8use futures::sink::SinkExt;
9use futures::{select, Future, StreamExt};
10use log::debug;
11use scopeguard::defer;
12use std::cell::RefCell;
13use std::pin::Pin;
14use std::rc::Rc;
15use {fidl_fuchsia_hardware_hrtimer as ffhh, fuchsia_async as fasync};
16
17// TimerOps for platforms that do not support wake alarms.
18//
19// On such platforms, we only signal on timers, and expect that the platform
20// is always awake, and can not go to sleep.
21#[derive(Debug)]
22pub(crate) struct EmulationTimerOps {
23    // When populated, indicates that a timer is active. The timer protocol forbids calling
24    // start_and_wait twice, so this is used to check for protocol errors.
25    stop_snd: Rc<RefCell<Option<mpsc::Sender<()>>>>,
26}
27
28impl EmulationTimerOps {
29    pub(crate) fn new() -> Self {
30        Self { stop_snd: Rc::new(RefCell::new(None)) }
31    }
32}
33
34#[async_trait(?Send)]
35impl TimerOps for EmulationTimerOps {
36    async fn stop(&self, _id: u64) {
37        if let Some(snd) = self.stop_snd.borrow().as_ref() {
38            let mut snd_clone = snd.clone();
39            snd_clone.send(()).await.expect("always able to send")
40        }
41        // Mark the timer as inactive.
42        let _ = self.stop_snd.borrow_mut().take();
43    }
44
45    async fn get_timer_properties(&self) -> TimerConfig {
46        fasync::Timer::new(fasync::BootInstant::after(zx::BootDuration::ZERO)).await; // yield.
47        TimerConfig::new_from_data(0, &[zx::BootDuration::from_nanos(1000)], i64::MAX as u64)
48    }
49
50    fn start_and_wait(
51        &self,
52        id: u64,
53        resolution: &ffhh::Resolution,
54        ticks: u64,
55        setup_event: zx::Event,
56    ) -> std::pin::Pin<Box<dyn SawResponseFut>> {
57        let ticks: i64 = ticks.try_into().unwrap();
58        if let Some(_) = *self.stop_snd.borrow() {
59            // We already have one timer going on, this is bad state.
60            return Box::pin(ForwardingFuture {
61                inner_future: Box::pin(async move {
62                    Err(TimerOpsError::Driver(ffhh::DriverError::BadState))
63                }),
64            });
65        }
66        defer!(
67                signal(&setup_event);
68                debug!("emu/start_and_wait: START: setup_event signaled");
69        );
70        let (stop_snd, mut stop_rcv) = mpsc::channel(1);
71        let tick_duration = match resolution {
72            ffhh::Resolution::Duration(d) => *d,
73            _ => 0,
74        };
75        let sleep_duration = zx::BootDuration::from_nanos(ticks * tick_duration);
76        let post_cancel = self.stop_snd.clone();
77        let fut = Box::pin(async move {
78            defer! {
79                // Mark the timer as inactive once the future completes.
80                let _ = post_cancel.borrow_mut().take();
81            };
82            let ret = select! {
83                _ = fasync::Timer::new(fasync::BootInstant::after(sleep_duration)) => {
84                    let (one, _) = zx::EventPair::create();
85                    Ok(one)
86                },
87                _ = stop_rcv.next() => {
88                    debug!("CANCELED: timer {id} canceled");
89                    Err(TimerOpsError::Driver(ffhh::DriverError::Canceled))
90                },
91            };
92            ret
93        });
94        *self.stop_snd.borrow_mut() = Some(stop_snd);
95        Box::pin(ForwardingFuture { inner_future: Box::pin(fut) })
96    }
97}
98
99struct ForwardingFuture<F: Future> {
100    inner_future: Pin<Box<F>>,
101}
102
103use std::task::{Context, Poll};
104type FRet = Result<zx::EventPair, TimerOpsError>;
105impl<F: Future<Output = FRet>> SawResponseFut for ForwardingFuture<F> {}
106impl<F: Future<Output = FRet>> Future for ForwardingFuture<F> {
107    type Output = F::Output;
108    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
109        self.inner_future.as_mut().poll(cx)
110    }
111}
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116    use crate::clone_handle;
117    use assert_matches::assert_matches;
118    use fidl::AsHandleRef;
119    use std::task::Poll;
120
121    const FAKE_ID: u64 = 42;
122
123    #[fuchsia::test]
124    fn test_alarm_triggers() {
125        let mut executor = fasync::TestExecutor::new_with_fake_time();
126        executor.set_fake_time(fasync::MonotonicInstant::from_nanos(0));
127        let ops = EmulationTimerOps::new();
128        let setup_event = zx::Event::create();
129
130        // Event is not signaled at the beginning.
131        let maybe_signaled = setup_event
132            .as_handle_ref()
133            .wait_handle(zx::Signals::EVENT_SIGNALED, zx::MonotonicInstant::INFINITE_PAST);
134        assert_matches!(maybe_signaled, zx::WaitResult::TimedOut(zx::Signals::NONE));
135
136        let mut start_fut = ops.start_and_wait(
137            FAKE_ID,
138            // 10us total.
139            &ffhh::Resolution::Duration(1000),
140            10,
141            clone_handle(&setup_event),
142        );
143
144        // Try polling now - future isn't ready yet.
145        let res = executor.run_until_stalled(&mut start_fut);
146        assert_matches!(res, Poll::Pending);
147
148        // Check that we signaled setup_event, so that callers can unblock.
149        let maybe_signaled = setup_event
150            .as_handle_ref()
151            .wait_handle(zx::Signals::EVENT_SIGNALED, zx::MonotonicInstant::INFINITE_PAST);
152        assert_matches!(maybe_signaled, zx::WaitResult::Ok(zx::Signals::EVENT_SIGNALED));
153
154        // Poll after 5us - future isn't ready yet.
155        executor.set_fake_time(fasync::MonotonicInstant::from_nanos(5_000));
156        executor.wake_expired_timers();
157        let res = executor.run_until_stalled(&mut start_fut);
158        assert_matches!(res, Poll::Pending);
159
160        // Poll past 10us - future is ready.
161        executor.set_fake_time(fasync::MonotonicInstant::from_nanos(11_000));
162        executor.wake_expired_timers();
163        let res = executor.run_until_stalled(&mut start_fut);
164        assert_matches!(res, Poll::Ready(Ok(_)));
165    }
166
167    #[fuchsia::test]
168    fn test_alarm_cancelation() {
169        let mut executor = fasync::TestExecutor::new_with_fake_time();
170        executor.set_fake_time(fasync::MonotonicInstant::from_nanos(0));
171        let ops = EmulationTimerOps::new();
172        let setup_event = zx::Event::create();
173        let mut start_fut = ops.start_and_wait(
174            FAKE_ID,
175            // 10us total.
176            &ffhh::Resolution::Duration(1000),
177            10,
178            clone_handle(&setup_event),
179        );
180
181        // Try polling now - future isn't ready yet.
182        let res = executor.run_until_stalled(&mut start_fut);
183        assert_matches!(res, Poll::Pending);
184
185        executor.set_fake_time(fasync::MonotonicInstant::from_nanos(5_000));
186        executor.wake_expired_timers();
187        let res = executor.run_until_stalled(&mut start_fut);
188        assert_matches!(res, Poll::Pending);
189
190        let mut stop_fut = ops.stop(FAKE_ID);
191        let res = executor.run_until_stalled(&mut stop_fut);
192        assert_matches!(res, Poll::Ready(_));
193
194        // Attempt to poll now and note that the alarm is now canceled.
195        let res = executor.run_until_stalled(&mut start_fut);
196        assert_matches!(res, Poll::Ready(Err(TimerOpsError::Driver(ffhh::DriverError::Canceled))));
197    }
198
199    #[fuchsia::test]
200    fn test_alarm_start_twice_is_error() {
201        let mut executor = fasync::TestExecutor::new_with_fake_time();
202        executor.set_fake_time(fasync::MonotonicInstant::from_nanos(0));
203        let ops = EmulationTimerOps::new();
204        let setup_event = zx::Event::create();
205        let mut start_fut = ops.start_and_wait(
206            FAKE_ID,
207            // 10us total.
208            &ffhh::Resolution::Duration(1000), // 1us per tick
209            10,
210            clone_handle(&setup_event),
211        );
212
213        // Try polling now - future isn't ready yet.
214        let res = executor.run_until_stalled(&mut start_fut);
215        assert_matches!(res, Poll::Pending);
216
217        // Try to start another time while one is already active, is an error.
218        let mut start_fut_2 = ops.start_and_wait(
219            FAKE_ID,
220            &ffhh::Resolution::Duration(1000),
221            10,
222            clone_handle(&setup_event),
223        );
224        let res = executor.run_until_stalled(&mut start_fut_2);
225        assert_matches!(res, Poll::Ready(Err(TimerOpsError::Driver(ffhh::DriverError::BadState))));
226    }
227
228    #[fuchsia::test]
229    fn test_alarm_start_stop_start() {
230        let mut executor = fasync::TestExecutor::new_with_fake_time();
231        executor.set_fake_time(fasync::MonotonicInstant::from_nanos(0));
232        let ops = EmulationTimerOps::new();
233        let setup_event = zx::Event::create();
234        let mut start_fut = ops.start_and_wait(
235            FAKE_ID,
236            // 10us total.
237            &ffhh::Resolution::Duration(1000),
238            10,
239            clone_handle(&setup_event),
240        );
241
242        executor.set_fake_time(fasync::MonotonicInstant::from_nanos(5_000));
243        executor.wake_expired_timers();
244        let res = executor.run_until_stalled(&mut start_fut);
245        assert_matches!(res, Poll::Pending);
246
247        let mut stop_fut = ops.stop(FAKE_ID);
248        let res = executor.run_until_stalled(&mut stop_fut);
249        assert_matches!(res, Poll::Ready(_));
250
251        // Start a new alarm.
252        let mut start_fut = ops.start_and_wait(
253            FAKE_ID,
254            // 10us total.
255            &ffhh::Resolution::Duration(1000),
256            10,
257            clone_handle(&setup_event),
258        );
259
260        let res = executor.run_until_stalled(&mut start_fut);
261        assert_matches!(res, Poll::Pending);
262
263        executor.set_fake_time(fasync::MonotonicInstant::from_nanos(50_000));
264        executor.wake_expired_timers();
265        let res = executor.run_until_stalled(&mut start_fut);
266        assert_matches!(res, Poll::Ready(_));
267    }
268
269    #[fuchsia::test]
270    fn test_alarm_start_expire_start() {
271        let mut executor = fasync::TestExecutor::new_with_fake_time();
272        executor.set_fake_time(fasync::MonotonicInstant::from_nanos(0));
273        let ops = EmulationTimerOps::new();
274        let setup_event = zx::Event::create();
275        let mut start_fut = ops.start_and_wait(
276            FAKE_ID,
277            // 10us total.
278            &ffhh::Resolution::Duration(1000),
279            10,
280            clone_handle(&setup_event),
281        );
282        let res = executor.run_until_stalled(&mut start_fut);
283        assert_matches!(res, Poll::Pending);
284
285        executor.set_fake_time(fasync::MonotonicInstant::from_nanos(20_000));
286        executor.wake_expired_timers();
287        let res = executor.run_until_stalled(&mut start_fut);
288        assert_matches!(res, Poll::Ready(_));
289
290        // Start a new alarm.
291        let mut start_fut = ops.start_and_wait(
292            FAKE_ID,
293            // 10us total.
294            &ffhh::Resolution::Duration(1000),
295            10,
296            clone_handle(&setup_event),
297        );
298        let res = executor.run_until_stalled(&mut start_fut);
299        assert_matches!(res, Poll::Pending);
300
301        // Ensure the timer expires, and check that it has expired.
302        executor.set_fake_time(fasync::MonotonicInstant::from_nanos(50_000));
303        executor.wake_expired_timers();
304        let res = executor.run_until_stalled(&mut start_fut);
305        assert_matches!(res, Poll::Ready(_));
306    }
307}