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::{SawResponseFut, TimerConfig, TimerOps, TimerOpsError, signal};
6use async_trait::async_trait;
7use futures::channel::mpsc;
8use futures::sink::SinkExt;
9use futures::{Future, StreamExt, select};
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 std::task::Poll;
119
120    const FAKE_ID: u64 = 42;
121
122    #[fuchsia::test]
123    fn test_alarm_triggers() {
124        let mut executor = fasync::TestExecutor::new_with_fake_time();
125        executor.set_fake_time(fasync::MonotonicInstant::from_nanos(0));
126        let ops = EmulationTimerOps::new();
127        let setup_event = zx::Event::create();
128
129        // Event is not signaled at the beginning.
130        let maybe_signaled = setup_event
131            .as_handle_ref()
132            .wait_one(zx::Signals::EVENT_SIGNALED, zx::MonotonicInstant::INFINITE_PAST);
133        assert_matches!(maybe_signaled, zx::WaitResult::TimedOut(zx::Signals::NONE));
134
135        let mut start_fut = ops.start_and_wait(
136            FAKE_ID,
137            // 10us total.
138            &ffhh::Resolution::Duration(1000),
139            10,
140            clone_handle(&setup_event),
141        );
142
143        // Try polling now - future isn't ready yet.
144        let res = executor.run_until_stalled(&mut start_fut);
145        assert_matches!(res, Poll::Pending);
146
147        // Check that we signaled setup_event, so that callers can unblock.
148        let maybe_signaled = setup_event
149            .as_handle_ref()
150            .wait_one(zx::Signals::EVENT_SIGNALED, zx::MonotonicInstant::INFINITE_PAST);
151        assert_matches!(maybe_signaled, zx::WaitResult::Ok(zx::Signals::EVENT_SIGNALED));
152
153        // Poll after 5us - future isn't ready yet.
154        executor.set_fake_time(fasync::MonotonicInstant::from_nanos(5_000));
155        executor.wake_expired_timers();
156        let res = executor.run_until_stalled(&mut start_fut);
157        assert_matches!(res, Poll::Pending);
158
159        // Poll past 10us - future is ready.
160        executor.set_fake_time(fasync::MonotonicInstant::from_nanos(11_000));
161        executor.wake_expired_timers();
162        let res = executor.run_until_stalled(&mut start_fut);
163        assert_matches!(res, Poll::Ready(Ok(_)));
164    }
165
166    #[fuchsia::test]
167    fn test_alarm_cancelation() {
168        let mut executor = fasync::TestExecutor::new_with_fake_time();
169        executor.set_fake_time(fasync::MonotonicInstant::from_nanos(0));
170        let ops = EmulationTimerOps::new();
171        let setup_event = zx::Event::create();
172        let mut start_fut = ops.start_and_wait(
173            FAKE_ID,
174            // 10us total.
175            &ffhh::Resolution::Duration(1000),
176            10,
177            clone_handle(&setup_event),
178        );
179
180        // Try polling now - future isn't ready yet.
181        let res = executor.run_until_stalled(&mut start_fut);
182        assert_matches!(res, Poll::Pending);
183
184        executor.set_fake_time(fasync::MonotonicInstant::from_nanos(5_000));
185        executor.wake_expired_timers();
186        let res = executor.run_until_stalled(&mut start_fut);
187        assert_matches!(res, Poll::Pending);
188
189        let mut stop_fut = ops.stop(FAKE_ID);
190        let res = executor.run_until_stalled(&mut stop_fut);
191        assert_matches!(res, Poll::Ready(_));
192
193        // Attempt to poll now and note that the alarm is now canceled.
194        let res = executor.run_until_stalled(&mut start_fut);
195        assert_matches!(res, Poll::Ready(Err(TimerOpsError::Driver(ffhh::DriverError::Canceled))));
196    }
197
198    #[fuchsia::test]
199    fn test_alarm_start_twice_is_error() {
200        let mut executor = fasync::TestExecutor::new_with_fake_time();
201        executor.set_fake_time(fasync::MonotonicInstant::from_nanos(0));
202        let ops = EmulationTimerOps::new();
203        let setup_event = zx::Event::create();
204        let mut start_fut = ops.start_and_wait(
205            FAKE_ID,
206            // 10us total.
207            &ffhh::Resolution::Duration(1000), // 1us per tick
208            10,
209            clone_handle(&setup_event),
210        );
211
212        // Try polling now - future isn't ready yet.
213        let res = executor.run_until_stalled(&mut start_fut);
214        assert_matches!(res, Poll::Pending);
215
216        // Try to start another time while one is already active, is an error.
217        let mut start_fut_2 = ops.start_and_wait(
218            FAKE_ID,
219            &ffhh::Resolution::Duration(1000),
220            10,
221            clone_handle(&setup_event),
222        );
223        let res = executor.run_until_stalled(&mut start_fut_2);
224        assert_matches!(res, Poll::Ready(Err(TimerOpsError::Driver(ffhh::DriverError::BadState))));
225    }
226
227    #[fuchsia::test]
228    fn test_alarm_start_stop_start() {
229        let mut executor = fasync::TestExecutor::new_with_fake_time();
230        executor.set_fake_time(fasync::MonotonicInstant::from_nanos(0));
231        let ops = EmulationTimerOps::new();
232        let setup_event = zx::Event::create();
233        let mut start_fut = ops.start_and_wait(
234            FAKE_ID,
235            // 10us total.
236            &ffhh::Resolution::Duration(1000),
237            10,
238            clone_handle(&setup_event),
239        );
240
241        executor.set_fake_time(fasync::MonotonicInstant::from_nanos(5_000));
242        executor.wake_expired_timers();
243        let res = executor.run_until_stalled(&mut start_fut);
244        assert_matches!(res, Poll::Pending);
245
246        let mut stop_fut = ops.stop(FAKE_ID);
247        let res = executor.run_until_stalled(&mut stop_fut);
248        assert_matches!(res, Poll::Ready(_));
249
250        // Start a new alarm.
251        let mut start_fut = ops.start_and_wait(
252            FAKE_ID,
253            // 10us total.
254            &ffhh::Resolution::Duration(1000),
255            10,
256            clone_handle(&setup_event),
257        );
258
259        let res = executor.run_until_stalled(&mut start_fut);
260        assert_matches!(res, Poll::Pending);
261
262        executor.set_fake_time(fasync::MonotonicInstant::from_nanos(50_000));
263        executor.wake_expired_timers();
264        let res = executor.run_until_stalled(&mut start_fut);
265        assert_matches!(res, Poll::Ready(_));
266    }
267
268    #[fuchsia::test]
269    fn test_alarm_start_expire_start() {
270        let mut executor = fasync::TestExecutor::new_with_fake_time();
271        executor.set_fake_time(fasync::MonotonicInstant::from_nanos(0));
272        let ops = EmulationTimerOps::new();
273        let setup_event = zx::Event::create();
274        let mut start_fut = ops.start_and_wait(
275            FAKE_ID,
276            // 10us total.
277            &ffhh::Resolution::Duration(1000),
278            10,
279            clone_handle(&setup_event),
280        );
281        let res = executor.run_until_stalled(&mut start_fut);
282        assert_matches!(res, Poll::Pending);
283
284        executor.set_fake_time(fasync::MonotonicInstant::from_nanos(20_000));
285        executor.wake_expired_timers();
286        let res = executor.run_until_stalled(&mut start_fut);
287        assert_matches!(res, Poll::Ready(_));
288
289        // Start a new alarm.
290        let mut start_fut = ops.start_and_wait(
291            FAKE_ID,
292            // 10us total.
293            &ffhh::Resolution::Duration(1000),
294            10,
295            clone_handle(&setup_event),
296        );
297        let res = executor.run_until_stalled(&mut start_fut);
298        assert_matches!(res, Poll::Pending);
299
300        // Ensure the timer expires, and check that it has expired.
301        executor.set_fake_time(fasync::MonotonicInstant::from_nanos(50_000));
302        executor.wake_expired_timers();
303        let res = executor.run_until_stalled(&mut start_fut);
304        assert_matches!(res, Poll::Ready(_));
305    }
306}