alarms/
lib.rs

1// Copyright 2024 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
5//! Alarm management subsystem.
6//!
7//! This subsystem serves the FIDL API `fuchsia.time.alarms/Wake`. To instantiate,
8//! you can use the following approach:
9//!
10//! ```ignore
11//! let proxy = client::connect_to_protocol::<ffhh::DeviceMarker>().map_err(
12//!    |e| error!("error: {}", e)).expect("add proper error handling");
13//!    let timer_loop = alarms::Handle::new(proxy);
14//! ```
15//!
16//! From here, use the standard approach with [ServiceFs::new] to expose the
17//! discoverable FIDL endpoint and call:
18//!
19//! ```ignore
20//! let stream: fidl_fuchsia_time_alarms::WakeRequestStream = ... ;
21//! alarms::serve(timer_loop, stream).await;
22//! // ...
23//! ```
24//!
25//! Of course, for everything to work well, your component will need appropriate
26//! capability routing.  Refer to capability routing docs for those details.
27
28mod emu;
29mod timers;
30
31use crate::emu::EmulationTimerOps;
32use anyhow::{Context, Result, anyhow};
33use async_trait::async_trait;
34use fidl::HandleBased;
35use fidl::encoding::ProxyChannelBox;
36use fidl::endpoints::RequestStream;
37use fuchsia_component::client::connect_to_named_protocol_at_dir_root;
38use fuchsia_inspect::{NumericProperty, Property};
39use futures::channel::mpsc;
40use futures::sink::SinkExt;
41use futures::{StreamExt, TryStreamExt};
42use log::{debug, error, warn};
43use scopeguard::defer;
44use std::cell::RefCell;
45use std::rc::Rc;
46use std::sync::LazyLock;
47use time_pretty::{MSEC_IN_NANOS, format_duration, format_timer};
48use zx::AsHandleRef;
49use {
50    fidl_fuchsia_hardware_hrtimer as ffhh, fidl_fuchsia_time_alarms as fta,
51    fuchsia_async as fasync, fuchsia_inspect as finspect, fuchsia_runtime as fxr,
52    fuchsia_trace as trace,
53};
54
55static I64_MAX_AS_U64: LazyLock<u64> = LazyLock::new(|| i64::MAX.try_into().expect("infallible"));
56static I32_MAX_AS_U64: LazyLock<u64> = LazyLock::new(|| i32::MAX.try_into().expect("infallible"));
57
58/// The largest value of timer "ticks" that is still considered useful.
59static MAX_USEFUL_TICKS: LazyLock<u64> = LazyLock::new(|| *I32_MAX_AS_U64);
60
61/// The smallest value of "ticks" that we can program into the driver. To wit,
62/// driver will reject "0" ticks, even though it probably shouldn't. See
63/// for details: b/437177931.
64static MIN_USEFUL_TICKS: u64 = 1;
65
66/// The hrtimer ID used for scheduling wake alarms.  This ID is reused from
67/// Starnix, and should eventually no longer be critical.
68const MAIN_TIMER_ID: usize = 6;
69
70/// This is what we consider a "long" delay in alarm operations.
71const LONG_DELAY_NANOS: i64 = 2000 * MSEC_IN_NANOS;
72
73/// A macro that waits on a future, but if the future takes longer than
74/// 30 seconds to complete, logs a warning message.
75macro_rules! log_long_op {
76    ($fut:expr) => {{
77        use futures::FutureExt;
78        let fut = $fut;
79        futures::pin_mut!(fut);
80        loop {
81            let timeout = fasync::Timer::new(std::time::Duration::from_secs(30));
82            futures::select! {
83                res = fut.as_mut().fuse() => {
84                    break res;
85                }
86                _ = timeout.fuse() => {
87                    warn!("unexpected blocking: long-running async operation at {}:{}", file!(), line!());
88                    #[cfg(all(target_os = "fuchsia", not(doc)))]
89                    ::debug::backtrace_request_all_threads();
90                }
91            }
92        }
93    }};
94}
95
96/// Compares two optional deadlines and returns true if the `before is different from `after.
97/// Nones compare as equal.
98fn is_deadline_changed(
99    before: Option<fasync::BootInstant>,
100    after: Option<fasync::BootInstant>,
101) -> bool {
102    match (before, after) {
103        (None, None) => false,
104        (None, Some(_)) | (Some(_), None) => true,
105        (Some(before), Some(after)) => before != after,
106    }
107}
108
109// Errors returnable from [TimerOps] calls.
110#[derive(Debug)]
111pub(crate) enum TimerOpsError {
112    /// The driver reported an error.
113    Driver(ffhh::DriverError),
114    /// FIDL-specific RPC error.
115    Fidl(fidl::Error),
116}
117
118trait SawResponseFut: std::future::Future<Output = Result<zx::EventPair, TimerOpsError>> {
119    // nop
120}
121
122/// Abstracts away timer operations.
123#[async_trait(?Send)]
124pub(crate) trait TimerOps {
125    /// Stop the timer with the specified ID.
126    async fn stop(&self, id: u64);
127
128    /// Examine the timer's properties, such as supported resolutions and tick
129    /// counts.
130    async fn get_timer_properties(&self) -> TimerConfig;
131
132    /// This method must return an actual future, to handle the borrow checker:
133    /// making this async will assume that `self` remains borrowed, which will
134    /// thwart attempts to move the return value of this call into a separate
135    /// closure.
136    fn start_and_wait(
137        &self,
138        id: u64,
139        resolution: &ffhh::Resolution,
140        ticks: u64,
141        setup_event: zx::Event,
142    ) -> std::pin::Pin<Box<dyn SawResponseFut>>;
143}
144
145/// TimerOps backed by an actual hardware timer.
146struct HardwareTimerOps {
147    proxy: ffhh::DeviceProxy,
148}
149
150impl HardwareTimerOps {
151    fn new(proxy: ffhh::DeviceProxy) -> Box<Self> {
152        Box::new(Self { proxy })
153    }
154}
155
156#[async_trait(?Send)]
157impl TimerOps for HardwareTimerOps {
158    async fn stop(&self, id: u64) {
159        let _ = self
160            .proxy
161            .stop(id)
162            .await
163            .map(|result| {
164                let _ = result.map_err(|e| warn!("stop_hrtimer: driver error: {:?}", e));
165            })
166            .map_err(|e| warn!("stop_hrtimer: could not stop prior timer: {}", e));
167    }
168
169    async fn get_timer_properties(&self) -> TimerConfig {
170        match log_long_op!(self.proxy.get_properties()) {
171            Ok(p) => {
172                let timers_properties = &p.timers_properties.expect("timers_properties must exist");
173                debug!("get_timer_properties: got: {:?}", timers_properties);
174
175                // Pick the correct hrtimer to use for wakes.
176                let timer_index = if timers_properties.len() > MAIN_TIMER_ID {
177                    // Mostly vim3, where we have pre-existing timer allocations
178                    // that we don't need to change.
179                    MAIN_TIMER_ID
180                } else if timers_properties.len() > 0 {
181                    // Newer devices that don't need to allocate timer IDs, and/or
182                    // may not even have as many timers as vim3 does. But, at least
183                    // one timer is needed.
184                    0
185                } else {
186                    // Give up.
187                    return TimerConfig::new_empty();
188                };
189                let main_timer_properties = &timers_properties[timer_index];
190                debug!("alarms: main_timer_properties: {:?}", main_timer_properties);
191                // Not sure whether it is useful to have more ticks than this, so limit it.
192                let max_ticks: u64 = std::cmp::min(
193                    main_timer_properties.max_ticks.unwrap_or(*MAX_USEFUL_TICKS),
194                    *MAX_USEFUL_TICKS,
195                );
196                let resolutions = &main_timer_properties
197                    .supported_resolutions
198                    .as_ref()
199                    .expect("supported_resolutions is populated")
200                    .iter()
201                    .last() //  Limits the resolution to the coarsest available.
202                    .map(|r| match *r {
203                        ffhh::Resolution::Duration(d) => d,
204                        _ => {
205                            error!(
206                            "get_timer_properties: Unknown resolution type, returning millisecond."
207                        );
208                            MSEC_IN_NANOS
209                        }
210                    })
211                    .map(|d| zx::BootDuration::from_nanos(d))
212                    .into_iter() // Used with .last() above.
213                    .collect::<Vec<_>>();
214                let timer_id = main_timer_properties.id.expect("timer ID is always present");
215                TimerConfig::new_from_data(timer_id, resolutions, max_ticks)
216            }
217            Err(e) => {
218                error!("could not get timer properties: {:?}", e);
219                TimerConfig::new_empty()
220            }
221        }
222    }
223
224    fn start_and_wait(
225        &self,
226        id: u64,
227        resolution: &ffhh::Resolution,
228        ticks: u64,
229        setup_event: zx::Event,
230    ) -> std::pin::Pin<Box<dyn SawResponseFut>> {
231        let inner = self.proxy.start_and_wait(id, resolution, ticks, setup_event);
232        Box::pin(HwResponseFut { pinner: Box::pin(inner) })
233    }
234}
235
236// Untangles the borrow checker issues that otherwise result from making
237// TimerOps::start_and_wait an async function.
238struct HwResponseFut {
239    pinner: std::pin::Pin<
240        Box<
241            fidl::client::QueryResponseFut<
242                ffhh::DeviceStartAndWaitResult,
243                fidl::encoding::DefaultFuchsiaResourceDialect,
244            >,
245        >,
246    >,
247}
248
249use std::task::Poll;
250impl SawResponseFut for HwResponseFut {}
251impl std::future::Future for HwResponseFut {
252    type Output = Result<zx::EventPair, TimerOpsError>;
253    fn poll(
254        mut self: std::pin::Pin<&mut Self>,
255        cx: &mut std::task::Context<'_>,
256    ) -> std::task::Poll<Self::Output> {
257        let inner_poll = self.pinner.as_mut().poll(cx);
258        match inner_poll {
259            Poll::Ready(result) => Poll::Ready(match result {
260                Ok(Ok(keep_alive)) => Ok(keep_alive),
261                Ok(Err(e)) => Err(TimerOpsError::Driver(e)),
262                Err(e) => Err(TimerOpsError::Fidl(e)),
263            }),
264            Poll::Pending => Poll::Pending,
265        }
266    }
267}
268
269/// Stops a currently running hardware timer.
270async fn stop_hrtimer(hrtimer: &Box<dyn TimerOps>, timer_config: &TimerConfig) {
271    trace::duration!(c"alarms", c"hrtimer:stop", "id" => timer_config.id);
272    debug!("stop_hrtimer: stopping hardware timer: {}", timer_config.id);
273    hrtimer.stop(timer_config.id).await;
274    debug!("stop_hrtimer: stopped  hardware timer: {}", timer_config.id);
275}
276
277// The default size of the channels created in this module.
278const CHANNEL_SIZE: usize = 100;
279
280/// A type handed around between the concurrent loops run by this module.
281#[derive(Debug)]
282enum Cmd {
283    /// Request a timer to be started.
284    Start {
285        /// The unique connection ID.
286        conn_id: zx::Koid,
287        /// A timestamp (presumably in the future), at which to expire the timer.
288        deadline: timers::Deadline,
289        // The API supports several modes. See fuchsia.time.alarms/Wake.fidl.
290        //
291        // Optional, because not always needed:
292        //
293        // * `mode` is required for hanging get API calls (e.g. `StartAndWait`), as we must signal
294        //   when the alarm is scheduled.
295        // * The calls such as `SetUtc` which return only upon scheduling do not need a `mode`, as
296        //   the caller can wait for the call to return immediately.
297        mode: Option<fta::SetMode>,
298        /// An alarm identifier, chosen by the caller.
299        alarm_id: String,
300        /// A responder that will be called when the timer expires. The
301        /// client end of the connection will block until we send something
302        /// on this responder.
303        ///
304        /// This is packaged into a Rc... only because both the "happy path"
305        /// and the error path must consume the responder.  This allows them
306        /// to be consumed, without the responder needing to implement Default.
307        responder: Rc<dyn timers::Responder>,
308    },
309    StopById {
310        done: zx::Event,
311        timer_id: timers::Id,
312    },
313    Alarm {
314        expired_deadline: fasync::BootInstant,
315        keep_alive: fidl::EventPair,
316    },
317    AlarmFidlError {
318        expired_deadline: fasync::BootInstant,
319        error: fidl::Error,
320    },
321    AlarmDriverError {
322        expired_deadline: fasync::BootInstant,
323        error: ffhh::DriverError,
324
325        // Added these for debugging details, otherwise not necessary.
326        timer_config_id: u64,
327        resolution_nanos: i64,
328        ticks: u64,
329    },
330    /// The UTC clock transformation has been updated.
331    UtcUpdated {
332        // The new boot-to-utc clock transformation.
333        transform: fxr::UtcClockTransform,
334    },
335}
336
337impl std::fmt::Display for Cmd {
338    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
339        match self {
340            Cmd::Start { conn_id, deadline, alarm_id, .. } => {
341                write!(
342                    f,
343                    "Start[alarm_id=\"{}\", conn_id={:?}, deadline={}]",
344                    alarm_id, conn_id, deadline,
345                )
346            }
347            Cmd::Alarm { expired_deadline, .. } => {
348                write!(f, "Alarm[deadline={}]", format_timer((*expired_deadline).into()))
349            }
350            Cmd::AlarmFidlError { expired_deadline, error } => {
351                write!(
352                    f,
353                    "FIDLError[deadline={}, err={}, NO_WAKE_LEASE!]",
354                    format_timer((*expired_deadline).into()),
355                    error
356                )
357            }
358            Cmd::AlarmDriverError { expired_deadline, error, .. } => {
359                write!(
360                    f,
361                    "DriverError[deadline={}, err={:?}, NO_WAKE_LEASE!]",
362                    format_timer((*expired_deadline).into()),
363                    error
364                )
365            }
366            Cmd::StopById { timer_id, done: _ } => {
367                write!(f, "StopById[timerId={}]", timer_id,)
368            }
369            Cmd::UtcUpdated { transform } => {
370                write!(f, "UtcUpdated[timerId={transform:?}]")
371            }
372        }
373    }
374}
375
376/// Extracts a KOID from the underlying channel of the provided stream.
377///
378/// This function deconstructs the provided stream to access the underlying
379/// channel and extract its KOID. It then reconstructs the stream and returns
380/// it to the caller along with the KOID.
381///
382/// # Args
383/// - `stream`: The `fta::WakeAlarmsRequestStream` to extract the KOID from.
384///
385/// # Returns
386/// A tuple containing the `zx::Koid` of the stream's channel and the
387/// reconstructed `fta::WakeAlarmsRequestStream`.
388pub fn get_stream_koid(
389    stream: fta::WakeAlarmsRequestStream,
390) -> (zx::Koid, fta::WakeAlarmsRequestStream) {
391    let (inner, is_terminated) = stream.into_inner();
392    let koid = inner.channel().as_channel().get_koid().expect("infallible");
393    let stream = fta::WakeAlarmsRequestStream::from_inner(inner, is_terminated);
394    (koid, stream)
395}
396
397/// Serves a single Wake API client.
398///
399/// This function processes incoming requests from a `fta::WakeAlarmsRequestStream`,
400/// handling each request by calling `handle_request`. It continues to process
401/// requests until the stream is exhausted.
402///
403/// # Args
404/// - `timer_loop`: A reference-counted pointer to the `Loop` that manages timers.
405/// - `requests`: The stream of incoming `fta::WakeAlarmsRequest` from a client.
406pub async fn serve(timer_loop: Rc<Loop>, requests: fta::WakeAlarmsRequestStream) {
407    let timer_loop = timer_loop.clone();
408    let timer_loop_send = || timer_loop.get_sender();
409    let (conn_id, mut requests) = get_stream_koid(requests);
410    let mut request_count = 0;
411    debug!("alarms::serve: opened connection: {:?}", conn_id);
412    while let Some(maybe_request) = requests.next().await {
413        request_count += 1;
414        debug!("alarms::serve: conn_id: {:?} incoming request: {}", conn_id, request_count);
415        match maybe_request {
416            Ok(request) => {
417                // Should return quickly.
418                handle_request(conn_id, timer_loop_send(), request).await;
419            }
420            Err(e) => {
421                warn!("alarms::serve: error in request: {:?}", e);
422            }
423        }
424        debug!("alarms::serve: conn_id: {:?} done request: {}", conn_id, request_count);
425    }
426    // Check if connection closure was intentional. It is way too easy to close
427    // a FIDL connection inadvertently if doing non-mainstream things with FIDL.
428    warn!("alarms::serve: CLOSED CONNECTION: conn_id: {:?}", conn_id);
429}
430
431async fn handle_cancel(alarm_id: String, conn_id: zx::Koid, cmd: &mut mpsc::Sender<Cmd>) {
432    let done = zx::Event::create();
433    let timer_id = timers::Id::new(alarm_id.clone(), conn_id);
434    if let Err(e) = cmd.send(Cmd::StopById { timer_id, done: clone_handle(&done) }).await {
435        warn!("handle_request: error while trying to cancel: {}: {:?}", alarm_id, e);
436    }
437    wait_signaled(&done).await;
438}
439
440/// Processes a single Wake API request from a single client.
441/// This function is expected to return quickly.
442///
443/// # Args
444/// - `conn_id`: the unique identifier of the connection producing these requests.
445/// - `cmd`: the outbound queue of commands to deliver to the timer manager.
446/// - `request`: a single inbound Wake FIDL API request.
447async fn handle_request(
448    conn_id: zx::Koid,
449    mut cmd: mpsc::Sender<Cmd>,
450    request: fta::WakeAlarmsRequest,
451) {
452    match request {
453        fta::WakeAlarmsRequest::SetAndWait { deadline, mode, alarm_id, responder } => {
454            // Since responder is consumed by the happy path and the error path, but not both,
455            // and because the responder does not implement Default, this is a way to
456            // send it in two mutually exclusive directions.  Each direction will reverse
457            // this wrapping once the responder makes it to the other side.
458            //
459            // Rc required because of sharing a noncopyable struct; RefCell required because
460            // borrow_mut() is needed to move out; and Option is required so we can
461            // use take() to replace the struct with None so it does not need to leave
462            // a Default in its place.
463            let responder = Rc::new(RefCell::new(Some(responder)));
464
465            // Alarm is not scheduled yet!
466            debug!(
467                "handle_request: scheduling alarm_id: \"{}\"\n\tconn_id: {:?}\n\tdeadline: {}",
468                alarm_id,
469                conn_id,
470                format_timer(deadline.into())
471            );
472            // Expected to return quickly.
473            let deadline = timers::Deadline::Boot(deadline.into());
474            if let Err(e) = log_long_op!(cmd.send(Cmd::Start {
475                conn_id,
476                deadline,
477                mode: Some(mode),
478                alarm_id: alarm_id.clone(),
479                responder: responder.clone(),
480            })) {
481                warn!("handle_request: error while trying to schedule `{}`: {:?}", alarm_id, e);
482                responder
483                    .borrow_mut()
484                    .take()
485                    .expect("always present if call fails")
486                    .send(Err(fta::WakeAlarmsError::Internal))
487                    .unwrap();
488            }
489        }
490        fta::WakeAlarmsRequest::SetAndWaitUtc { deadline, mode, alarm_id, responder } => {
491            // Quickly get rid of the custom wake alarms deadline type.
492            let deadline =
493                timers::Deadline::Utc(fxr::UtcInstant::from_nanos(deadline.timestamp_utc));
494
495            // The rest of this match branch is the same as for `SetAndWait`. However, the handling
496            // is for now simple enough that we don't need to explore factoring common actions out.
497            let responder = Rc::new(RefCell::new(Some(responder)));
498            debug!(
499                "handle_request: scheduling alarm_id UTC: \"{alarm_id}\"\n\tconn_id: {conn_id:?}\n\tdeadline: {deadline}",
500            );
501
502            if let Err(e) = log_long_op!(cmd.send(Cmd::Start {
503                conn_id,
504                deadline,
505                mode: Some(mode),
506                alarm_id: alarm_id.clone(),
507                responder: responder.clone(),
508            })) {
509                warn!("handle_request: error while trying to schedule `{}`: {:?}", alarm_id, e);
510                responder
511                    .borrow_mut()
512                    .take()
513                    .expect("always present if call fails")
514                    .send(Err(fta::WakeAlarmsError::Internal))
515                    .unwrap();
516            }
517        }
518        fta::WakeAlarmsRequest::Cancel { alarm_id, .. } => {
519            // TODO: b/383062441 - make this into an async task so that we wait
520            // less to schedule the next alarm.
521            log_long_op!(handle_cancel(alarm_id, conn_id, &mut cmd));
522        }
523        fta::WakeAlarmsRequest::Set { notifier, deadline, mode, alarm_id, responder } => {
524            // Alarm is not scheduled yet!
525            debug!(
526                "handle_request: scheduling alarm_id: \"{alarm_id}\"\n\tconn_id: {conn_id:?}\n\tdeadline: {}",
527                format_timer(deadline.into())
528            );
529            // Expected to return quickly.
530            if let Err(e) = log_long_op!(cmd.send(Cmd::Start {
531                conn_id,
532                deadline: timers::Deadline::Boot(deadline.into()),
533                mode: Some(mode),
534                alarm_id: alarm_id.clone(),
535                responder: Rc::new(RefCell::new(Some(notifier))),
536            })) {
537                warn!("handle_request: error while trying to schedule `{}`: {:?}", alarm_id, e);
538                responder.send(Err(fta::WakeAlarmsError::Internal)).unwrap();
539            } else {
540                // Successfully scheduled the alarm.
541                responder.send(Ok(())).unwrap();
542            }
543        }
544        fta::WakeAlarmsRequest::_UnknownMethod { .. } => {}
545    };
546}
547
548/// Represents a single alarm event processing loop.
549///
550/// One instance is created per each alarm-capable low-level device. The `Loop`
551/// is responsible for managing the lifecycle of wake alarms, including their
552/// creation, scheduling, and cancellation. It interacts with the underlying
553/// hardware timer through a `TimerOps` trait object.
554pub struct Loop {
555    // Given to any clients that need to send messages to `_task`
556    // via [get_sender].
557    snd: mpsc::Sender<Cmd>,
558}
559
560impl Loop {
561    /// Creates a new instance of `Loop`.
562    ///
563    /// This function initializes a new `Loop` with a connection to a low-level
564    /// hardware timer device. It spawns two background tasks: one for the main
565    /// timer event loop and another for monitoring UTC clock changes.
566    ///
567    /// # Args
568    /// - `scope`: The `fasync::ScopeHandle` to spawn background tasks in.
569    /// - `device_proxy`: A `ffhh::DeviceProxy` for communicating with the hardware timer.
570    /// - `inspect`: A `finspect::Node` for recording diagnostics.
571    /// - `utc_clock`: A `fxr::UtcClock` for tracking UTC time.
572    ///
573    /// # Returns
574    /// A new instance of `Loop`.
575    pub fn new(
576        scope: fasync::ScopeHandle,
577        device_proxy: ffhh::DeviceProxy,
578        inspect: finspect::Node,
579        utc_clock: fxr::UtcClock,
580    ) -> Self {
581        let hw_device_timer_ops = HardwareTimerOps::new(device_proxy);
582        Loop::new_internal(scope, hw_device_timer_ops, inspect, utc_clock)
583    }
584
585    /// Creates a new instance of `Loop` with emulated wake alarms.
586    ///
587    /// This function is similar to `new`, but it uses an emulated timer instead
588    /// of a real hardware timer. This is useful for testing environments where
589    /// a hardware timer may not be available.
590    ///
591    /// # Args
592    /// - `scope`: The `fasync::ScopeHandle` to spawn background tasks in.
593    /// - `inspect`: A `finspect::Node` for recording diagnostics.
594    /// - `utc_clock`: A `fxr::UtcClock` for tracking UTC time.
595    ///
596    /// # Returns
597    /// A new instance of `Loop` with an emulated timer.
598    pub fn new_emulated(
599        scope: fasync::ScopeHandle,
600        inspect: finspect::Node,
601        utc_clock: fxr::UtcClock,
602    ) -> Self {
603        let timer_ops = Box::new(EmulationTimerOps::new());
604        Loop::new_internal(scope, timer_ops, inspect, utc_clock)
605    }
606
607    fn new_internal(
608        scope: fasync::ScopeHandle,
609        timer_ops: Box<dyn TimerOps>,
610        inspect: finspect::Node,
611        utc_clock: fxr::UtcClock,
612    ) -> Self {
613        let utc_transform = Rc::new(RefCell::new(
614            utc_clock.get_details().expect("has UTC clock READ capability").reference_to_synthetic,
615        ));
616
617        let (snd, rcv) = mpsc::channel(CHANNEL_SIZE);
618        let loop_scope = scope.clone();
619
620        scope.spawn_local(wake_timer_loop(
621            loop_scope,
622            snd.clone(),
623            rcv,
624            timer_ops,
625            inspect,
626            utc_transform,
627        ));
628        scope.spawn_local(monitor_utc_clock_changes(utc_clock, snd.clone()));
629        Self { snd }
630    }
631
632    /// Gets a copy of a channel through which async commands may be sent to
633    /// the [Loop].
634    fn get_sender(&self) -> mpsc::Sender<Cmd> {
635        self.snd.clone()
636    }
637}
638
639// Forwards the clock transformation of an updated clock into the alarm manager, to allow
640// correcting the boot time deadlines of clocks on the UTC timeline.
641async fn monitor_utc_clock_changes(utc_clock: fxr::UtcClock, mut cmd: mpsc::Sender<Cmd>) {
642    let koid = utc_clock.as_handle_ref().get_koid();
643    log::info!("monitor_utc_clock_changes: entry");
644    loop {
645        // CLOCK_UPDATED signal is self-clearing.
646        fasync::OnSignals::new(utc_clock.as_handle_ref(), zx::Signals::CLOCK_UPDATED)
647            .await
648            .expect("UTC clock is readable");
649
650        let transform =
651            utc_clock.get_details().expect("UTC clock details are readable").reference_to_synthetic;
652        log::debug!("Received a UTC update: koid={koid:?}: {transform:?}");
653        if let Err(err) = cmd.send(Cmd::UtcUpdated { transform }).await {
654            // This is OK in tests.
655            log::warn!("monitor_utc_clock_changes: exit: {err:?}");
656            break;
657        }
658    }
659}
660
661/// Clones a handle infallibly with `zx::Rights::SAME_RIGHTS`.
662///
663/// This function duplicates a handle, preserving its rights. It will panic if
664/// the handle duplication fails, which is not expected to happen under normal
665/// circumstances.
666///
667/// # Args
668/// - `handle`: A reference to a handle-based object to be cloned.
669///
670/// # Returns
671/// A new handle with the same rights as the original.
672pub fn clone_handle<H: HandleBased>(handle: &H) -> H {
673    handle.duplicate_handle(zx::Rights::SAME_RIGHTS).expect("infallible")
674}
675
676async fn wait_signaled<H: HandleBased>(handle: &H) {
677    fasync::OnSignals::new(handle, zx::Signals::EVENT_SIGNALED).await.expect("infallible");
678}
679
680pub(crate) fn signal<H: HandleBased>(handle: &H) {
681    handle.signal_handle(zx::Signals::NONE, zx::Signals::EVENT_SIGNALED).expect("infallible");
682}
683
684/// A [TimerDuration] represents a duration of time that can be expressed by
685/// a discrete timer register.
686///
687/// This is a low-level representation of time duration, used in interaction with
688/// hardware devices. It is therefore necessarily discretized, with adaptive
689/// resolution, depending on the physical characteristics of the underlying
690/// hardware timer that it models.
691#[derive(Debug, Clone, Copy)]
692struct TimerDuration {
693    // The resolution of each one of the `ticks` below.
694    resolution: zx::BootDuration,
695    // The number of ticks that encodes time duration. Each "tick" represents
696    // one unit of `resolution` above.
697    ticks: u64,
698}
699
700/// This and the comparison traits below are used to allow TimerDuration
701/// calculations in a compact form.
702impl Eq for TimerDuration {}
703
704impl std::cmp::PartialOrd for TimerDuration {
705    fn partial_cmp(&self, other: &TimerDuration) -> Option<std::cmp::Ordering> {
706        Some(self.cmp(other))
707    }
708}
709
710impl std::cmp::PartialEq for TimerDuration {
711    fn eq(&self, other: &Self) -> bool {
712        self.cmp(other) == std::cmp::Ordering::Equal
713    }
714}
715
716impl std::cmp::Ord for TimerDuration {
717    /// Two [TimerDuration]s compare equal if they model exactly the same duration of time,
718    /// no matter the resolutions.
719    fn cmp(&self, other: &TimerDuration) -> std::cmp::Ordering {
720        let self_ticks_128: i128 = self.ticks as i128;
721        let self_resolution: i128 = self.resolution_as_nanos() as i128;
722        let self_nanos = self_resolution * self_ticks_128;
723
724        let other_ticks_128: i128 = other.ticks as i128;
725        let other_resolution: i128 = other.resolution_as_nanos() as i128;
726        let other_nanos = other_resolution * other_ticks_128;
727
728        self_nanos.cmp(&other_nanos)
729    }
730}
731
732impl std::fmt::Display for TimerDuration {
733    /// Human readable TimerDuration exposes both the tick count and the resolution,
734    /// in the format of "ticks x resolution", with an end result of
735    /// `10x5ms` for example.
736    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
737        let ticks = self.ticks;
738        let resolution = self.resolution();
739        // Example: 10x1ms
740        write!(f, "{}x{}", ticks, format_duration(resolution),)
741    }
742}
743
744impl TimerDuration {
745    /// The maximum representable TimerDuration that we allow.
746    fn max() -> Self {
747        TimerDuration::new(zx::BootDuration::from_nanos(1), *I64_MAX_AS_U64)
748    }
749
750    /// The zero [TimerDuration].
751    fn zero() -> Self {
752        TimerDuration::new(zx::BootDuration::from_nanos(1), 0)
753    }
754
755    /// Creates a new timer duration with the given parameters.
756    fn new(resolution: zx::BootDuration, ticks: u64) -> Self {
757        Self { resolution, ticks }
758    }
759
760    /// Creates a new timer duration using the resolution from `res_source` and
761    /// a specified number of ticks.
762    fn new_with_resolution(res_source: &TimerDuration, ticks: u64) -> Self {
763        Self::new(res_source.resolution, ticks)
764    }
765
766    /// Returns the time duration represented by this TimerDuration.
767    ///
768    /// Due to the way duration is expressed, the same time duration
769    /// can be represented in multiple ways.
770    fn duration(&self) -> zx::BootDuration {
771        let duration_as_nanos = self.resolution_as_nanos() * self.ticks;
772        let clamp_duration = std::cmp::min(*I32_MAX_AS_U64, duration_as_nanos);
773        zx::BootDuration::from_nanos(clamp_duration.try_into().expect("result was clamped"))
774    }
775
776    /// The resolution of this TimerDuration
777    fn resolution(&self) -> zx::BootDuration {
778        self.resolution
779    }
780
781    fn resolution_as_nanos(&self) -> u64 {
782        self.resolution().into_nanos().try_into().expect("resolution is never negative")
783    }
784
785    /// The number of ticks of this [TimerDuration].
786    fn ticks(&self) -> u64 {
787        self.ticks
788    }
789}
790
791impl From<zx::BootDuration> for TimerDuration {
792    fn from(d: zx::BootDuration) -> TimerDuration {
793        let nanos = d.into_nanos();
794        assert!(nanos >= 0);
795        let nanos_u64 = nanos.try_into().expect("guarded by assert");
796        TimerDuration::new(zx::BootDuration::from_nanos(1), nanos_u64)
797    }
798}
799
800impl std::ops::Div for TimerDuration {
801    type Output = u64;
802    fn div(self, rhs: Self) -> Self::Output {
803        let self_nanos = self.resolution_as_nanos() * self.ticks;
804        let rhs_nanos = rhs.resolution_as_nanos() * rhs.ticks;
805        self_nanos / rhs_nanos
806    }
807}
808
809impl std::ops::Mul<u64> for TimerDuration {
810    type Output = Self;
811    fn mul(self, rhs: u64) -> Self::Output {
812        Self::new(self.resolution, self.ticks * rhs)
813    }
814}
815
816/// Contains the configuration of a specific timer.
817#[derive(Debug)]
818pub(crate) struct TimerConfig {
819    /// The resolutions supported by this timer. Each entry is one possible
820    /// duration for on timer "tick".  The resolution is picked when a timer
821    /// request is sent.
822    ///
823    /// The resolutions MUST be sorted from finest (index 0) to coarsest.
824    ///
825    /// There MUST be at least one resolution.
826    resolutions: Vec<zx::BootDuration>,
827    /// The maximum count of "ticks" that the timer supports. The timer usually
828    /// has a register that counts up or down based on a clock signal with
829    /// the period specified by `resolutions`.  This is the maximum value that
830    /// the counter can count to without overflowing.
831    max_ticks: u64,
832    /// The stable ID of the timer with the above configuration.
833    id: u64,
834}
835
836impl TimerConfig {
837    /// Creates a new timer config with supported timer resolutions and the max
838    /// ticks value for the timer's counter.
839    fn new_from_data(timer_id: u64, resolutions: &[zx::BootDuration], max_ticks: u64) -> Self {
840        debug!(
841            "TimerConfig: resolutions: {:?}, max_ticks: {}, timer_id: {}",
842            resolutions.iter().map(|r| format_duration(*r)).collect::<Vec<_>>(),
843            max_ticks,
844            timer_id
845        );
846        let resolutions = resolutions.iter().map(|d| *d).collect::<Vec<zx::BootDuration>>();
847        TimerConfig { resolutions, max_ticks, id: timer_id }
848    }
849
850    fn new_empty() -> Self {
851        error!("TimerConfig::new_empty() called, this is not OK.");
852        TimerConfig { resolutions: vec![], max_ticks: 0, id: 0 }
853    }
854
855    // Picks the most appropriate timer setting for it to fire as close as possible
856    // when `duration` expires.
857    //
858    // If duration is too far in the future for what the timer supports,
859    // return a smaller value, to allow the timer to be reprogrammed multiple
860    // times.
861    //
862    // If the available menu of resolutions is such that we can wake only after
863    // the intended deadline, begrudgingly return that option.
864    fn pick_setting(&self, duration: zx::BootDuration) -> TimerDuration {
865        assert!(self.resolutions.len() > 0, "there must be at least one supported resolution");
866
867        // Driver does not support zero ticks, so we must accept the finest resolution duration
868        // instead.
869        if duration <= zx::BootDuration::ZERO {
870            return TimerDuration::new(self.resolutions[0], 1);
871        }
872
873        //  0         |-------------->|<---------------|
874        //  |---------+---------------+----------------+---->
875        //  |---------^               |                |
876        //  | best positive slack     |                |
877        //  |-------------------------^ duration       |
878        //  |------------------------------------------^ best negative slack.
879        let mut best_positive_slack = TimerDuration::zero();
880        let mut best_negative_slack = TimerDuration::max();
881
882        if self.max_ticks == 0 {
883            return TimerDuration::new(zx::BootDuration::from_millis(1), 0);
884        }
885        let duration_slack: TimerDuration = duration.into();
886
887        for res1 in self.resolutions.iter() {
888            let smallest_unit = TimerDuration::new(*res1, 1);
889            let max_tick_at_res = TimerDuration::new(*res1, self.max_ticks);
890
891            let smallest_slack_larger_than_duration = smallest_unit > duration_slack;
892            let largest_slack_smaller_than_duration = max_tick_at_res < duration_slack;
893
894            if smallest_slack_larger_than_duration {
895                if duration_slack == TimerDuration::zero() {
896                    best_negative_slack = TimerDuration::zero();
897                } else if smallest_unit < best_negative_slack {
898                    best_negative_slack = smallest_unit;
899                }
900            }
901            if largest_slack_smaller_than_duration {
902                if max_tick_at_res > best_positive_slack
903                    || best_positive_slack == TimerDuration::zero()
904                {
905                    best_positive_slack = max_tick_at_res;
906                }
907            }
908
909            // "Regular" case.
910            if !smallest_slack_larger_than_duration && !largest_slack_smaller_than_duration {
911                // Check whether duration divides evenly into the available slack options
912                // for this resolution.  If it does, then that is the slack we're looking for.
913                let q = duration_slack / smallest_unit;
914                let d = smallest_unit * q;
915                if d == duration_slack {
916                    // Exact match, we can return right now.
917                    return d;
918                } else {
919                    // Not an exact match, so q ticks is before, but q+1 is after.
920                    if d > best_positive_slack {
921                        best_positive_slack = TimerDuration::new_with_resolution(&smallest_unit, q);
922                    }
923                    let d_plus = TimerDuration::new_with_resolution(&smallest_unit, q + 1);
924                    if d_plus < best_negative_slack {
925                        best_negative_slack = d_plus;
926                    }
927                }
928            }
929        }
930
931        let p_slack = duration - best_positive_slack.duration();
932        let n_slack = best_negative_slack.duration() - duration;
933
934        // If the closest approximation is 0ns, then we can not advance time, so we reject it.
935        // Otherwise pick the smallest slack.  Note that when we pick the best positive slack,
936        // we will wake *before* the actual deadline.  In multi-resolution counters, this enables
937        // us to pick a finer count in the next go.
938        let ret = if p_slack < n_slack && best_positive_slack.duration().into_nanos() > 0 {
939            best_positive_slack
940        } else {
941            best_negative_slack
942        };
943        debug!("TimerConfig: picked slack: {} for duration: {}", ret, format_duration(duration));
944        assert!(
945            ret.duration().into_nanos() >= 0,
946            "ret: {}, p_slack: {}, n_slack: {}, orig.duration: {}\n\tbest_p_slack: {}\n\tbest_n_slack: {}\n\ttarget: {}\n\t 1: {} 2: {:?}, 3: {:?}",
947            ret,
948            format_duration(p_slack),
949            format_duration(n_slack),
950            format_duration(duration),
951            best_positive_slack,
952            best_negative_slack,
953            duration_slack,
954            p_slack != zx::BootDuration::ZERO,
955            p_slack,
956            zx::BootDuration::ZERO,
957        );
958        ret
959    }
960}
961
962async fn get_timer_properties(hrtimer: &Box<dyn TimerOps>) -> TimerConfig {
963    debug!("get_timer_properties: requesting timer properties.");
964    hrtimer.get_timer_properties().await
965}
966
967/// The state of a single hardware timer that we must bookkeep.
968struct TimerState {
969    // The task waiting for the proximate timer to expire.
970    task: fasync::Task<()>,
971    // The deadline that the above task is waiting for.
972    deadline: fasync::BootInstant,
973}
974
975/// The command loop for timer interaction.  All changes to the wake alarm device programming
976/// come in form of commands through `cmd`.
977///
978/// Args:
979/// - `snd`: the send end of `cmd` below, a clone is given to each spawned sub-task.
980/// - `cmds``: the input queue of alarm related commands.
981/// - `timer_proxy`: the FIDL API proxy for interacting with the hardware device.
982/// - `inspect`: the inspect node to record loop info into.
983async fn wake_timer_loop(
984    scope: fasync::ScopeHandle,
985    snd: mpsc::Sender<Cmd>,
986    mut cmds: mpsc::Receiver<Cmd>,
987    timer_proxy: Box<dyn TimerOps>,
988    inspect: finspect::Node,
989    utc_transform: Rc<RefCell<fxr::UtcClockTransform>>,
990) {
991    debug!("wake_timer_loop: started");
992
993    let mut timers = timers::Heap::new(utc_transform.clone());
994    let timer_config = get_timer_properties(&timer_proxy).await;
995
996    // Keeps the currently executing HrTimer closure.  This is not read from, but
997    // keeps the timer task active.
998    #[allow(clippy::collection_is_never_read)]
999    let mut hrtimer_status: Option<TimerState> = None;
1000
1001    // Initialize inspect properties. This must be done only once.
1002    //
1003    // Take note that these properties are updated when the `cmds` loop runs.
1004    // This means that repeated reads while no `cmds` activity occurs will return
1005    // old readings.  This is to ensure a consistent ability to replay the last
1006    // loop run if needed.
1007    let now_prop = inspect.create_int("now_ns", 0);
1008    let now_formatted_prop = inspect.create_string("now_formatted", "");
1009    let pending_timers_count_prop = inspect.create_uint("pending_timers_count", 0);
1010    let pending_timers_prop = inspect.create_string("pending_timers", "");
1011    let _deadline_histogram_prop = inspect.create_int_exponential_histogram(
1012        "requested_deadlines_ns",
1013        finspect::ExponentialHistogramParams {
1014            floor: 0,
1015            initial_step: zx::BootDuration::from_micros(1).into_nanos(),
1016            // Allows capturing deadlines up to dozens of days.
1017            step_multiplier: 10,
1018            buckets: 16,
1019        },
1020    );
1021    let slack_histogram_prop = inspect.create_int_exponential_histogram(
1022        "slack_ns",
1023        finspect::ExponentialHistogramParams {
1024            floor: 0,
1025            initial_step: zx::BootDuration::from_micros(1).into_nanos(),
1026            step_multiplier: 10,
1027            buckets: 16,
1028        },
1029    );
1030    let schedule_delay_prop = inspect.create_int_exponential_histogram(
1031        "schedule_delay_ns",
1032        finspect::ExponentialHistogramParams {
1033            floor: 0,
1034            initial_step: zx::BootDuration::from_micros(1).into_nanos(),
1035            step_multiplier: 10,
1036            buckets: 16,
1037        },
1038    );
1039    let boot_deadlines_count_prop = inspect.create_uint("boot_deadlines_count", 0);
1040    let utc_deadlines_count_prop = inspect.create_uint("utc_deadlines_count", 0);
1041    // Internals of what was programmed into the wake alarms hardware.
1042    let hw_node = inspect.create_child("hardware");
1043    let current_hw_deadline_prop = hw_node.create_string("current_deadline", "");
1044    let remaining_until_alarm_prop = hw_node.create_string("remaining_until_alarm", "");
1045
1046    while let Some(cmd) = cmds.next().await {
1047        trace::duration!(c"alarms", c"Cmd");
1048        // Use a consistent notion of "now" across commands.
1049        let now = fasync::BootInstant::now();
1050        now_prop.set(now.into_nanos());
1051        trace::instant!(c"alarms", c"wake_timer_loop", trace::Scope::Process, "now" => now.into_nanos());
1052        match cmd {
1053            Cmd::Start { conn_id, deadline, mode, alarm_id, responder } => {
1054                trace::duration!(c"alarms", c"Cmd::Start");
1055                fuchsia_trace::flow_step!(
1056                    c"alarms",
1057                    c"hrtimer_lifecycle",
1058                    timers::get_trace_id(&alarm_id)
1059                );
1060                // NOTE: hold keep_alive until all work is done.
1061                debug!(
1062                    "wake_timer_loop: START alarm_id: \"{}\", conn_id: {:?}\n\tdeadline: {}\n\tnow:      {}",
1063                    alarm_id,
1064                    conn_id,
1065                    deadline,
1066                    format_timer(now.into()),
1067                );
1068
1069                defer! {
1070                    // This is the only option that requires further action.
1071                    if let Some(mode) = mode {
1072                        if let fta::SetMode::NotifySetupDone(setup_done) = mode {
1073                            // Must signal once the setup is completed.
1074                            signal(&setup_done);
1075                            debug!("wake_timer_loop: START: setup_done signaled");
1076                        };
1077                    }
1078                }
1079                let deadline_boot = deadline.as_boot(&*utc_transform.borrow());
1080
1081                // TODO: b/444236931: re-enable.
1082                //// Bookkeeping, record the incidence of deadline types.
1083                //deadline_histogram_prop.insert((deadline_boot - now).into_nanos());
1084                match deadline {
1085                    timers::Deadline::Boot(_) => boot_deadlines_count_prop.add(1),
1086                    timers::Deadline::Utc(_) => utc_deadlines_count_prop.add(1),
1087                };
1088
1089                if timers::Heap::expired(now, deadline_boot) {
1090                    trace::duration!(c"alarms", c"Cmd::Start:immediate");
1091                    fuchsia_trace::flow_step!(
1092                        c"alarms",
1093                        c"hrtimer_lifecycle",
1094                        timers::get_trace_id(&alarm_id)
1095                    );
1096                    // A timer set into now or the past expires right away.
1097                    let (_lease, keep_alive) = zx::EventPair::create();
1098                    debug!(
1099                        "[{}] wake_timer_loop: bogus lease {:?}",
1100                        line!(),
1101                        &keep_alive.get_koid().unwrap()
1102                    );
1103
1104                    if let Err(e) = responder
1105                        .send(&alarm_id, Ok(keep_alive))
1106                        .expect("responder is always present")
1107                    {
1108                        error!(
1109                            "wake_timer_loop: conn_id: {conn_id:?}, alarm: {alarm_id}: could not notify, dropping: {e}",
1110                        );
1111                    } else {
1112                        debug!(
1113                            "wake_timer_loop: conn_id: {conn_id:?}, alarm: {alarm_id}: EXPIRED IMMEDIATELY\n\tdeadline({}) <= now({})\n\tfull deadline: {}",
1114                            format_timer(deadline_boot.into()),
1115                            format_timer(now.into()),
1116                            deadline,
1117                        )
1118                    }
1119                } else {
1120                    trace::duration!(c"alarms", c"Cmd::Start:regular");
1121                    fuchsia_trace::flow_step!(
1122                        c"alarms",
1123                        c"hrtimer_lifecycle",
1124                        timers::get_trace_id(&alarm_id)
1125                    );
1126                    // A timer scheduled for the future gets inserted into the timer heap.
1127                    let was_empty = timers.is_empty();
1128
1129                    let deadline_before = timers.peek_deadline_as_boot();
1130                    let node = match deadline {
1131                        timers::Deadline::Boot(_) => {
1132                            timers.new_node_boot(deadline_boot, alarm_id, conn_id, responder)
1133                        }
1134                        timers::Deadline::Utc(d) => {
1135                            timers.new_node_utc(d, alarm_id, conn_id, responder)
1136                        }
1137                    };
1138                    timers.push(node);
1139                    let deadline_after = timers.peek_deadline_as_boot();
1140
1141                    let deadline_changed = is_deadline_changed(deadline_before, deadline_after);
1142                    let needs_cancel = !was_empty && deadline_changed;
1143                    let needs_reschedule = was_empty || deadline_changed;
1144
1145                    if needs_reschedule {
1146                        // Always schedule the proximate deadline.
1147                        let schedulable_deadline = deadline_after.unwrap_or(deadline_boot);
1148                        if needs_cancel {
1149                            log_long_op!(stop_hrtimer(&timer_proxy, &timer_config));
1150                        }
1151                        hrtimer_status = Some(log_long_op!(schedule_hrtimer(
1152                            scope.clone(),
1153                            now,
1154                            &timer_proxy,
1155                            schedulable_deadline,
1156                            snd.clone(),
1157                            &timer_config,
1158                            &schedule_delay_prop,
1159                        )));
1160                    }
1161                }
1162            }
1163            Cmd::StopById { timer_id, done } => {
1164                trace::duration!(c"alarms", c"Cmd::StopById", "alarm_id" => timer_id.alarm());
1165                fuchsia_trace::flow_step!(
1166                    c"alarms",
1167                    c"hrtimer_lifecycle",
1168                    timers::get_trace_id(&timer_id.alarm())
1169                );
1170                debug!("wake_timer_loop: STOP timer: {}", timer_id);
1171                let deadline_before = timers.peek_deadline_as_boot();
1172
1173                if let Some(timer_node) = timers.remove_by_id(&timer_id) {
1174                    let deadline_after = timers.peek_deadline_as_boot();
1175
1176                    if let Some(res) = timer_node
1177                        .get_responder()
1178                        .send(timer_node.id().alarm(), Err(fta::WakeAlarmsError::Dropped))
1179                    {
1180                        // We must reply to the responder to keep the connection open.
1181                        res.expect("infallible");
1182                    }
1183                    if is_deadline_changed(deadline_before, deadline_after) {
1184                        log_long_op!(stop_hrtimer(&timer_proxy, &timer_config));
1185                    }
1186                    if let Some(deadline) = deadline_after {
1187                        // Reschedule the hardware timer if the removed timer is the earliest one,
1188                        // and another one exists.
1189                        let new_timer_state = log_long_op!(schedule_hrtimer(
1190                            scope.clone(),
1191                            now,
1192                            &timer_proxy,
1193                            deadline,
1194                            snd.clone(),
1195                            &timer_config,
1196                            &schedule_delay_prop,
1197                        ));
1198                        let old_hrtimer_status = hrtimer_status.replace(new_timer_state);
1199                        if let Some(task) = old_hrtimer_status.map(|ev| ev.task) {
1200                            // Allow the task to complete. Since this task should have been
1201                            // canceled or completed already, this call should not block for
1202                            // a long time.
1203                            log_long_op!(task);
1204                        }
1205                    } else {
1206                        // No next timer, clean up the hrtimer status.
1207                        hrtimer_status = None;
1208                    }
1209                } else {
1210                    debug!("wake_timer_loop: STOP: no active timer to stop: {}", timer_id);
1211                }
1212                signal(&done);
1213            }
1214            Cmd::Alarm { expired_deadline, keep_alive } => {
1215                trace::duration!(c"alarms", c"Cmd::Alarm");
1216                // Expire all eligible timers, based on "now".  This is because
1217                // we may have woken up earlier than the actual deadline. This
1218                // happens for example if the timer can not make the actual
1219                // deadline and needs to be re-programmed.
1220                debug!(
1221                    "wake_timer_loop: ALARM!!! reached deadline: {}, wakey-wakey! {:?}",
1222                    format_timer(expired_deadline.into()),
1223                    keep_alive.get_koid().unwrap(),
1224                );
1225                let expired_count =
1226                    notify_all(&mut timers, &keep_alive, now, &slack_histogram_prop)
1227                        .expect("notification succeeds");
1228                if expired_count == 0 {
1229                    // This could be a resolution switch, or a straggler notification.
1230                    // Either way, the hardware timer is still ticking, cancel it.
1231                    debug!("wake_timer_loop: no expired alarms, reset hrtimer state");
1232                    log_long_op!(stop_hrtimer(&timer_proxy, &timer_config));
1233                }
1234                // There is a timer to reschedule, do that now.
1235                hrtimer_status = match timers.peek_deadline_as_boot() {
1236                    None => None,
1237                    Some(deadline) => Some(log_long_op!(schedule_hrtimer(
1238                        scope.clone(),
1239                        now,
1240                        &timer_proxy,
1241                        deadline,
1242                        snd.clone(),
1243                        &timer_config,
1244                        &schedule_delay_prop,
1245                    ))),
1246                }
1247            }
1248            Cmd::AlarmFidlError { expired_deadline, error } => {
1249                trace::duration!(c"alarms", c"Cmd::AlarmFidlError");
1250                // We do not have a wake lease, so the system may sleep before
1251                // we get to schedule a new timer. We have no way to avoid it
1252                // today.
1253                warn!(
1254                    "wake_timer_loop: FIDL error: {:?}, deadline: {}, now: {}",
1255                    error,
1256                    format_timer(expired_deadline.into()),
1257                    format_timer(now.into()),
1258                );
1259                // Manufacture a fake lease to make the code below work.
1260                // Maybe use Option instead?
1261                let (_dummy_lease, peer) = zx::EventPair::create();
1262                debug!(
1263                    "bogus lease: {:?} fidl error [{}:{}]",
1264                    &peer.get_koid().unwrap(),
1265                    file!(),
1266                    line!()
1267                );
1268                notify_all(&mut timers, &peer, now, &slack_histogram_prop)
1269                    .expect("notification succeeds");
1270                hrtimer_status = match timers.peek_deadline_as_boot() {
1271                    None => None, // No remaining timers, nothing to schedule.
1272                    Some(deadline) => Some(log_long_op!(schedule_hrtimer(
1273                        scope.clone(),
1274                        now,
1275                        &timer_proxy,
1276                        deadline,
1277                        snd.clone(),
1278                        &timer_config,
1279                        &schedule_delay_prop,
1280                    ))),
1281                }
1282            }
1283            Cmd::AlarmDriverError {
1284                expired_deadline,
1285                error,
1286                timer_config_id,
1287                resolution_nanos,
1288                ticks,
1289            } => {
1290                trace::duration!(c"alarms", c"Cmd::AlarmDriverError");
1291                let (_dummy_lease, peer) = zx::EventPair::create();
1292                debug!(
1293                    "bogus lease: {:?} driver error. [{}:{}]",
1294                    &peer.get_koid().unwrap(),
1295                    file!(),
1296                    line!()
1297                );
1298                notify_all(&mut timers, &peer, now, &slack_histogram_prop)
1299                    .expect("notification succeeds");
1300                match error {
1301                    fidl_fuchsia_hardware_hrtimer::DriverError::Canceled => {
1302                        // Nothing to do here, cancelation is handled in Stop code.
1303                        debug!(
1304                            "wake_timer_loop: CANCELED timer at deadline: {}",
1305                            format_timer(expired_deadline.into())
1306                        );
1307                    }
1308                    _ => {
1309                        error!(
1310                            "wake_timer_loop: DRIVER SAYS: {:?}, deadline: {}, now: {}\n\ttimer_id={}\n\tresolution={}\n\tticks={}",
1311                            error,
1312                            format_timer(expired_deadline.into()),
1313                            format_timer(now.into()),
1314                            timer_config_id,
1315                            resolution_nanos,
1316                            ticks,
1317                        );
1318                        // We do not have a wake lease, so the system may sleep before
1319                        // we get to schedule a new timer. We have no way to avoid it
1320                        // today.
1321                        hrtimer_status = match timers.peek_deadline_as_boot() {
1322                            None => None,
1323                            Some(deadline) => Some(log_long_op!(schedule_hrtimer(
1324                                scope.clone(),
1325                                now,
1326                                &timer_proxy,
1327                                deadline,
1328                                snd.clone(),
1329                                &timer_config,
1330                                &schedule_delay_prop,
1331                            ))),
1332                        }
1333                    }
1334                }
1335            }
1336            Cmd::UtcUpdated { transform } => {
1337                trace::duration!(c"alarms", c"Cmd::UtcUpdated");
1338                debug!("wake_timer_loop: applying new clock transform: {transform:?}");
1339
1340                // Assigning to this shared reference updates the deadlines of all
1341                // UTC timers.
1342                *utc_transform.borrow_mut() = transform;
1343
1344                // Reschedule the hardware timer with the now-current deadline if there is an
1345                // active timer.
1346                if hrtimer_status.is_some() {
1347                    log_long_op!(stop_hrtimer(&timer_proxy, &timer_config));
1348                    // Should we request a wake lock here?
1349                    hrtimer_status = match timers.peek_deadline_as_boot() {
1350                        None => None,
1351                        Some(deadline) => Some(log_long_op!(schedule_hrtimer(
1352                            scope.clone(),
1353                            now,
1354                            &timer_proxy,
1355                            deadline,
1356                            snd.clone(),
1357                            &timer_config,
1358                            &schedule_delay_prop,
1359                        ))),
1360                    }
1361                }
1362            }
1363        }
1364
1365        {
1366            // Print and record diagnostics after each iteration, record the
1367            // duration for performance awareness.  Note that iterations happen
1368            // only occasionally, so these stats can remain unchanged for a long
1369            // time.
1370            trace::duration!(c"timekeeper", c"inspect");
1371            let now_formatted = format_timer(now.into());
1372            debug!("wake_timer_loop: now:                             {}", &now_formatted);
1373            now_formatted_prop.set(&now_formatted);
1374
1375            let pending_timers_count: u64 =
1376                timers.timer_count().try_into().expect("always convertible");
1377            debug!("wake_timer_loop: currently pending timer count:   {}", pending_timers_count);
1378            pending_timers_count_prop.set(pending_timers_count);
1379
1380            let pending_timers = format!("{}", timers);
1381            debug!("wake_timer_loop: currently pending timers:        \n\t{}", &timers);
1382            pending_timers_prop.set(&pending_timers);
1383
1384            let current_deadline: String = hrtimer_status
1385                .as_ref()
1386                .map(|s| format!("{}", format_timer(s.deadline.into())))
1387                .unwrap_or_else(|| "(none)".into());
1388            debug!("wake_timer_loop: current hardware timer deadline: {:?}", current_deadline);
1389            current_hw_deadline_prop.set(&current_deadline);
1390
1391            let remaining_duration_until_alarm = hrtimer_status
1392                .as_ref()
1393                .map(|s| format!("{}", format_duration((s.deadline - now).into())))
1394                .unwrap_or_else(|| "(none)".into());
1395            debug!(
1396                "wake_timer_loop: remaining duration until alarm:  {}",
1397                &remaining_duration_until_alarm
1398            );
1399            remaining_until_alarm_prop.set(&remaining_duration_until_alarm);
1400            debug!("---");
1401        }
1402    }
1403
1404    debug!("wake_timer_loop: exiting. This is unlikely in prod code.");
1405}
1406
1407/// Schedules a wake alarm.
1408///
1409/// # Args:
1410///
1411/// - `scope`: used to spawn async tasks.
1412/// - `now`: the time instant used as the value of current instant.
1413/// - `hrtimer`: the proxy for the hrtimer device driver.
1414/// - `deadline`: the time instant in the future at which the alarm should fire.
1415/// - `command_send`: the sender channel to use when the timer expires.
1416/// - `timer_config`: a configuration of the hardware timer showing supported resolutions and
1417///   max tick value.
1418/// - `schedule_delay_histogram`: inspect instrumentation.
1419async fn schedule_hrtimer(
1420    scope: fasync::ScopeHandle,
1421    now: fasync::BootInstant,
1422    hrtimer: &Box<dyn TimerOps>,
1423    deadline: fasync::BootInstant,
1424    mut command_send: mpsc::Sender<Cmd>,
1425    timer_config: &TimerConfig,
1426    _schedule_delay_histogram: &finspect::IntExponentialHistogramProperty,
1427) -> TimerState {
1428    let timeout = std::cmp::max(zx::BootDuration::ZERO, deadline - now);
1429    trace::duration!(c"alarms", c"schedule_hrtimer", "timeout" => timeout.into_nanos());
1430    // When signaled, the hrtimer has been scheduled.
1431    let hrtimer_scheduled = zx::Event::create();
1432
1433    debug!(
1434        "schedule_hrtimer:\n\tnow: {}\n\tdeadline: {}\n\ttimeout: {}",
1435        format_timer(now.into()),
1436        format_timer(deadline.into()),
1437        format_duration(timeout),
1438    );
1439
1440    let slack = timer_config.pick_setting(timeout);
1441    let resolution_nanos = slack.resolution.into_nanos();
1442    // The driver will reject "0" ticks, even though it probably shouldn't. See for details:
1443    // b/437177931.
1444    let useful_ticks = std::cmp::max(MIN_USEFUL_TICKS, slack.ticks());
1445
1446    trace::instant!(c"alarms", c"hrtimer:programmed",
1447        trace::Scope::Process,
1448        "resolution_ns" => resolution_nanos,
1449        "ticks" => useful_ticks
1450    );
1451    let timer_config_id = timer_config.id;
1452    let start_and_wait_fut = hrtimer.start_and_wait(
1453        timer_config.id,
1454        &ffhh::Resolution::Duration(resolution_nanos),
1455        useful_ticks,
1456        clone_handle(&hrtimer_scheduled),
1457    );
1458    let hrtimer_scheduled_if_error = clone_handle(&hrtimer_scheduled);
1459    let hrtimer_task = scope.spawn_local(async move {
1460        debug!("hrtimer_task: waiting for hrtimer driver response");
1461        trace::instant!(c"alarms", c"hrtimer:started", trace::Scope::Process);
1462        let response = start_and_wait_fut.await;
1463        trace::instant!(c"alarms", c"hrtimer:response", trace::Scope::Process);
1464        match response {
1465            Err(TimerOpsError::Fidl(e)) => {
1466                defer! {
1467                    // Allow hrtimer_scheduled to proceed anyways.
1468                    signal(&hrtimer_scheduled_if_error);
1469                }
1470                trace::instant!(c"alarms", c"hrtimer:response:fidl_error", trace::Scope::Process);
1471                warn!("hrtimer_task: hrtimer FIDL error: {:?}", e);
1472                command_send
1473                    .start_send(Cmd::AlarmFidlError { expired_deadline: now, error: e })
1474                    .unwrap();
1475                // BAD: no way to keep alive.
1476            }
1477            Err(TimerOpsError::Driver(e)) => {
1478                defer! {
1479                    // This should be idempotent if the error occurs after
1480                    // the timer was scheduled.
1481                    signal(&hrtimer_scheduled_if_error);
1482                }
1483                let driver_error_str = format!("{:?}", e);
1484                trace::instant!(c"alarms", c"hrtimer:response:driver_error", trace::Scope::Process, "error" => &driver_error_str[..]);
1485                // This is very common. For example, a "timer canceled" event
1486                // will result in this code path being hit.
1487                debug!("schedule_hrtimer: hrtimer driver error: {:?}", e);
1488                command_send
1489                    .start_send(Cmd::AlarmDriverError {
1490                        expired_deadline: now,
1491                        error: e,
1492                        timer_config_id,
1493                        resolution_nanos,
1494                        ticks: useful_ticks,
1495                    })
1496                    .unwrap();
1497                // BAD: no way to keep alive.
1498            }
1499            Ok(keep_alive) => {
1500                trace::instant!(c"alarms", c"hrtimer:response:alarm", trace::Scope::Process);
1501                debug!("hrtimer: got alarm response: {:?}", keep_alive);
1502                // May trigger sooner than the deadline.
1503                command_send
1504                    .start_send(Cmd::Alarm { expired_deadline: deadline, keep_alive })
1505                    .unwrap();
1506            }
1507        }
1508        debug!("hrtimer_task: exiting task.");
1509        trace::instant!(c"alarms", c"hrtimer:task_exit", trace::Scope::Process);
1510    }).into();
1511    debug!("schedule_hrtimer: waiting for event to be signaled");
1512
1513    // We must wait here to ensure that the wake alarm has been scheduled.
1514    log_long_op!(wait_signaled(&hrtimer_scheduled));
1515
1516    let now_after_signaled = fasync::BootInstant::now();
1517    let duration_until_scheduled: zx::BootDuration = (now_after_signaled - now).into();
1518    if duration_until_scheduled > zx::BootDuration::from_nanos(LONG_DELAY_NANOS) {
1519        trace::duration!(c"alarms", c"schedule_hrtimer:unusual_duration",
1520            "duration" => duration_until_scheduled.into_nanos());
1521        warn!(
1522            "unusual duration until hrtimer scheduled: {}",
1523            format_duration(duration_until_scheduled)
1524        );
1525    }
1526    // TODO: b/444236931: re-enable.
1527    //schedule_delay_histogram.insert(duration_until_scheduled.into_nanos());
1528    debug!("schedule_hrtimer: hrtimer wake alarm has been scheduled.");
1529    TimerState { task: hrtimer_task, deadline }
1530}
1531
1532/// Notify all `timers` that `reference_instant` has been reached.
1533///
1534/// The notified `timers` are removed from the list of timers to notify.
1535///
1536/// Args:
1537/// - `timers`: the collection of currently available timers.
1538/// - `lease_prototype`: an EventPair used as a wake lease.
1539/// - `reference_instant`: the time instant used as a reference for alarm notification.
1540///   All timers
1541fn notify_all(
1542    timers: &mut timers::Heap,
1543    lease_prototype: &zx::EventPair,
1544    reference_instant: fasync::BootInstant,
1545    _unusual_slack_histogram: &finspect::IntExponentialHistogramProperty,
1546) -> Result<usize> {
1547    trace::duration!(c"alarms", c"notify_all");
1548    let now = fasync::BootInstant::now();
1549    let mut expired = 0;
1550    while let Some(timer_node) = timers.maybe_expire_earliest(reference_instant) {
1551        expired += 1;
1552        // How much later than requested did the notification happen.
1553        let deadline = timer_node.get_boot_deadline();
1554        let alarm_id = timer_node.id().alarm().to_string();
1555        trace::duration!(c"alarms", c"notify_all:notified", "alarm_id" => &*alarm_id);
1556        fuchsia_trace::flow_step!(c"alarms", c"hrtimer_lifecycle", timers::get_trace_id(&alarm_id));
1557        let conn_id = timer_node.id().conn.clone();
1558        let slack: zx::BootDuration = deadline - now;
1559        if slack < zx::BootDuration::from_nanos(-LONG_DELAY_NANOS) {
1560            trace::duration!(c"alarms", c"schedule_hrtimer:unusual_slack", "slack" => slack.into_nanos());
1561            // This alarm triggered noticeably later than it should have.
1562            warn!(
1563                "alarm id: {} had an unusually large slack: {}",
1564                alarm_id,
1565                format_duration(slack)
1566            );
1567        }
1568        if slack < zx::BootDuration::ZERO {
1569            // TODO: b/444236931: re-enable.
1570            //unusual_slack_histogram.insert(-slack.into_nanos());
1571        }
1572        debug!(
1573            concat!(
1574                "wake_alarm_loop: ALARM alarm_id: \"{}\"\n\tdeadline: {},\n\tconn_id: {:?},\n\t",
1575                "reference_instant: {},\n\tnow: {},\n\tslack: {}",
1576            ),
1577            alarm_id,
1578            format_timer(deadline.into()),
1579            conn_id,
1580            format_timer(reference_instant.into()),
1581            format_timer(now.into()),
1582            format_duration(slack),
1583        );
1584        let lease = clone_handle(lease_prototype);
1585        trace::instant!(c"alarms", c"notify", trace::Scope::Process, "alarm_id" => &alarm_id[..], "conn_id" => conn_id);
1586        if let Some(Err(e)) = timer_node.get_responder().send(timer_node.id().alarm(), Ok(lease)) {
1587            error!("could not signal responder: {:?}", e);
1588        }
1589        trace::instant!(c"alarms", c"notified", trace::Scope::Process);
1590    }
1591    trace::instant!(c"alarms", c"notify", trace::Scope::Process, "expired_count" => expired);
1592    debug!("notify_all: expired count: {}", expired);
1593    Ok(expired)
1594    // A new timer is not scheduled yet here.
1595}
1596
1597/// The hrtimer driver service directory.  hrtimer driver APIs appear as randomly
1598/// named files in this directory. They are expected to come and go.
1599const HRTIMER_DIRECTORY: &str = "/dev/class/hrtimer";
1600
1601/// Connects to the high resolution timer device driver.
1602///
1603/// This function watches the hrtimer device directory and connects to the first
1604/// available hrtimer device.
1605///
1606/// # Returns
1607/// A `Result` containing a `ffhh::DeviceProxy` on success, or an error if
1608/// the connection fails.
1609pub async fn connect_to_hrtimer_async() -> Result<ffhh::DeviceProxy> {
1610    debug!("connect_to_hrtimer: trying directory: {}", HRTIMER_DIRECTORY);
1611    let dir =
1612        fuchsia_fs::directory::open_in_namespace(HRTIMER_DIRECTORY, fidl_fuchsia_io::PERM_READABLE)
1613            .with_context(|| format!("Opening {}", HRTIMER_DIRECTORY))?;
1614    let path = device_watcher::watch_for_files(&dir)
1615        .await
1616        .with_context(|| format!("Watching for files in {}", HRTIMER_DIRECTORY))?
1617        .try_next()
1618        .await
1619        .with_context(|| format!("Getting a file from {}", HRTIMER_DIRECTORY))?;
1620    let path = path.ok_or_else(|| anyhow!("Could not find {}", HRTIMER_DIRECTORY))?;
1621    let path = path
1622        .to_str()
1623        .ok_or_else(|| anyhow!("Could not find a valid str for {}", HRTIMER_DIRECTORY))?;
1624    connect_to_named_protocol_at_dir_root::<ffhh::DeviceMarker>(&dir, path)
1625        .context("Failed to connect built-in service")
1626}
1627
1628#[cfg(test)]
1629mod tests {
1630    use super::*;
1631    use assert_matches::assert_matches;
1632    use diagnostics_assertions::{AnyProperty, assert_data_tree};
1633    use fuchsia_async::TestExecutor;
1634    use futures::select;
1635    use test_case::test_case;
1636    use test_util::{assert_gt, assert_lt};
1637
1638    fn fake_wake_lease() -> fidl_fuchsia_power_system::LeaseToken {
1639        let (_lease, peer) = zx::EventPair::create();
1640        peer
1641    }
1642
1643    #[test]
1644    fn timer_duration_no_overflow() {
1645        let duration1 = TimerDuration {
1646            resolution: zx::BootDuration::from_seconds(100_000_000),
1647            ticks: u64::MAX,
1648        };
1649        let duration2 = TimerDuration {
1650            resolution: zx::BootDuration::from_seconds(110_000_000),
1651            ticks: u64::MAX,
1652        };
1653        assert_eq!(duration1, duration1);
1654        assert_eq!(duration2, duration2);
1655
1656        assert_lt!(duration1, duration2);
1657        assert_gt!(duration2, duration1);
1658    }
1659
1660    #[test_case(
1661        TimerDuration::new(zx::BootDuration::from_nanos(1), 1),
1662        TimerDuration::new(zx::BootDuration::from_nanos(1), 1)
1663    )]
1664    #[test_case(
1665        TimerDuration::new(zx::BootDuration::from_nanos(1), 10),
1666        TimerDuration::new(zx::BootDuration::from_nanos(10), 1)
1667    )]
1668    #[test_case(
1669        TimerDuration::new(zx::BootDuration::from_nanos(10), 1),
1670        TimerDuration::new(zx::BootDuration::from_nanos(1), 10)
1671    )]
1672    #[test_case(
1673        TimerDuration::new(zx::BootDuration::from_micros(1), 1),
1674        TimerDuration::new(zx::BootDuration::from_nanos(1), 1000)
1675    )]
1676    fn test_slack_eq(one: TimerDuration, other: TimerDuration) {
1677        assert_eq!(one, other);
1678    }
1679
1680    #[test_case(
1681        TimerDuration::new(zx::BootDuration::from_nanos(1), 1),
1682        TimerDuration::new(zx::BootDuration::from_nanos(1), 2)
1683    )]
1684    #[test_case(
1685        TimerDuration::new(zx::BootDuration::from_nanos(1), 1),
1686        TimerDuration::new(zx::BootDuration::from_nanos(10), 1)
1687    )]
1688    fn test_slack_lt(one: TimerDuration, other: TimerDuration) {
1689        assert_lt!(one, other);
1690    }
1691
1692    #[test_case(
1693        TimerDuration::new(zx::BootDuration::from_nanos(1), 2),
1694        TimerDuration::new(zx::BootDuration::from_nanos(1), 1)
1695    )]
1696    #[test_case(
1697        TimerDuration::new(zx::BootDuration::from_nanos(10), 1),
1698        TimerDuration::new(zx::BootDuration::from_nanos(1), 1)
1699    )]
1700    fn test_slack_gt(one: TimerDuration, other: TimerDuration) {
1701        assert_gt!(one, other);
1702    }
1703
1704    #[test_case(
1705        vec![zx::BootDuration::from_nanos(1)],
1706        100,
1707        zx::BootDuration::from_nanos(0),
1708        TimerDuration::new(zx::BootDuration::from_nanos(1), 1) ; "0ns becomes 1ns"
1709    )]
1710    #[test_case(
1711        vec![zx::BootDuration::from_nanos(1)],
1712        100,
1713        zx::BootDuration::from_nanos(50),
1714        TimerDuration::new(zx::BootDuration::from_nanos(1), 50) ; "Exact at 50x1ns"
1715    )]
1716    #[test_case(
1717        vec![zx::BootDuration::from_nanos(2)],
1718        100,
1719        zx::BootDuration::from_nanos(50),
1720        TimerDuration::new(zx::BootDuration::from_nanos(2), 25) ; "Exact at 25x2ns"
1721    )]
1722    #[test_case(
1723        vec![zx::BootDuration::from_nanos(3)],
1724        100,
1725        zx::BootDuration::from_nanos(50),
1726        // The closest duration is 51ns.
1727        TimerDuration::new(zx::BootDuration::from_nanos(3), 17) ; "Inexact at 51ns"
1728    )]
1729    #[test_case(
1730        vec![
1731            zx::BootDuration::from_nanos(3),
1732            zx::BootDuration::from_nanos(4)
1733        ],
1734        100,
1735        zx::BootDuration::from_nanos(50),
1736        TimerDuration::new(zx::BootDuration::from_nanos(3), 17) ; "3ns is a better resolution"
1737    )]
1738    #[test_case(
1739        vec![
1740            zx::BootDuration::from_nanos(1000),
1741        ],
1742        100,
1743        zx::BootDuration::from_nanos(50),
1744        TimerDuration::new(zx::BootDuration::from_nanos(1000), 1) ;
1745        "950ns negative slack is the best we can do"
1746    )]
1747    #[test_case(
1748        vec![
1749            zx::BootDuration::from_nanos(1),
1750        ],
1751        10,
1752        zx::BootDuration::from_nanos(50),
1753        TimerDuration::new(zx::BootDuration::from_nanos(1), 10) ;
1754        "10ns positive slack is the best we can do"
1755    )]
1756    #[test_case(
1757        vec![
1758            zx::BootDuration::from_millis(1),
1759            zx::BootDuration::from_micros(100),
1760            zx::BootDuration::from_micros(10),
1761            zx::BootDuration::from_micros(1),
1762        ],
1763        20,  // Make only one of the resolutions above match.
1764        zx::BootDuration::from_micros(150),
1765        TimerDuration::new(zx::BootDuration::from_micros(10), 15) ;
1766        "Realistic case with resolutions from driver, should be 15us"
1767    )]
1768    #[test_case(
1769        vec![
1770            zx::BootDuration::from_millis(1),
1771            zx::BootDuration::from_micros(100),
1772            zx::BootDuration::from_micros(10),
1773            zx::BootDuration::from_micros(1),
1774        ],
1775        2000,  // Make only one of the resolutions above match.
1776        zx::BootDuration::from_micros(6000),
1777        TimerDuration::new(zx::BootDuration::from_millis(1), 6) ;
1778        "Coarser exact unit wins"
1779    )]
1780    #[test_case(
1781        vec![
1782            zx::BootDuration::from_millis(1),
1783            zx::BootDuration::from_millis(10),
1784            zx::BootDuration::from_millis(100),
1785        ],
1786        1000,
1787        zx::BootDuration::from_micros(-10),
1788        TimerDuration::new(zx::BootDuration::from_millis(1), 1) ;
1789        "Negative duration gets the smallest timer duration"
1790    )]
1791    #[test_case(
1792        vec![
1793            zx::BootDuration::from_millis(1),
1794            zx::BootDuration::from_millis(10),
1795            zx::BootDuration::from_millis(100),
1796        ],
1797        1000,
1798        zx::BootDuration::ZERO,
1799        TimerDuration::new(zx::BootDuration::from_millis(1), 1) ;
1800        "Zero duration gets the smallest timer duration"
1801    )]
1802    fn test_pick_setting(
1803        resolutions: Vec<zx::BootDuration>,
1804        max_ticks: u64,
1805        duration: zx::BootDuration,
1806        expected: TimerDuration,
1807    ) {
1808        let config = TimerConfig::new_from_data(MAIN_TIMER_ID as u64, &resolutions[..], max_ticks);
1809        let actual = config.pick_setting(duration);
1810
1811        // .eq() does not work here, since we do not just require that the values
1812        // be equal, but also that the same resolution is used in both.
1813        assert_slack_eq(expected, actual);
1814    }
1815
1816    // TimerDuration assertion with human-friendly output in case of an error.
1817    fn assert_slack_eq(expected: TimerDuration, actual: TimerDuration) {
1818        let slack = expected.duration() - actual.duration();
1819        assert_eq!(
1820            actual.resolution(),
1821            expected.resolution(),
1822            "\n\texpected: {} ({})\n\tactual  : {} ({})\n\tslack: expected-actual={}",
1823            expected,
1824            format_duration(expected.duration()),
1825            actual,
1826            format_duration(actual.duration()),
1827            format_duration(slack)
1828        );
1829        assert_eq!(
1830            actual.ticks(),
1831            expected.ticks(),
1832            "\n\texpected: {} ({})\n\tactual  : {} ({})\n\tslack: expected-actual={}",
1833            expected,
1834            format_duration(expected.duration()),
1835            actual,
1836            format_duration(actual.duration()),
1837            format_duration(slack)
1838        );
1839    }
1840
1841    #[derive(Debug)]
1842    enum FakeCmd {
1843        SetProperties {
1844            resolutions: Vec<zx::BootDuration>,
1845            max_ticks: i64,
1846            keep_alive: zx::EventPair,
1847            done: zx::Event,
1848        },
1849    }
1850
1851    use std::cell::RefCell;
1852    use std::rc::Rc;
1853
1854    // A fake that emulates some aspects of the hrtimer driver.
1855    //
1856    // Specifically it can be configured with different resolutions, and will
1857    // bomb out if any waiting methods are called twice in a succession, without
1858    // canceling the timer in between.
1859    fn fake_hrtimer_connection(
1860        scope: fasync::ScopeHandle,
1861        rcv: mpsc::Receiver<FakeCmd>,
1862    ) -> ffhh::DeviceProxy {
1863        debug!("fake_hrtimer_connection: entry.");
1864        let (hrtimer, mut stream) =
1865            fidl::endpoints::create_proxy_and_stream::<ffhh::DeviceMarker>();
1866        scope.clone().spawn_local(async move {
1867            let mut rcv = rcv.fuse();
1868            let timer_properties = Rc::new(RefCell::new(None));
1869            let wake_lease = Rc::new(RefCell::new(None));
1870
1871            // Set to true when the hardware timer is supposed to be running.
1872            // Hardware timer may not be reprogrammed without canceling it first,
1873            // make sure the tests fail the same way as production would.
1874            let timer_running = Rc::new(RefCell::new(false));
1875
1876            loop {
1877                let timer_properties = timer_properties.clone();
1878                let wake_lease = wake_lease.clone();
1879                select! {
1880                    cmd = rcv.next() => {
1881                        debug!("fake_hrtimer_connection: cmd: {:?}", cmd);
1882                        match cmd {
1883                            Some(FakeCmd::SetProperties{ resolutions, max_ticks, keep_alive, done}) => {
1884                                let mut timer_props = vec![];
1885                                for v in 0..10 {
1886                                    timer_props.push(ffhh::TimerProperties {
1887                                        supported_resolutions: Some(
1888                                            resolutions.iter()
1889                                                .map(|d| ffhh::Resolution::Duration(d.into_nanos())).collect()),
1890                                        max_ticks: Some(max_ticks.try_into().unwrap()),
1891                                        // start_and_wait method works.
1892                                        supports_wait: Some(true),
1893                                        id: Some(v),
1894                                        ..Default::default()
1895                                        },
1896                                    );
1897                                }
1898                                *timer_properties.borrow_mut() = Some(timer_props);
1899                                *wake_lease.borrow_mut() = Some(keep_alive);
1900                                debug!("set timer properties to: {:?}", timer_properties);
1901                                signal(&done);
1902                            }
1903                            e => {
1904                                panic!("unrecognized command: {:?}", e);
1905                            }
1906                        }
1907                        // Set some responses if we have them.
1908                    },
1909                    event = stream.next() => {
1910                        debug!("fake_hrtimer_connection: event: {:?}", event);
1911                        if let Some(Ok(event)) = event {
1912                            match event {
1913                                ffhh::DeviceRequest::Start { responder, .. } => {
1914                                    assert!(!*timer_running.borrow(), "invariant broken: timer may not be running here");
1915                                    *timer_running.borrow_mut() = true;
1916                                    responder.send(Ok(())).expect("");
1917                                }
1918                                ffhh::DeviceRequest::Stop { responder, .. } => {
1919                                    *timer_running.borrow_mut() = false;
1920                                    responder.send(Ok(())).expect("");
1921                                }
1922                                ffhh::DeviceRequest::GetTicksLeft { responder, .. } => {
1923                                    responder.send(Ok(1)).expect("");
1924                                }
1925                                ffhh::DeviceRequest::SetEvent { responder, .. } => {
1926                                    responder.send(Ok(())).expect("");
1927                                }
1928                                ffhh::DeviceRequest::StartAndWait { id, resolution, ticks, setup_event, responder, .. } => {
1929                                    assert!(!*timer_running.borrow(), "invariant broken: timer may not be running here");
1930                                    *timer_running.borrow_mut() = true;
1931                                    debug!("fake_hrtimer_connection: starting timer: \"{}\", resolution: {:?}, ticks: {}", id, resolution, ticks);
1932                                    let ticks: i64 = ticks.try_into().unwrap();
1933                                    let sleep_duration  = zx::BootDuration::from_nanos(ticks * match resolution {
1934                                        ffhh::Resolution::Duration(e) => e,
1935                                        _ => {
1936                                            error!("resolution has an unexpected value");
1937                                            1
1938                                        }
1939                                    });
1940                                    let timer_running_clone = timer_running.clone();
1941                                    scope.spawn_local(async move {
1942                                        // Signaling the setup event allows the client to proceed
1943                                        // with post-scheduling work.
1944                                        signal(&setup_event);
1945
1946                                        // Respond after the requested sleep time. In tests this will
1947                                        // be sleeping in fake time.
1948                                        fasync::Timer::new(sleep_duration).await;
1949                                        *timer_running_clone.borrow_mut() = false;
1950                                        responder.send(Ok(clone_handle(wake_lease.borrow().as_ref().unwrap()))).unwrap();
1951                                        debug!("StartAndWait: hrtimer expired");
1952                                    });
1953                                }
1954                                ffhh::DeviceRequest::StartAndWait2 { responder, .. } => {
1955                                    assert!(!*timer_running.borrow(), "invariant broken: timer may not be running here");
1956                                    *timer_running.borrow_mut() = true;
1957                                    responder.send(Err(ffhh::DriverError::InternalError)).expect("");
1958                                }
1959                                ffhh::DeviceRequest::GetProperties { responder, .. } => {
1960                                    if (*timer_properties).borrow().is_none() {
1961                                        error!("timer_properties is empty, this is not what you want!");
1962                                    }
1963                                    responder
1964                                        .send(ffhh::Properties {
1965                                            timers_properties: (*timer_properties).borrow().clone(),
1966                                            ..Default::default()
1967                                        })
1968                                        .expect("");
1969                                }
1970                                ffhh::DeviceRequest::ReadTimer { responder, .. } => {
1971                                    responder.send(Err(ffhh::DriverError::NotSupported)).expect("");
1972                                }
1973                                ffhh::DeviceRequest::ReadClock { responder, .. } => {
1974                                    responder.send(Err(ffhh::DriverError::NotSupported)).expect("");
1975                                }
1976                                ffhh::DeviceRequest::_UnknownMethod { .. } => todo!(),
1977                            }
1978                        }
1979                    },
1980                }
1981            }
1982        });
1983        hrtimer
1984    }
1985
1986    fn clone_utc_clock(orig: &fxr::UtcClock) -> fxr::UtcClock {
1987        orig.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap()
1988    }
1989
1990    struct TestContext {
1991        wake_proxy: fta::WakeAlarmsProxy,
1992        _scope: fasync::Scope,
1993        _cmd_tx: mpsc::Sender<FakeCmd>,
1994        // Use to manipulate the UTC clock from the test.
1995        utc_clock: fxr::UtcClock,
1996        utc_backstop: fxr::UtcInstant,
1997    }
1998
1999    impl TestContext {
2000        async fn new() -> Self {
2001            TestExecutor::advance_to(fasync::MonotonicInstant::from_nanos(0)).await;
2002
2003            let scope = fasync::Scope::new();
2004            let utc_backstop = fxr::UtcInstant::from_nanos(1000);
2005            let utc_clock =
2006                fxr::UtcClock::create(zx::ClockOpts::empty(), Some(utc_backstop)).unwrap();
2007            let utc_clone = clone_utc_clock(&utc_clock);
2008            let (mut cmd_tx, wake_proxy) = {
2009                let (tx, rx) = mpsc::channel::<FakeCmd>(0);
2010                let hrtimer_proxy = fake_hrtimer_connection(scope.to_handle(), rx);
2011
2012                let inspector = finspect::component::inspector();
2013                let alarms = Rc::new(Loop::new(
2014                    scope.to_handle(),
2015                    hrtimer_proxy,
2016                    inspector.root().create_child("test"),
2017                    utc_clone,
2018                ));
2019
2020                let (proxy, stream) =
2021                    fidl::endpoints::create_proxy_and_stream::<fta::WakeAlarmsMarker>();
2022                scope.spawn_local(async move {
2023                    serve(alarms, stream).await;
2024                });
2025                (tx, proxy)
2026            };
2027
2028            let (_wake_lease, peer) = zx::EventPair::create();
2029            let done = zx::Event::create();
2030            cmd_tx
2031                .start_send(FakeCmd::SetProperties {
2032                    resolutions: vec![zx::Duration::from_nanos(1)],
2033                    max_ticks: 100,
2034                    keep_alive: peer,
2035                    done: done.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap(),
2036                })
2037                .unwrap();
2038
2039            // Wait until hrtimer configuration has completed.
2040            assert_matches!(fasync::OnSignals::new(done, zx::Signals::EVENT_SIGNALED).await, Ok(_));
2041
2042            Self { wake_proxy, _scope: scope, _cmd_tx: cmd_tx, utc_clock, utc_backstop }
2043        }
2044    }
2045
2046    impl Drop for TestContext {
2047        fn drop(&mut self) {
2048            assert_matches!(TestExecutor::next_timer(), None, "Unexpected lingering timers");
2049        }
2050    }
2051
2052    #[fuchsia::test(allow_stalls = false)]
2053    async fn test_basic_timed_wait() {
2054        let ctx = TestContext::new().await;
2055
2056        let deadline = zx::BootInstant::from_nanos(100);
2057        let setup_done = zx::Event::create();
2058        let mut set_task = ctx.wake_proxy.set_and_wait(
2059            deadline.into(),
2060            fta::SetMode::NotifySetupDone(
2061                setup_done.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap(),
2062            ),
2063            "Hello".into(),
2064        );
2065
2066        assert_matches!(TestExecutor::poll_until_stalled(&mut set_task).await, Poll::Pending);
2067
2068        let mut setup_done_task = fasync::OnSignals::new(setup_done, zx::Signals::EVENT_SIGNALED);
2069        assert_matches!(
2070            TestExecutor::poll_until_stalled(&mut setup_done_task).await,
2071            Poll::Ready(Ok(_)),
2072            "Setup event not triggered after scheduling an alarm"
2073        );
2074
2075        TestExecutor::advance_to(fasync::MonotonicInstant::from_nanos(100)).await;
2076        assert_matches!(TestExecutor::poll_until_stalled(set_task).await, Poll::Ready(Ok(Ok(_))));
2077    }
2078
2079    #[fuchsia::test(allow_stalls = false)]
2080    async fn test_basic_timed_wait_notify() {
2081        const ALARM_ID: &str = "Hello";
2082        let ctx = TestContext::new().await;
2083
2084        let (notifier_client, mut notifier_stream) =
2085            fidl::endpoints::create_request_stream::<fta::NotifierMarker>();
2086        let setup_done = zx::Event::create();
2087        assert_matches!(
2088            ctx.wake_proxy
2089                .set(
2090                    notifier_client,
2091                    fidl::BootInstant::from_nanos(2),
2092                    fta::SetMode::NotifySetupDone(
2093                        setup_done.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap()
2094                    ),
2095                    ALARM_ID,
2096                )
2097                .await,
2098            Ok(Ok(()))
2099        );
2100
2101        let mut done_task = fasync::OnSignals::new(setup_done, zx::Signals::EVENT_SIGNALED);
2102        assert_matches!(
2103            TestExecutor::poll_until_stalled(&mut done_task).await,
2104            Poll::Ready(Ok(_)),
2105            "Setup event not triggered after scheduling an alarm"
2106        );
2107
2108        let mut next_task = notifier_stream.next();
2109        assert_matches!(TestExecutor::poll_until_stalled(&mut next_task).await, Poll::Pending);
2110
2111        TestExecutor::advance_to(fasync::MonotonicInstant::from_nanos(1)).await;
2112        assert_matches!(TestExecutor::poll_until_stalled(&mut next_task).await, Poll::Pending);
2113
2114        TestExecutor::advance_to(fasync::MonotonicInstant::from_nanos(2)).await;
2115        assert_matches!(
2116            TestExecutor::poll_until_stalled(next_task).await,
2117            Poll::Ready(Some(Ok(fta::NotifierRequest::Notify { alarm_id, .. }))) if alarm_id == ALARM_ID
2118        );
2119    }
2120
2121    #[fuchsia::test(allow_stalls = false)]
2122    async fn test_two_alarms_same() {
2123        const DEADLINE_NANOS: i64 = 100;
2124
2125        let ctx = TestContext::new().await;
2126
2127        let mut set_task_1 = ctx.wake_proxy.set_and_wait(
2128            fidl::BootInstant::from_nanos(DEADLINE_NANOS),
2129            fta::SetMode::KeepAlive(fake_wake_lease()),
2130            "Hello1".into(),
2131        );
2132        let mut set_task_2 = ctx.wake_proxy.set_and_wait(
2133            fidl::BootInstant::from_nanos(DEADLINE_NANOS),
2134            fta::SetMode::KeepAlive(fake_wake_lease()),
2135            "Hello2".into(),
2136        );
2137
2138        assert_matches!(TestExecutor::poll_until_stalled(&mut set_task_1).await, Poll::Pending);
2139        assert_matches!(TestExecutor::poll_until_stalled(&mut set_task_2).await, Poll::Pending);
2140
2141        TestExecutor::advance_to(fasync::MonotonicInstant::from_nanos(DEADLINE_NANOS)).await;
2142
2143        assert_matches!(
2144            TestExecutor::poll_until_stalled(&mut set_task_1).await,
2145            Poll::Ready(Ok(Ok(_)))
2146        );
2147        assert_matches!(
2148            TestExecutor::poll_until_stalled(&mut set_task_2).await,
2149            Poll::Ready(Ok(Ok(_)))
2150        );
2151    }
2152
2153    #[fuchsia::test(allow_stalls = false)]
2154    async fn test_two_alarms_same_notify() {
2155        const DEADLINE_NANOS: i64 = 100;
2156        const ALARM_ID_1: &str = "Hello1";
2157        const ALARM_ID_2: &str = "Hello2";
2158
2159        let ctx = TestContext::new().await;
2160
2161        let schedule = async |deadline_nanos: i64, alarm_id: &str| {
2162            let (notifier_client, notifier_stream) =
2163                fidl::endpoints::create_request_stream::<fta::NotifierMarker>();
2164            assert_matches!(
2165                ctx.wake_proxy
2166                    .set(
2167                        notifier_client,
2168                        fidl::BootInstant::from_nanos(deadline_nanos),
2169                        fta::SetMode::KeepAlive(fake_wake_lease()),
2170                        alarm_id,
2171                    )
2172                    .await,
2173                Ok(Ok(()))
2174            );
2175            notifier_stream
2176        };
2177
2178        let mut notifier_1 = schedule(DEADLINE_NANOS, ALARM_ID_1).await;
2179        let mut notifier_2 = schedule(DEADLINE_NANOS, ALARM_ID_2).await;
2180
2181        let mut next_task_1 = notifier_1.next();
2182        let mut next_task_2 = notifier_2.next();
2183
2184        assert_matches!(TestExecutor::poll_until_stalled(&mut next_task_1).await, Poll::Pending);
2185        assert_matches!(TestExecutor::poll_until_stalled(&mut next_task_2).await, Poll::Pending);
2186
2187        TestExecutor::advance_to(fasync::MonotonicInstant::from_nanos(DEADLINE_NANOS)).await;
2188
2189        assert_matches!(
2190            TestExecutor::poll_until_stalled(&mut next_task_1).await,
2191            Poll::Ready(Some(Ok(fta::NotifierRequest::Notify { alarm_id, .. }))) if alarm_id == ALARM_ID_1
2192        );
2193        assert_matches!(
2194            TestExecutor::poll_until_stalled(&mut next_task_2).await,
2195            Poll::Ready(Some(Ok(fta::NotifierRequest::Notify { alarm_id, .. }))) if alarm_id == ALARM_ID_2
2196        );
2197
2198        assert_matches!(
2199            TestExecutor::poll_until_stalled(notifier_1.next()).await,
2200            Poll::Ready(None)
2201        );
2202        assert_matches!(
2203            TestExecutor::poll_until_stalled(notifier_2.next()).await,
2204            Poll::Ready(None)
2205        );
2206    }
2207
2208    #[test_case(100, 200 ; "push out")]
2209    #[test_case(200, 100 ; "pull in")]
2210    #[fuchsia::test(allow_stalls = false)]
2211    async fn test_two_alarms_different(
2212        // One timer scheduled at this instant (fake time starts from zero).
2213        first_deadline_nanos: i64,
2214        // Another timer scheduled at this instant.
2215        second_deadline_nanos: i64,
2216    ) {
2217        let ctx = TestContext::new().await;
2218
2219        let mut set_task_1 = ctx.wake_proxy.set_and_wait(
2220            fidl::BootInstant::from_nanos(first_deadline_nanos),
2221            fta::SetMode::KeepAlive(fake_wake_lease()),
2222            "Hello1".into(),
2223        );
2224        let mut set_task_2 = ctx.wake_proxy.set_and_wait(
2225            fidl::BootInstant::from_nanos(second_deadline_nanos),
2226            fta::SetMode::KeepAlive(fake_wake_lease()),
2227            "Hello2".into(),
2228        );
2229
2230        assert_matches!(TestExecutor::poll_until_stalled(&mut set_task_1).await, Poll::Pending);
2231        assert_matches!(TestExecutor::poll_until_stalled(&mut set_task_2).await, Poll::Pending);
2232
2233        // Sort alarms by their deadlines.
2234        let mut tasks = [(first_deadline_nanos, set_task_1), (second_deadline_nanos, set_task_2)];
2235        tasks.sort_by(|a, b| a.0.cmp(&b.0));
2236        let [mut first_task, mut second_task] = tasks;
2237
2238        // Alarms should fire in order of deadlines.
2239        TestExecutor::advance_to(fasync::MonotonicInstant::from_nanos(first_task.0)).await;
2240        assert_matches!(
2241            TestExecutor::poll_until_stalled(&mut first_task.1).await,
2242            Poll::Ready(Ok(Ok(_)))
2243        );
2244        assert_matches!(TestExecutor::poll_until_stalled(&mut second_task.1).await, Poll::Pending);
2245
2246        TestExecutor::advance_to(fasync::MonotonicInstant::from_nanos(second_task.0)).await;
2247        assert_matches!(
2248            TestExecutor::poll_until_stalled(&mut second_task.1).await,
2249            Poll::Ready(Ok(Ok(_)))
2250        );
2251    }
2252
2253    #[test_case(100, 200 ; "push out")]
2254    #[test_case(200, 100 ; "pull in")]
2255    #[fuchsia::test(allow_stalls = false)]
2256    async fn test_two_alarms_different_notify(
2257        // One timer scheduled at this instant (fake time starts from zero).
2258        first_deadline_nanos: i64,
2259        // Another timer scheduled at this instant.
2260        second_deadline_nanos: i64,
2261    ) {
2262        const ALARM_ID_1: &str = "Hello1";
2263        const ALARM_ID_2: &str = "Hello2";
2264
2265        let ctx = TestContext::new().await;
2266
2267        let schedule = async |deadline_nanos: i64, alarm_id: &str| {
2268            let (notifier_client, notifier_stream) =
2269                fidl::endpoints::create_request_stream::<fta::NotifierMarker>();
2270            assert_matches!(
2271                ctx.wake_proxy
2272                    .set(
2273                        notifier_client,
2274                        fidl::BootInstant::from_nanos(deadline_nanos),
2275                        fta::SetMode::KeepAlive(fake_wake_lease()),
2276                        alarm_id,
2277                    )
2278                    .await,
2279                Ok(Ok(()))
2280            );
2281            notifier_stream
2282        };
2283
2284        // Sort alarms by their deadlines.
2285        let mut notifier_all = futures::stream::select_all([
2286            schedule(first_deadline_nanos, ALARM_ID_1).await,
2287            schedule(second_deadline_nanos, ALARM_ID_2).await,
2288        ]);
2289        let [(early_ns, early_alarm), (later_ns, later_alarm)] = {
2290            let mut tasks =
2291                [(first_deadline_nanos, ALARM_ID_1), (second_deadline_nanos, ALARM_ID_2)];
2292            tasks.sort_by(|a, b| a.0.cmp(&b.0));
2293            tasks
2294        };
2295
2296        // Alarms should fire in order of deadlines.
2297        let mut next_task = notifier_all.next();
2298        assert_matches!(TestExecutor::poll_until_stalled(&mut next_task).await, Poll::Pending);
2299
2300        TestExecutor::advance_to(fasync::MonotonicInstant::from_nanos(early_ns)).await;
2301        assert_matches!(
2302            TestExecutor::poll_until_stalled(next_task).await,
2303            Poll::Ready(Some(Ok(fta::NotifierRequest::Notify { alarm_id, .. }))) if alarm_id == early_alarm
2304        );
2305
2306        let mut next_task = notifier_all.next();
2307        assert_matches!(TestExecutor::poll_until_stalled(&mut next_task).await, Poll::Pending);
2308
2309        TestExecutor::advance_to(fasync::MonotonicInstant::from_nanos(later_ns)).await;
2310        assert_matches!(
2311            TestExecutor::poll_until_stalled(next_task).await,
2312            Poll::Ready(Some(Ok(fta::NotifierRequest::Notify { alarm_id, .. }))) if alarm_id == later_alarm
2313        );
2314        assert_matches!(
2315            TestExecutor::poll_until_stalled(notifier_all.next()).await,
2316            Poll::Ready(None)
2317        );
2318    }
2319
2320    #[fuchsia::test(allow_stalls = false)]
2321    async fn test_alarm_immediate() {
2322        let ctx = TestContext::new().await;
2323        let mut set_task = ctx.wake_proxy.set_and_wait(
2324            fidl::BootInstant::INFINITE_PAST,
2325            fta::SetMode::KeepAlive(fake_wake_lease()),
2326            "Hello1".into(),
2327        );
2328        assert_matches!(
2329            TestExecutor::poll_until_stalled(&mut set_task).await,
2330            Poll::Ready(Ok(Ok(_)))
2331        );
2332    }
2333
2334    #[fuchsia::test(allow_stalls = false)]
2335    async fn test_alarm_immediate_notify() {
2336        const ALARM_ID: &str = "Hello";
2337        let ctx = TestContext::new().await;
2338
2339        let (notifier_client, mut notifier_stream) =
2340            fidl::endpoints::create_request_stream::<fta::NotifierMarker>();
2341
2342        let mut set_task = ctx.wake_proxy.set(
2343            notifier_client,
2344            fidl::BootInstant::INFINITE_PAST,
2345            fta::SetMode::KeepAlive(fake_wake_lease()),
2346            ALARM_ID,
2347        );
2348        assert_matches!(
2349            TestExecutor::poll_until_stalled(&mut set_task).await,
2350            Poll::Ready(Ok(Ok(_)))
2351        );
2352        assert_matches!(
2353            TestExecutor::poll_until_stalled(notifier_stream.next()).await,
2354            Poll::Ready(Some(Ok(fta::NotifierRequest::Notify { alarm_id, .. }))) if alarm_id == ALARM_ID
2355        );
2356    }
2357
2358    // Rescheduling a timer will cancel the earlier call and use the new
2359    // deadline for the later call.
2360    #[test_case(200, 100 ; "pull in")]
2361    #[test_case(100, 200 ; "push out")]
2362    #[test_case(100, 100 ; "replace with the same deadline")]
2363    #[fuchsia::test(allow_stalls = false)]
2364    async fn test_reschedule(initial_deadline_nanos: i64, override_deadline_nanos: i64) {
2365        const ALARM_ID: &str = "Hello";
2366
2367        let ctx = TestContext::new().await;
2368
2369        let schedule = |deadline_nanos: i64| {
2370            let setup_done = zx::Event::create();
2371            let task = ctx.wake_proxy.set_and_wait(
2372                fidl::BootInstant::from_nanos(deadline_nanos),
2373                fta::SetMode::NotifySetupDone(
2374                    setup_done.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap(),
2375                ),
2376                ALARM_ID.into(),
2377            );
2378            (task, setup_done)
2379        };
2380
2381        // Schedule timer with a long timeout first. Let it wait, then
2382        // try to reschedule the same timer
2383        let (mut set_task_1, setup_done_1) = schedule(initial_deadline_nanos);
2384        fasync::OnSignals::new(setup_done_1, zx::Signals::EVENT_SIGNALED).await.unwrap();
2385        assert_matches!(TestExecutor::poll_until_stalled(&mut set_task_1).await, Poll::Pending);
2386
2387        // Schedule the same timer as above, but with a shorter deadline. This
2388        // should cancel the earlier call.
2389        let (mut set_task_2, setup_done_2) = schedule(override_deadline_nanos);
2390        fasync::OnSignals::new(setup_done_2, zx::Signals::EVENT_SIGNALED).await.unwrap();
2391        assert_matches!(
2392            TestExecutor::poll_until_stalled(&mut set_task_1).await,
2393            Poll::Ready(Ok(Err(fta::WakeAlarmsError::Dropped)))
2394        );
2395        assert_matches!(TestExecutor::poll_until_stalled(&mut set_task_2).await, Poll::Pending);
2396
2397        // The later call will be fired exactly on the new shorter deadline.
2398        TestExecutor::advance_to(fasync::MonotonicInstant::from_nanos(override_deadline_nanos - 1))
2399            .await;
2400        assert_matches!(TestExecutor::poll_until_stalled(&mut set_task_2).await, Poll::Pending);
2401        TestExecutor::advance_to(fasync::MonotonicInstant::from_nanos(override_deadline_nanos))
2402            .await;
2403        assert_matches!(
2404            TestExecutor::poll_until_stalled(&mut set_task_2).await,
2405            Poll::Ready(Ok(Ok(_)))
2406        );
2407
2408        // The values in the inspector tree are fixed because the test
2409        // runs fully deterministically in fake time.
2410        assert_data_tree!(finspect::component::inspector(), root: {
2411            test: {
2412                hardware: {
2413                    // All alarms fired, so this should be "none".
2414                    current_deadline: "(none)",
2415                    remaining_until_alarm: "(none)",
2416                },
2417                now_formatted: format!("{override_deadline_nanos}ns ({override_deadline_nanos})"),
2418                now_ns: override_deadline_nanos,
2419                pending_timers: "Boot:\n\t\n\tUTC:\n\t",
2420                pending_timers_count: 0u64,
2421                requested_deadlines_ns: AnyProperty,
2422                schedule_delay_ns: AnyProperty,
2423                slack_ns: AnyProperty,
2424                boot_deadlines_count: AnyProperty,
2425                utc_deadlines_count: AnyProperty,
2426            },
2427        });
2428    }
2429
2430    // Rescheduling a timer will send an error on the old notifier and use the
2431    // new notifier for the new deadline.
2432    #[fuchsia::test(allow_stalls = false)]
2433    async fn test_reschedule_notify() {
2434        const ALARM_ID: &str = "Hello";
2435        const INITIAL_DEADLINE_NANOS: i64 = 100;
2436        const OVERRIDE_DEADLINE_NANOS: i64 = 200;
2437
2438        let ctx = TestContext::new().await;
2439
2440        let schedule = async |deadline_nanos: i64| {
2441            let (notifier_client, notifier_stream) =
2442                fidl::endpoints::create_request_stream::<fta::NotifierMarker>();
2443            assert_matches!(
2444                ctx.wake_proxy
2445                    .set(
2446                        notifier_client,
2447                        fidl::BootInstant::from_nanos(deadline_nanos),
2448                        fta::SetMode::KeepAlive(fake_wake_lease()),
2449                        ALARM_ID.into(),
2450                    )
2451                    .await,
2452                Ok(Ok(()))
2453            );
2454            notifier_stream
2455        };
2456
2457        let mut notifier_1 = schedule(INITIAL_DEADLINE_NANOS).await;
2458        let mut next_task_1 = notifier_1.next();
2459        assert_matches!(TestExecutor::poll_until_stalled(&mut next_task_1).await, Poll::Pending);
2460
2461        let mut notifier_2 = schedule(OVERRIDE_DEADLINE_NANOS).await;
2462        let mut next_task_2 = notifier_2.next();
2463        assert_matches!(TestExecutor::poll_until_stalled(&mut next_task_2).await, Poll::Pending);
2464
2465        // First notifier is called with an error then closed.
2466        assert_matches!(
2467            TestExecutor::poll_until_stalled(&mut next_task_1).await,
2468            Poll::Ready(Some(Ok(fta::NotifierRequest::NotifyError { alarm_id, error, .. }))) if alarm_id == ALARM_ID && error == fta::WakeAlarmsError::Dropped
2469        );
2470        assert_matches!(
2471            TestExecutor::poll_until_stalled(notifier_1.next()).await,
2472            Poll::Ready(None)
2473        );
2474
2475        // Second notifier is called upon the new deadline then closed.
2476        TestExecutor::advance_to(fasync::MonotonicInstant::from_nanos(OVERRIDE_DEADLINE_NANOS))
2477            .await;
2478        assert_matches!(
2479            TestExecutor::poll_until_stalled(next_task_2).await,
2480            Poll::Ready(Some(Ok(fta::NotifierRequest::Notify { alarm_id, .. }))) if alarm_id == ALARM_ID
2481        );
2482        assert_matches!(
2483            TestExecutor::poll_until_stalled(notifier_2.next()).await,
2484            Poll::Ready(None)
2485        );
2486    }
2487
2488    // If we get two scheduling FIDL errors one after another, the wake alarm
2489    // manager must not lock up.
2490    #[fuchsia::test(allow_stalls = false)]
2491    async fn test_fidl_error_on_reschedule() {
2492        const DEADLINE_NANOS: i64 = 100;
2493
2494        let (wake_proxy, _stream) =
2495            fidl::endpoints::create_proxy_and_stream::<fta::WakeAlarmsMarker>();
2496        drop(_stream);
2497
2498        assert_matches!(
2499            wake_proxy
2500                .set_and_wait(
2501                    zx::BootInstant::from_nanos(DEADLINE_NANOS).into(),
2502                    fta::SetMode::KeepAlive(fake_wake_lease()),
2503                    "hello1".into(),
2504                )
2505                .await,
2506            Err(fidl::Error::ClientChannelClosed { .. })
2507        );
2508
2509        assert_matches!(
2510            wake_proxy
2511                .set_and_wait(
2512                    zx::BootInstant::from_nanos(DEADLINE_NANOS).into(),
2513                    fta::SetMode::KeepAlive(fake_wake_lease()),
2514                    "hello2".into(),
2515                )
2516                .await,
2517            Err(fidl::Error::ClientChannelClosed { .. })
2518        );
2519    }
2520
2521    // Verify that if a UTC timer is scheduled in the future on the UTC timeline, then the
2522    // UTC clock is changed to move "now" beyond the timer's deadline, the timer fires.
2523    #[fuchsia::test(allow_stalls = false)]
2524    async fn test_set_and_wait_utc() {
2525        const ALARM_ID: &str = "Hello_set_and_wait_utc";
2526        let ctx = TestContext::new().await;
2527
2528        let now_boot = fasync::BootInstant::now();
2529        ctx.utc_clock
2530            .update(
2531                zx::ClockUpdate::builder()
2532                    .absolute_value(now_boot.into(), ctx.utc_backstop)
2533                    .build(),
2534            )
2535            .unwrap();
2536
2537        let timestamp_utc = ctx.utc_backstop + fxr::UtcDuration::from_nanos(2);
2538        let mut wake_fut = ctx.wake_proxy.set_and_wait_utc(
2539            &fta::InstantUtc { timestamp_utc: timestamp_utc.into_nanos() },
2540            fta::SetMode::KeepAlive(fake_wake_lease()),
2541            ALARM_ID,
2542        );
2543
2544        // Timer is not expired yet.
2545        assert_matches!(TestExecutor::poll_until_stalled(&mut wake_fut).await, Poll::Pending);
2546
2547        // Move the UTC timeline.
2548        ctx.utc_clock
2549            .update(
2550                zx::ClockUpdate::builder()
2551                    .absolute_value(
2552                        now_boot.into(),
2553                        ctx.utc_backstop + fxr::UtcDuration::from_nanos(100),
2554                    )
2555                    .build(),
2556            )
2557            .unwrap();
2558
2559        // See similar code in the test above.
2560        TestExecutor::advance_to(fasync::MonotonicInstant::from_nanos(1)).await;
2561        assert_matches!(TestExecutor::poll_until_stalled(wake_fut).await, Poll::Ready(_));
2562    }
2563}