1mod 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
58static MAX_USEFUL_TICKS: LazyLock<u64> = LazyLock::new(|| *I32_MAX_AS_U64);
60
61static MIN_USEFUL_TICKS: u64 = 1;
65
66const MAIN_TIMER_ID: usize = 6;
69
70const LONG_DELAY_NANOS: i64 = 2000 * MSEC_IN_NANOS;
72
73macro_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
96fn 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#[derive(Debug)]
111pub(crate) enum TimerOpsError {
112 Driver(ffhh::DriverError),
114 Fidl(fidl::Error),
116}
117
118trait SawResponseFut: std::future::Future<Output = Result<zx::EventPair, TimerOpsError>> {
119 }
121
122#[async_trait(?Send)]
124pub(crate) trait TimerOps {
125 async fn stop(&self, id: u64);
127
128 async fn get_timer_properties(&self) -> TimerConfig;
131
132 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
145struct 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 let timer_index = if timers_properties.len() > MAIN_TIMER_ID {
177 MAIN_TIMER_ID
180 } else if timers_properties.len() > 0 {
181 0
185 } else {
186 return TimerConfig::new_empty();
188 };
189 let main_timer_properties = &timers_properties[timer_index];
190 debug!("alarms: main_timer_properties: {:?}", main_timer_properties);
191 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() .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() .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
236struct 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
269async 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
277const CHANNEL_SIZE: usize = 100;
279
280#[derive(Debug)]
282enum Cmd {
283 Start {
285 conn_id: zx::Koid,
287 deadline: timers::Deadline,
289 mode: Option<fta::SetMode>,
298 alarm_id: String,
300 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 timer_config_id: u64,
327 resolution_nanos: i64,
328 ticks: u64,
329 },
330 UtcUpdated {
332 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
376pub 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
397pub 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 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 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
440async 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 let responder = Rc::new(RefCell::new(Some(responder)));
464
465 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 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 let deadline =
493 timers::Deadline::Utc(fxr::UtcInstant::from_nanos(deadline.timestamp_utc));
494
495 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 log_long_op!(handle_cancel(alarm_id, conn_id, &mut cmd));
522 }
523 fta::WakeAlarmsRequest::Set { notifier, deadline, mode, alarm_id, responder } => {
524 debug!(
526 "handle_request: scheduling alarm_id: \"{alarm_id}\"\n\tconn_id: {conn_id:?}\n\tdeadline: {}",
527 format_timer(deadline.into())
528 );
529 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 responder.send(Ok(())).unwrap();
542 }
543 }
544 fta::WakeAlarmsRequest::_UnknownMethod { .. } => {}
545 };
546}
547
548pub struct Loop {
555 snd: mpsc::Sender<Cmd>,
558}
559
560impl Loop {
561 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 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 fn get_sender(&self) -> mpsc::Sender<Cmd> {
635 self.snd.clone()
636 }
637}
638
639async 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 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 log::warn!("monitor_utc_clock_changes: exit: {err:?}");
656 break;
657 }
658 }
659}
660
661pub 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#[derive(Debug, Clone, Copy)]
692struct TimerDuration {
693 resolution: zx::BootDuration,
695 ticks: u64,
698}
699
700impl 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 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 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
737 let ticks = self.ticks;
738 let resolution = self.resolution();
739 write!(f, "{}x{}", ticks, format_duration(resolution),)
741 }
742}
743
744impl TimerDuration {
745 fn max() -> Self {
747 TimerDuration::new(zx::BootDuration::from_nanos(1), *I64_MAX_AS_U64)
748 }
749
750 fn zero() -> Self {
752 TimerDuration::new(zx::BootDuration::from_nanos(1), 0)
753 }
754
755 fn new(resolution: zx::BootDuration, ticks: u64) -> Self {
757 Self { resolution, ticks }
758 }
759
760 fn new_with_resolution(res_source: &TimerDuration, ticks: u64) -> Self {
763 Self::new(res_source.resolution, ticks)
764 }
765
766 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 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 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#[derive(Debug)]
818pub(crate) struct TimerConfig {
819 resolutions: Vec<zx::BootDuration>,
827 max_ticks: u64,
832 id: u64,
834}
835
836impl TimerConfig {
837 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 fn pick_setting(&self, duration: zx::BootDuration) -> TimerDuration {
865 assert!(self.resolutions.len() > 0, "there must be at least one supported resolution");
866
867 if duration <= zx::BootDuration::ZERO {
870 return TimerDuration::new(self.resolutions[0], 1);
871 }
872
873 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 if !smallest_slack_larger_than_duration && !largest_slack_smaller_than_duration {
911 let q = duration_slack / smallest_unit;
914 let d = smallest_unit * q;
915 if d == duration_slack {
916 return d;
918 } else {
919 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 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
967struct TimerState {
969 task: fasync::Task<()>,
971 deadline: fasync::BootInstant,
973}
974
975async 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 #[allow(clippy::collection_is_never_read)]
999 let mut hrtimer_status: Option<TimerState> = None;
1000
1001 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 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 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 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 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 if let Some(mode) = mode {
1072 if let fta::SetMode::NotifySetupDone(setup_done) = mode {
1073 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 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 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 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 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 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 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 log_long_op!(task);
1204 }
1205 } else {
1206 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 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 debug!("wake_timer_loop: no expired alarms, reset hrtimer state");
1232 log_long_op!(stop_hrtimer(&timer_proxy, &timer_config));
1233 }
1234 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 warn!(
1254 "wake_timer_loop: FIDL error: {:?}, deadline: {}, now: {}",
1255 error,
1256 format_timer(expired_deadline.into()),
1257 format_timer(now.into()),
1258 );
1259 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, 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 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 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 *utc_transform.borrow_mut() = transform;
1343
1344 if hrtimer_status.is_some() {
1347 log_long_op!(stop_hrtimer(&timer_proxy, &timer_config));
1348 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 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(¤t_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
1407async 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 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 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 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 }
1477 Err(TimerOpsError::Driver(e)) => {
1478 defer! {
1479 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 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 }
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 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 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 debug!("schedule_hrtimer: hrtimer wake alarm has been scheduled.");
1529 TimerState { task: hrtimer_task, deadline }
1530}
1531
1532fn 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 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 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 }
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 }
1596
1597const HRTIMER_DIRECTORY: &str = "/dev/class/hrtimer";
1600
1601pub 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 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, 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, 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 assert_slack_eq(expected, actual);
1814 }
1815
1816 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 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 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 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 },
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 signal(&setup_event);
1945
1946 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 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 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 first_deadline_nanos: i64,
2214 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 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 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 first_deadline_nanos: i64,
2259 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 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 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 #[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 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 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 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 assert_data_tree!(finspect::component::inspector(), root: {
2411 test: {
2412 hardware: {
2413 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 #[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 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 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 #[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 #[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 assert_matches!(TestExecutor::poll_until_stalled(&mut wake_fut).await, Poll::Pending);
2546
2547 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 TestExecutor::advance_to(fasync::MonotonicInstant::from_nanos(1)).await;
2561 assert_matches!(TestExecutor::poll_until_stalled(wake_fut).await, Poll::Ready(_));
2562 }
2563}