1use anyhow::Context;
6use chrono::{Datelike, TimeZone, Timelike};
7use fidl::endpoints::ServerEnd;
8use fidl_fuchsia_hardware_rtc::{DeviceRequest, DeviceRequestStream};
9use fidl_fuchsia_metrics::MetricEvent;
10use fidl_fuchsia_metrics_test::{LogMethod, MetricEventLoggerQuerierProxy};
11use fidl_fuchsia_testing::{FakeClockControlProxy, FakeClockProxy};
12use fidl_fuchsia_time::{MaintenanceRequest, MaintenanceRequestStream};
13use fidl_fuchsia_time_external::{PushSourceMarker, Status, TimeSample};
14use fidl_test_time::{TimeSourceControlRequest, TimeSourceControlRequestStream};
15use fuchsia_component::server::ServiceFs;
16use fuchsia_component_test::{
17 Capability, ChildOptions, ChildRef, LocalComponentHandles, RealmBuilder, RealmInstance, Ref,
18 Route,
19};
20use fuchsia_sync::Mutex;
21use futures::channel::mpsc::Sender;
22use futures::stream::{Stream, StreamExt, TryStreamExt};
23use futures::{Future, FutureExt, SinkExt};
24use lazy_static::lazy_static;
25use push_source::{PushSource, TestUpdateAlgorithm, Update};
26use std::ops::Deref;
27use std::sync::Arc;
28use time_metrics_registry::PROJECT_ID;
29use vfs::pseudo_directory;
30use zx::{self as zx, HandleBased, Rights};
31use {fidl_fuchsia_io as fio, fuchsia_async as fasync};
32
33const TIMEKEEPER_URL: &str = "#meta/timekeeper_for_integration.cm";
35const TIMEKEEPER_FAKE_TIME_URL: &str = "#meta/timekeeper_with_fake_time.cm";
37const COBALT_URL: &str = "#meta/fake_cobalt.cm";
39const FAKE_CLOCK_URL: &str = "#meta/fake_clock.cm";
41
42pub struct NestedTimekeeper {
45 _realm_instance: RealmInstance,
46}
47
48impl Into<RealmInstance> for NestedTimekeeper {
49 fn into(self) -> RealmInstance {
51 self._realm_instance
52 }
53}
54
55impl NestedTimekeeper {
56 pub async fn new(
69 clock: Arc<zx::Clock>,
70 rtc_options: RtcOptions,
71 use_fake_clock: bool,
72 ) -> (
73 Self,
74 Arc<PushSourcePuppet>,
75 RtcUpdates,
76 MetricEventLoggerQuerierProxy,
77 Option<FakeClockController>,
78 ) {
79 let push_source_puppet = Arc::new(PushSourcePuppet::new());
80
81 let builder = RealmBuilder::new().await.unwrap();
82 let fake_cobalt =
83 builder.add_child("fake_cobalt", COBALT_URL, ChildOptions::new()).await.unwrap();
84
85 let timekeeper_url = if use_fake_clock { TIMEKEEPER_FAKE_TIME_URL } else { TIMEKEEPER_URL };
86 log::trace!("using timekeeper_url: {}", timekeeper_url);
87 let timekeeper = builder
88 .add_child("timekeeper_test", timekeeper_url, ChildOptions::new().eager())
89 .await
90 .with_context(|| format!("while starting up timekeeper_test from: {timekeeper_url}"))
91 .unwrap();
92
93 let timesource_server = builder
94 .add_local_child(
95 "timesource_mock",
96 {
97 let push_source_puppet = Arc::clone(&push_source_puppet);
98 move |handles: LocalComponentHandles| {
99 Box::pin(timesource_mock_server(handles, Arc::clone(&push_source_puppet)))
100 }
101 },
102 ChildOptions::new(),
103 )
104 .await
105 .context("while starting up timesource_mock")
106 .unwrap();
107
108 let maintenance_server = builder
109 .add_local_child(
110 "maintenance_mock",
111 move |handles: LocalComponentHandles| {
112 Box::pin(maintenance_mock_server(handles, Arc::clone(&clock)))
113 },
114 ChildOptions::new(),
115 )
116 .await
117 .context("while starting up maintenance_mock")
118 .unwrap();
119
120 if use_fake_clock {
122 let fake_clock =
123 builder.add_child("fake_clock", FAKE_CLOCK_URL, ChildOptions::new()).await.unwrap();
124
125 builder
126 .add_route(
127 Route::new()
128 .capability(Capability::protocol_by_name(
129 "fuchsia.testing.FakeClockControl",
130 ))
131 .from(&fake_clock)
132 .to(Ref::parent()),
133 )
134 .await
135 .context("while setting up FakeClockControl")
136 .unwrap();
137
138 builder
139 .add_route(
140 Route::new()
141 .capability(Capability::protocol_by_name("fuchsia.testing.FakeClock"))
142 .from(&fake_clock)
143 .to(Ref::parent())
144 .to(&timekeeper),
145 )
146 .await
147 .context("while setting up FakeClock")
148 .unwrap();
149
150 builder
151 .add_route(
152 Route::new()
153 .capability(Capability::protocol_by_name("fuchsia.logger.LogSink"))
154 .from(Ref::parent())
155 .to(&fake_clock),
156 )
157 .await
158 .context("while setting up LogSink")
159 .unwrap();
160 };
161
162 builder
163 .add_route(
164 Route::new()
165 .capability(Capability::protocol_by_name("fuchsia.time.Maintenance"))
166 .from(&maintenance_server)
167 .to(&timekeeper),
168 )
169 .await
170 .context("while setting up Maintenance")
171 .unwrap();
172
173 builder
174 .add_route(
175 Route::new()
176 .capability(Capability::protocol_by_name("test.time.TimeSourceControl"))
177 .from(×ource_server)
178 .to(&timekeeper),
179 )
180 .await
181 .unwrap();
182
183 builder
184 .add_route(
185 Route::new()
186 .capability(Capability::protocol_by_name(
187 "fuchsia.metrics.test.MetricEventLoggerQuerier",
188 ))
189 .from(&fake_cobalt)
190 .to(Ref::parent()),
191 )
192 .await
193 .unwrap();
194
195 builder
196 .add_route(
197 Route::new()
198 .capability(Capability::protocol_by_name(
199 "fuchsia.metrics.MetricEventLoggerFactory",
200 ))
201 .from(&fake_cobalt)
202 .to(&timekeeper),
203 )
204 .await
205 .unwrap();
206
207 builder
208 .add_route(
209 Route::new()
210 .capability(Capability::protocol_by_name("fuchsia.logger.LogSink"))
211 .from(Ref::parent())
212 .to(&fake_cobalt)
213 .to(&timekeeper)
214 .to(×ource_server)
215 .to(&maintenance_server),
216 )
217 .await
218 .unwrap();
219
220 builder
221 .add_route(
222 Route::new()
223 .capability(Capability::configuration("fuchsia.time.config.WritableUTCTime"))
224 .from(Ref::parent())
225 .to(&timekeeper),
226 )
227 .await
228 .unwrap();
229
230 let rtc_updates = setup_rtc(rtc_options, &builder, &timekeeper).await;
231 let realm_instance = builder.build().await.unwrap();
232
233 let fake_clock_control = if use_fake_clock {
234 let control_proxy = realm_instance.root.connect_to_protocol_at_exposed_dir().unwrap();
235 let clock_proxy = realm_instance.root.connect_to_protocol_at_exposed_dir().unwrap();
236 Some(FakeClockController { control_proxy, clock_proxy })
237 } else {
238 None
239 };
240
241 let cobalt_querier = realm_instance
242 .root
243 .connect_to_protocol_at_exposed_dir()
244 .expect("the connection succeeds");
245
246 let nested_timekeeper = Self { _realm_instance: realm_instance };
247
248 (nested_timekeeper, push_source_puppet, rtc_updates, cobalt_querier, fake_clock_control)
249 }
250}
251
252pub struct RemotePushSourcePuppet {
253 proxy: fidl_test_time_realm::PushSourcePuppetProxy,
254}
255
256impl RemotePushSourcePuppet {
257 pub fn new(proxy: fidl_test_time_realm::PushSourcePuppetProxy) -> Arc<Self> {
259 Arc::new(Self { proxy })
260 }
261
262 pub async fn set_sample(&self, sample: TimeSample) {
264 self.proxy.set_sample(&sample).await.expect("original API was infallible");
265 }
266
267 pub async fn set_status(&self, status: Status) {
269 self.proxy.set_status(status).await.expect("original API was infallible");
270 }
271
272 pub async fn simulate_crash(&self) {
274 self.proxy.crash().await.expect("original local API was infallible");
275 }
276
277 pub async fn lifetime_served_connections(&self) -> u32 {
280 self.proxy.get_lifetime_served_connections().await.expect("original API was infallible")
281 }
282}
283
284pub struct PushSourcePuppet {
286 inner: Mutex<PushSourcePuppetInner>,
289 cumulative_clients: Mutex<u32>,
291}
292
293impl PushSourcePuppet {
294 fn new() -> Self {
296 Self { inner: Mutex::new(PushSourcePuppetInner::new()), cumulative_clients: Mutex::new(0) }
297 }
298
299 fn serve_client(&self, server_end: ServerEnd<PushSourceMarker>) {
301 log::debug!("serve_client entry");
302 let mut inner = self.inner.lock();
303 if inner.served_client() {
307 *inner = PushSourcePuppetInner::new();
308 }
309 inner.serve_client(server_end);
310 *self.cumulative_clients.lock() += 1;
311 }
312
313 pub async fn set_sample(&self, sample: TimeSample) {
315 let mut sink = self.inner.lock().get_sink();
316 sink.send(sample.into()).await.unwrap();
317 }
318
319 pub async fn set_status(&self, status: Status) {
321 let mut sink = self.inner.lock().get_sink();
322 sink.send(status.into()).await.unwrap();
323 }
324
325 pub fn simulate_crash(&self) {
327 *self.inner.lock() = PushSourcePuppetInner::new();
328 }
330
331 pub fn lifetime_served_connections(&self) -> u32 {
334 *self.cumulative_clients.lock()
335 }
336}
337
338struct PushSourcePuppetInner {
341 push_source: Arc<PushSource<TestUpdateAlgorithm>>,
342 tasks: Vec<fasync::Task<()>>,
344 update_sink: Sender<Update>,
346}
347
348impl PushSourcePuppetInner {
349 fn new() -> Self {
350 let (update_algorithm, update_sink) = TestUpdateAlgorithm::new();
351 let push_source = Arc::new(PushSource::new(update_algorithm, Status::Ok).unwrap());
352 let push_source_clone = Arc::clone(&push_source);
353 let tasks = vec![fasync::Task::spawn(async move {
354 push_source_clone.poll_updates().await.unwrap();
355 })];
356 Self { push_source, tasks, update_sink }
357 }
358
359 fn served_client(&self) -> bool {
361 self.tasks.len() > 1
362 }
363
364 fn serve_client(&mut self, server_end: ServerEnd<PushSourceMarker>) {
366 let push_source_clone = Arc::clone(&self.push_source);
367 self.tasks.push(fasync::Task::spawn(async move {
368 push_source_clone.handle_requests_for_stream(server_end.into_stream()).await.unwrap();
369 }));
370 }
371
372 fn get_sink(&self) -> Sender<Update> {
377 self.update_sink.clone()
378 }
379}
380
381#[derive(Clone, Debug)]
383pub struct RtcUpdates(Arc<Mutex<Vec<fidl_fuchsia_hardware_rtc::Time>>>);
384
385impl RtcUpdates {
386 pub fn to_vec(&self) -> Vec<fidl_fuchsia_hardware_rtc::Time> {
388 self.0.lock().clone()
389 }
390}
391
392pub struct RemoteRtcUpdates {
395 proxy: fidl_test_time_realm::RtcUpdatesProxy,
396}
397
398impl RemoteRtcUpdates {
399 pub async fn to_vec(&self) -> Vec<fidl_fuchsia_hardware_rtc::Time> {
400 self.proxy
401 .get(fidl_test_time_realm::GetRequest::default())
402 .await
403 .expect("no errors or overflows") .unwrap()
405 .0
406 }
407 pub fn new(proxy: fidl_test_time_realm::RtcUpdatesProxy) -> Self {
408 RemoteRtcUpdates { proxy }
409 }
410}
411
412pub struct FakeClockController {
415 control_proxy: FakeClockControlProxy,
416 clock_proxy: FakeClockProxy,
417}
418
419impl Deref for FakeClockController {
420 type Target = FakeClockControlProxy;
421
422 fn deref(&self) -> &Self::Target {
423 &self.control_proxy
424 }
425}
426
427impl FakeClockController {
428 pub fn new(control_proxy: FakeClockControlProxy, clock_proxy: FakeClockProxy) -> Self {
430 FakeClockController { control_proxy, clock_proxy }
431 }
432
433 pub fn into_components(self) -> (FakeClockControlProxy, FakeClockProxy) {
435 (self.control_proxy, self.clock_proxy)
436 }
437
438 pub async fn get_monotonic(&self) -> Result<i64, fidl::Error> {
439 self.clock_proxy.get().await
440 }
441
442 pub async fn get_reference(&self) -> Result<zx::BootInstant, fidl::Error> {
444 self.get_monotonic().await.map(|v| zx::BootInstant::from_nanos(v))
445 }
446}
447
448pub enum RtcOptions {
450 None,
453 InitialRtcTime(zx::SyntheticInstant),
455 InjectedRtc(fio::DirectoryProxy),
471}
472
473impl From<fidl_test_time_realm::RtcOptions> for RtcOptions {
474 fn from(value: fidl_test_time_realm::RtcOptions) -> Self {
475 match value {
476 fidl_test_time_realm::RtcOptions::DevClassRtc(h) => {
477 RtcOptions::InjectedRtc(h.into_proxy())
478 }
479 fidl_test_time_realm::RtcOptions::InitialRtcTime(t) => {
480 RtcOptions::InitialRtcTime(zx::SyntheticInstant::from_nanos(t))
481 }
482 _ => unimplemented!(),
483 }
484 }
485}
486
487impl From<zx::SyntheticInstant> for RtcOptions {
488 fn from(value: zx::SyntheticInstant) -> Self {
489 RtcOptions::InitialRtcTime(value)
490 }
491}
492
493impl From<Option<zx::SyntheticInstant>> for RtcOptions {
494 fn from(value: Option<zx::SyntheticInstant>) -> Self {
495 value.map(|t| t.into()).unwrap_or(Self::None)
496 }
497}
498
499async fn setup_rtc(
510 rtc_options: RtcOptions,
511 builder: &RealmBuilder,
512 timekeeper: &ChildRef,
513) -> RtcUpdates {
514 let rtc_updates = RtcUpdates(Arc::new(Mutex::new(vec![])));
515
516 let rtc_dir = match rtc_options {
517 RtcOptions::InitialRtcTime(initial_time) => {
518 log::debug!("using fake /dev/class/rtc/000");
519 pseudo_directory! {
520 "class" => pseudo_directory! {
521 "rtc" => pseudo_directory! {
522 "000" => vfs::service::host({
523 let rtc_updates = rtc_updates.clone();
524 move |stream| {
525 serve_fake_rtc(initial_time, rtc_updates.clone(), stream)
526 }
527 })
528 }
529 }
530 }
531 }
532 RtcOptions::None => {
533 log::debug!("using an empty /dev/class/rtc directory");
534 pseudo_directory! {
535 "class" => pseudo_directory! {
536 "rtc" => pseudo_directory! {
537 }
538 }
539 }
540 }
541 RtcOptions::InjectedRtc(h) => {
542 log::debug!("using /dev/class/rtc provided by client");
543 pseudo_directory! {
544 "class" => pseudo_directory! {
545 "rtc" => vfs::remote::remote_dir(h)
546 }
547 }
548 }
549 };
550
551 let fake_rtc_server = builder
552 .add_local_child(
553 "fake_rtc",
554 {
555 move |handles| {
556 let rtc_dir = rtc_dir.clone();
557 async move {
558 let _ = &handles;
559 let mut fs = ServiceFs::new();
560 fs.add_remote("dev", vfs::directory::serve_read_only(rtc_dir));
561 fs.serve_connection(handles.outgoing_dir)
562 .expect("failed to serve fake RTC ServiceFs");
563 fs.collect::<()>().await;
564 Ok(())
565 }
566 .boxed()
567 }
568 },
569 ChildOptions::new().eager(),
570 )
571 .await
572 .unwrap();
573
574 builder
575 .add_route(
576 Route::new()
577 .capability(
578 Capability::directory("dev-rtc").path("/dev/class/rtc").rights(fio::R_STAR_DIR),
579 )
580 .from(&fake_rtc_server)
581 .to(&*timekeeper),
582 )
583 .await
584 .unwrap();
585
586 rtc_updates
587}
588
589async fn serve_fake_rtc(
590 initial_time: zx::SyntheticInstant,
591 rtc_updates: RtcUpdates,
592 mut stream: DeviceRequestStream,
593) {
594 while let Some(req) = stream.try_next().await.unwrap() {
595 match req {
596 DeviceRequest::Get { responder } => {
597 log::debug!("serve_fake_rtc: DeviceRequest::Get");
598 responder.send(Ok(&zx_time_to_rtc_time(initial_time))).unwrap();
601 }
602 DeviceRequest::Set { rtc, responder } => {
603 log::debug!("serve_fake_rtc: DeviceRequest::Set");
604 rtc_updates.0.lock().push(rtc);
605 responder.send(zx::Status::OK.into_raw()).unwrap();
606 }
607 DeviceRequest::Set2 { rtc, responder } => {
608 log::debug!("serve_fake_rtc: DeviceRequest::Set2");
609 rtc_updates.0.lock().push(rtc);
610 responder.send(Ok(())).unwrap();
611 }
612 DeviceRequest::_UnknownMethod { .. } => {}
613 }
614 }
615}
616
617async fn serve_test_control(puppet: &PushSourcePuppet, stream: TimeSourceControlRequestStream) {
618 stream
619 .try_for_each_concurrent(None, |req| async {
620 let _ = &req;
621 let TimeSourceControlRequest::ConnectPushSource { push_source, .. } = req;
622 puppet.serve_client(push_source);
623 Ok(())
624 })
625 .await
626 .unwrap();
627}
628
629async fn serve_maintenance(clock_handle: Arc<zx::Clock>, mut stream: MaintenanceRequestStream) {
630 while let Some(req) = stream.try_next().await.unwrap() {
631 let MaintenanceRequest::GetWritableUtcClock { responder } = req;
632 responder.send(clock_handle.duplicate_handle(Rights::SAME_RIGHTS).unwrap()).unwrap();
633 }
634}
635
636async fn timesource_mock_server(
637 handles: LocalComponentHandles,
638 push_source_puppet: Arc<PushSourcePuppet>,
639) -> Result<(), anyhow::Error> {
640 let mut fs = ServiceFs::new();
641 let mut tasks = vec![];
642
643 fs.dir("svc").add_fidl_service(move |stream: TimeSourceControlRequestStream| {
644 let puppet_clone = Arc::clone(&push_source_puppet);
645
646 tasks.push(fasync::Task::local(async move {
647 serve_test_control(&*puppet_clone, stream).await;
648 }));
649 });
650
651 fs.serve_connection(handles.outgoing_dir)?;
652 fs.collect::<()>().await;
653
654 Ok(())
655}
656
657async fn maintenance_mock_server(
658 handles: LocalComponentHandles,
659 clock: Arc<zx::Clock>,
660) -> Result<(), anyhow::Error> {
661 let mut fs = ServiceFs::new();
662 let mut tasks = vec![];
663
664 fs.dir("svc").add_fidl_service(move |stream: MaintenanceRequestStream| {
665 let clock_clone = Arc::clone(&clock);
666
667 tasks.push(fasync::Task::local(async move {
668 serve_maintenance(clock_clone, stream).await;
669 }));
670 });
671
672 fs.serve_connection(handles.outgoing_dir)?;
673 fs.collect::<()>().await;
674
675 Ok(())
676}
677
678fn from_rfc2822(date: &str) -> zx::SyntheticInstant {
679 zx::SyntheticInstant::from_nanos(
680 chrono::DateTime::parse_from_rfc2822(date).unwrap().timestamp_nanos_opt().unwrap(),
681 )
682}
683
684lazy_static! {
685 pub static ref BACKSTOP_TIME: zx::SyntheticInstant =
686 from_rfc2822("Sun, 20 Sep 2020 01:01:01 GMT");
687 pub static ref VALID_RTC_TIME: zx::SyntheticInstant =
688 from_rfc2822("Sun, 20 Sep 2020 02:02:02 GMT");
689 pub static ref BEFORE_BACKSTOP_TIME: zx::SyntheticInstant =
690 from_rfc2822("Fri, 06 Mar 2020 04:04:04 GMT");
691 pub static ref VALID_TIME: zx::SyntheticInstant = from_rfc2822("Tue, 29 Sep 2020 02:19:01 GMT");
692 pub static ref VALID_TIME_2: zx::SyntheticInstant =
693 from_rfc2822("Wed, 30 Sep 2020 14:59:59 GMT");
694}
695
696pub const BETWEEN_SAMPLES: zx::BootDuration = zx::BootDuration::from_seconds(5);
698
699pub const STD_DEV: zx::BootDuration = zx::BootDuration::from_millis(50);
701
702pub fn new_clock() -> Arc<zx::SyntheticClock> {
705 Arc::new(new_nonshareable_clock())
706}
707
708pub fn new_nonshareable_clock() -> zx::SyntheticClock {
710 zx::SyntheticClock::create(zx::ClockOpts::empty(), Some(*BACKSTOP_TIME)).unwrap()
711}
712
713fn zx_time_to_rtc_time(zx_time: zx::SyntheticInstant) -> fidl_fuchsia_hardware_rtc::Time {
714 let date = chrono::Utc.timestamp_nanos(zx_time.into_nanos());
715 fidl_fuchsia_hardware_rtc::Time {
716 seconds: date.second() as u8,
717 minutes: date.minute() as u8,
718 hours: date.hour() as u8,
719 day: date.day() as u8,
720 month: date.month() as u8,
721 year: date.year() as u16,
722 }
723}
724
725pub fn rtc_time_to_zx_time(rtc_time: fidl_fuchsia_hardware_rtc::Time) -> zx::SyntheticInstant {
726 let date = chrono::Utc
727 .with_ymd_and_hms(
728 rtc_time.year as i32,
729 rtc_time.month as u32,
730 rtc_time.day as u32,
731 rtc_time.hours as u32,
732 rtc_time.minutes as u32,
733 rtc_time.seconds as u32,
734 )
735 .unwrap();
736 zx::SyntheticInstant::from_nanos(date.timestamp_nanos_opt().unwrap())
737}
738
739pub fn create_cobalt_event_stream(
741 proxy: Arc<MetricEventLoggerQuerierProxy>,
742 log_method: LogMethod,
743) -> std::pin::Pin<Box<dyn Stream<Item = MetricEvent>>> {
744 async_utils::hanging_get::client::HangingGetStream::new(proxy, move |p| {
745 p.watch_logs(PROJECT_ID, log_method)
746 })
747 .map(|res| futures::stream::iter(res.expect("there should be a valid result here").0))
748 .flatten()
749 .boxed()
750}
751
752#[macro_export]
754macro_rules! poll_until_some {
755 ($condition:expr) => {
756 $crate::poll_until_some_impl(
757 $condition,
758 &$crate::SourceLocation::new(file!(), line!(), column!()),
759 )
760 };
761}
762
763#[macro_export]
766macro_rules! poll_until_some_async {
767 ($condition:expr) => {{
768 let loc = $crate::SourceLocation::new(file!(), line!(), column!());
769 log::info!("=> poll_until_some_async() for {}", &loc);
770 let mut result = None;
771 loop {
772 result = $condition.await;
773 if result.is_some() {
774 break;
775 }
776 fasync::Timer::new(fasync::MonotonicInstant::after($crate::RETRY_WAIT_DURATION)).await;
777 }
778 log::info!("=> poll_until_some_async() done for {}", &loc);
779 result.expect("we loop around while result is None")
780 }};
781}
782
783#[macro_export]
786macro_rules! poll_until_async {
787 ($condition:expr) => {
788 $crate::poll_until_async_impl(
789 $condition,
790 &$crate::SourceLocation::new(file!(), line!(), column!()),
791 )
792 };
793}
794
795#[macro_export]
797macro_rules! poll_until_async_2 {
798 ($condition:expr) => {{
799 let loc = $crate::SourceLocation::new(file!(), line!(), column!());
800 log::info!("=> poll_until_async() for {}", &loc);
801 let mut result = true;
802 loop {
803 result = $condition.await;
804 if result {
805 break;
806 }
807 fasync::Timer::new(fasync::MonotonicInstant::after($crate::RETRY_WAIT_DURATION)).await;
808 }
809 log::info!("=> poll_until_async_2() done for {}", &loc);
810 result
811 }};
812}
813
814#[macro_export]
816macro_rules! poll_until {
817 ($condition:expr) => {
818 $crate::poll_until_impl(
819 $condition,
820 &$crate::SourceLocation::new(file!(), line!(), column!()),
821 )
822 };
823}
824
825pub const RETRY_WAIT_DURATION: zx::MonotonicDuration = zx::MonotonicDuration::from_millis(10);
827
828pub struct SourceLocation {
829 file: &'static str,
830 line: u32,
831 column: u32,
832}
833
834impl std::fmt::Display for SourceLocation {
835 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
836 write!(f, "(file: {}, line: {}, column: {})", self.file, self.line, self.column)
837 }
838}
839
840impl SourceLocation {
841 pub fn new(file: &'static str, line: u32, column: u32) -> Self {
842 Self { file, line, column }
843 }
844}
845
846pub async fn poll_until_some_impl<T, F>(poll_fn: F, loc: &SourceLocation) -> T
848where
849 F: Fn() -> Option<T>,
850{
851 log::info!("=> poll_until_some() for {}", loc);
852 loop {
853 match poll_fn() {
854 Some(value) => {
855 log::info!("<= poll_until_some() for {}", loc);
856 return value;
857 }
858 None => fasync::Timer::new(fasync::MonotonicInstant::after(RETRY_WAIT_DURATION)).await,
859 }
860 }
861}
862
863pub async fn poll_until_async_impl<F, Fut>(poll_fn: F, loc: &SourceLocation)
865where
866 F: Fn() -> Fut,
867 Fut: Future<Output = bool>,
868{
869 log::info!("=> poll_until_async() for {}", loc);
870 while !poll_fn().await {
871 fasync::Timer::new(fasync::MonotonicInstant::after(RETRY_WAIT_DURATION)).await
872 }
873 log::info!("<= poll_until_async() for {}", loc);
874}
875
876pub async fn poll_until_impl<F: Fn() -> bool>(poll_fn: F, loc: &SourceLocation) {
878 log::info!("=> poll_until() for {}", loc);
879 while !poll_fn() {
880 fasync::Timer::new(fasync::MonotonicInstant::after(RETRY_WAIT_DURATION)).await
881 }
882 log::info!("<= poll_until() for {}", loc);
883}