1use crate::fs::sysfs::sample_cpu_c2_residency;
6use crate::power::{SuspendState, SuspendStats};
7use crate::task::{CurrentTask, Kernel};
8
9use std::collections::{HashMap, HashSet};
10use std::future::Future;
11use std::sync::{Arc, Weak};
12
13use anyhow::{Context, anyhow};
14use fidl::endpoints::Proxy;
15use fidl_fuchsia_power_observability as fobs;
16use fidl_fuchsia_session_power as fpower;
17use fidl_fuchsia_starnix_runner as frunner;
18use fuchsia_component::client::connect_to_protocol_sync;
19use fuchsia_inspect as inspect;
20use fuchsia_inspect::ArrayProperty;
21use futures::stream::{FusedStream, Next};
22use futures::{FutureExt, StreamExt};
23use starnix_logging::{log_info, log_warn};
24use starnix_sync::{
25 EbpfSuspendLock, LockDepGuard, LockDepMutex, LockDepReadGuard, LockDepRwLock,
26 PowerMessageCountersLock, SuspendResumeManagerInnerLock,
27};
28use starnix_uapi::arc_key::WeakKey;
29use starnix_uapi::errors::Errno;
30use starnix_uapi::{errno, error};
31use std::collections::VecDeque;
32use std::fmt;
33use zx::Peered;
34
35#[derive(Debug, Default)]
37pub struct WakeupSource {
38 pub last_actor: Option<starnix_task_command::TaskCommand>,
40
41 active_count: u64,
43
44 event_count: u64,
47
48 wakeup_count: u64,
54
55 expire_count: u64,
57
58 active_since: zx::MonotonicInstant,
61
62 total_time: zx::MonotonicDuration,
64
65 max_time: zx::MonotonicDuration,
67
68 last_change: zx::MonotonicInstant,
70}
71
72impl WakeupSource {
73 pub fn display_name(&self, origin: &WakeupSourceOrigin) -> String {
75 match &self.last_actor {
76 Some(actor) => format!("{} [{}]", origin.to_string(), actor),
77 None => origin.to_string(),
78 }
79 }
80
81 pub fn active_duration(&self) -> zx::MonotonicDuration {
85 if self.active_since == zx::MonotonicInstant::ZERO {
86 zx::MonotonicDuration::default()
87 } else {
88 let now = zx::MonotonicInstant::get();
89 now - self.active_since
90 }
91 }
92}
93
94#[derive(Debug, Clone, Eq, PartialEq, Hash)]
95pub enum WakeupSourceOrigin {
96 WakeLock(String),
97 Epoll(crate::vfs::EpollKey),
98 HAL(String),
99}
100
101impl std::string::ToString for WakeupSourceOrigin {
102 fn to_string(&self) -> String {
103 match self {
104 WakeupSourceOrigin::WakeLock(lock) => lock.clone(),
105 WakeupSourceOrigin::Epoll(key) => format!("[epoll] {}", key),
106 WakeupSourceOrigin::HAL(lock) => format!("[HAL] {}", lock),
107 }
108 }
109}
110
111pub struct SuspendResumeManager {
113 inner: Arc<LockDepMutex<SuspendResumeManagerInner, SuspendResumeManagerInnerLock>>,
115
116 message_counters:
119 Arc<LockDepMutex<HashSet<WeakKey<OwnedMessageCounter>>, PowerMessageCountersLock>>,
120
121 ebpf_suspend_lock: LockDepRwLock<(), EbpfSuspendLock>,
123}
124
125pub struct SuspendResumeManagerInner {
128 suspend_stats: SuspendStats,
130 sync_on_suspend_enabled: bool,
131
132 suspend_events: VecDeque<SuspendEvent>,
133
134 wakeup_sources: HashMap<WakeupSourceOrigin, WakeupSource>,
136
137 active_lock_reader: zx::EventPair,
140
141 active_lock_writer: zx::EventPair,
145
146 active_wakeup_source_count: u64,
148
149 total_wakeup_source_event_count: u64,
152
153 external_wake_sources: HashMap<zx::Koid, ExternalWakeSource>,
155}
156
157#[derive(Debug)]
158struct ExternalWakeSource {
159 handle: zx::NullableHandle,
161 signals: zx::Signals,
163 name: String,
165}
166
167impl SuspendResumeManager {
168 pub fn add_external_wake_source(
169 &self,
170 handle: zx::NullableHandle,
171 signals: zx::Signals,
172 name: String,
173 ) -> Result<(), Errno> {
174 let manager = connect_to_protocol_sync::<frunner::ManagerMarker>()
175 .map_err(|e| errno!(EINVAL, format!("Failed to connect to manager: {e:?}")))?;
176 manager
177 .add_wake_source(frunner::ManagerAddWakeSourceRequest {
178 container_job: Some(
179 fuchsia_runtime::job_default()
180 .duplicate_handle(zx::Rights::SAME_RIGHTS)
181 .expect("Failed to dup handle"),
182 ),
183 name: Some(name.clone()),
184 handle: Some(
185 handle.duplicate_handle(zx::Rights::SAME_RIGHTS).map_err(|e| errno!(EIO, e))?,
186 ),
187 signals: Some(signals.bits()),
188 ..Default::default()
189 })
190 .map_err(|e| errno!(EIO, e))?;
191
192 let koid = handle.koid().map_err(|e| errno!(EINVAL, e))?;
193 self.lock().external_wake_sources.insert(
194 koid,
195 ExternalWakeSource {
196 handle: handle
197 .duplicate_handle(zx::Rights::SAME_RIGHTS)
198 .map_err(|e| errno!(EIO, e))?,
199 signals,
200 name,
201 },
202 );
203 Ok(())
204 }
205
206 pub fn remove_external_wake_source(&self, handle: zx::NullableHandle) -> Result<(), Errno> {
207 let manager = connect_to_protocol_sync::<frunner::ManagerMarker>()
208 .map_err(|e| errno!(EINVAL, format!("Failed to connect to manager: {e:?}")))?;
209
210 let koid = handle.koid().map_err(|e| errno!(EINVAL, e))?;
211 self.lock().external_wake_sources.remove(&koid);
212
213 manager
214 .remove_wake_source(frunner::ManagerRemoveWakeSourceRequest {
215 container_job: Some(
216 fuchsia_runtime::job_default()
217 .duplicate_handle(zx::Rights::SAME_RIGHTS)
218 .expect("Failed to dup handle"),
219 ),
220 handle: Some(handle),
221 ..Default::default()
222 })
223 .map_err(|e| errno!(EIO, e))?;
224
225 Ok(())
226 }
227}
228
229pub type EbpfSuspendGuard<'a> = LockDepReadGuard<'a, ()>;
230
231#[derive(Clone, Debug)]
232pub enum SuspendEvent {
233 Attempt { time: zx::BootInstant, state: String },
234 Resume { time: zx::BootInstant, reason: String },
235 Fail { time: zx::BootInstant, wakeup_sources: Option<Vec<String>> },
236}
237
238const INSPECT_RING_BUFFER_CAPACITY: usize = 128;
240
241impl Default for SuspendResumeManagerInner {
242 fn default() -> Self {
243 let (active_lock_reader, active_lock_writer) = zx::EventPair::create();
244 active_lock_writer
245 .signal_peer(zx::Signals::empty(), zx::Signals::USER_0)
246 .expect("Failed to signal peer");
247 Self {
248 suspend_stats: Default::default(),
249 sync_on_suspend_enabled: false,
250 suspend_events: VecDeque::with_capacity(INSPECT_RING_BUFFER_CAPACITY),
251 wakeup_sources: Default::default(),
252 active_lock_reader,
253 active_lock_writer,
254 active_wakeup_source_count: 0,
255 total_wakeup_source_event_count: 0,
256 external_wake_sources: Default::default(),
257 }
258 }
259}
260
261impl SuspendResumeManagerInner {
262 pub fn can_suspend(&self) -> bool {
264 self.active_wakeup_source_count == 0
265 }
266
267 pub fn active_wake_locks(&self) -> Vec<WakeupSourceOrigin> {
268 self.wakeup_sources
269 .iter()
270 .filter_map(|(name, source)| match name {
271 WakeupSourceOrigin::WakeLock(_) => {
272 if source.active_since > zx::MonotonicInstant::ZERO {
273 Some(name.clone())
274 } else {
275 None
276 }
277 }
278 _ => None,
279 })
280 .collect()
281 }
282
283 pub fn inactive_wake_locks(&self) -> Vec<WakeupSourceOrigin> {
284 self.wakeup_sources
285 .iter()
286 .filter_map(|(name, source)| match name {
287 WakeupSourceOrigin::WakeLock(_) => {
288 if source.active_since == zx::MonotonicInstant::ZERO {
289 Some(name.clone())
290 } else {
291 None
292 }
293 }
294 _ => None,
295 })
296 .collect()
297 }
298
299 fn signal_wake_events(&mut self) {
301 let (clear_mask, set_mask) = if self.active_wakeup_source_count == 0 {
302 (zx::Signals::EVENT_SIGNALED, zx::Signals::USER_0)
303 } else {
304 (zx::Signals::USER_0, zx::Signals::EVENT_SIGNALED)
305 };
306 self.active_lock_writer.signal_peer(clear_mask, set_mask).expect("Failed to signal peer");
307 }
308
309 fn update_suspend_stats<UpdateFn>(&mut self, update: UpdateFn)
310 where
311 UpdateFn: FnOnce(&mut SuspendStats),
312 {
313 update(&mut self.suspend_stats);
314 }
315
316 fn add_suspend_event(&mut self, event: SuspendEvent) {
317 if self.suspend_events.len() >= INSPECT_RING_BUFFER_CAPACITY {
318 self.suspend_events.pop_front();
319 }
320 self.suspend_events.push_back(event);
321 }
322
323 fn record_suspend_events(&self, node: &inspect::Node) {
324 let events_node = node.create_child("suspend_events");
325 for (i, event) in self.suspend_events.iter().enumerate() {
326 let child = events_node.create_child(i.to_string());
327 match event {
328 SuspendEvent::Attempt { time, state } => {
329 child.record_int(fobs::SUSPEND_ATTEMPTED_AT, time.into_nanos());
330 child.record_string(fobs::SUSPEND_REQUESTED_STATE, state);
331 }
332 SuspendEvent::Resume { time, reason } => {
333 child.record_int(fobs::SUSPEND_RESUMED_AT, time.into_nanos());
334 child.record_string(fobs::SUSPEND_RESUME_REASON, reason);
335 }
336 SuspendEvent::Fail { time, wakeup_sources } => {
337 child.record_int(fobs::SUSPEND_FAILED_AT, time.into_nanos());
338 if let Some(names) = wakeup_sources {
339 let names_array =
340 child.create_string_array(fobs::WAKEUP_SOURCES_NAME, names.len());
341 for (i, name) in names.iter().enumerate() {
342 names_array.set(i, name);
343 }
344 child.record(names_array);
345 }
346 }
347 }
348 events_node.record(child);
349 }
350 node.record(events_node);
351 }
352
353 fn record_wakeup_sources(&self, node: &inspect::Node) {
354 let wakeup_node = node.create_child("wakeup_sources");
355 for (origin, source) in self.wakeup_sources.iter() {
356 let child = wakeup_node.create_child(&source.display_name(origin));
357 child.record_uint("active_count", source.active_count);
358 child.record_uint("event_count", source.event_count);
359 child.record_uint("wakeup_count", source.wakeup_count);
360 child.record_uint("expire_count", source.expire_count);
361 child.record_int("active_since (ns)", source.active_since.into_nanos());
362 child.record_int("active_duration_mono (ns)", source.active_duration().into_nanos());
365 child.record_int("total_time (ms)", source.total_time.into_millis());
366 child.record_int("max_time (ms)", source.max_time.into_millis());
367 child.record_int("last_change (ns)", source.last_change.into_nanos());
368 wakeup_node.record(child);
369 }
370 node.record(wakeup_node);
371 }
372}
373
374pub type SuspendResumeManagerHandle = Arc<SuspendResumeManager>;
375
376impl Default for SuspendResumeManager {
377 fn default() -> Self {
378 let message_counters: Arc<
379 LockDepMutex<HashSet<WeakKey<OwnedMessageCounter>>, PowerMessageCountersLock>,
380 > = Default::default();
381 let message_counters_clone = message_counters.clone();
382 let root = inspect::component::inspector().root();
383 root.record_lazy_values("message_counters", move || {
384 let message_counters_clone = message_counters_clone.clone();
385 async move {
386 let inspector = fuchsia_inspect::Inspector::default();
387 let root = inspector.root();
388 let message_counters = message_counters_clone.lock();
389 let active_counter_names: Vec<String> = message_counters
390 .iter()
391 .filter_map(|c| c.0.upgrade())
392 .map(|c| c.to_string())
393 .collect();
394 let message_counters_inspect =
395 root.create_string_array("message_counters", active_counter_names.len());
396 for (i, name) in active_counter_names.iter().enumerate() {
397 message_counters_inspect.set(i, name);
398 }
399 root.record(message_counters_inspect);
400 Ok(inspector)
401 }
402 .boxed()
403 });
404 let inner = Arc::new(LockDepMutex::new(SuspendResumeManagerInner::default()));
405 let inner_clone = inner.clone();
406 root.record_lazy_child("wakeup_sources", move || {
407 let inner = inner_clone.clone();
408 async move {
409 let inspector = fuchsia_inspect::Inspector::default();
410 let root = inspector.root();
411 let state = inner.lock();
412
413 state.record_suspend_events(root);
414 state.record_wakeup_sources(root);
415
416 Ok(inspector)
417 }
418 .boxed()
419 });
420 Self { message_counters, inner, ebpf_suspend_lock: Default::default() }
421 }
422}
423
424impl SuspendResumeManager {
425 pub fn lock(&self) -> LockDepGuard<'_, SuspendResumeManagerInner> {
427 self.inner.lock()
428 }
429
430 pub fn init(
432 self: &SuspendResumeManagerHandle,
433 system_task: &CurrentTask,
434 ) -> Result<(), anyhow::Error> {
435 let handoff = system_task
436 .kernel()
437 .connect_to_protocol_at_container_svc::<fpower::HandoffMarker>()?
438 .into_sync_proxy();
439 match handoff.take(zx::MonotonicInstant::INFINITE) {
440 Ok(parent_lease) => {
441 let parent_lease = parent_lease
442 .map_err(|e| anyhow!("Failed to take lessor and lease from parent: {e:?}"))?;
443 drop(parent_lease)
444 }
445 Err(e) => {
446 if e.is_closed() {
447 log_warn!(
448 "Failed to send the fuchsia.session.power/Handoff.Take request. Assuming no Handoff protocol exists and moving on..."
449 );
450 } else {
451 return Err(e).context("Handoff::Take");
452 }
453 }
454 }
455 Ok(())
456 }
457
458 pub fn activate_wakeup_source(&self, origin: WakeupSourceOrigin) -> bool {
459 self.activate_wakeup_source_with_actor(origin, None)
460 }
461
462 pub fn activate_wakeup_source_with_actor(
463 &self,
464 origin: WakeupSourceOrigin,
465 actor: Option<starnix_task_command::TaskCommand>,
466 ) -> bool {
467 let mut state = self.lock();
468 let did_activate = {
469 let entry = state.wakeup_sources.entry(origin).or_default();
470 entry.last_actor = actor;
471 let now = zx::MonotonicInstant::get();
472 entry.active_count += 1;
473 entry.event_count += 1;
474 entry.last_change = now;
475 if entry.active_since == zx::MonotonicInstant::ZERO {
476 entry.active_since = now;
477 true
478 } else {
479 false
480 }
481 };
482 if did_activate {
483 state.active_wakeup_source_count += 1;
484 state.signal_wake_events();
485 }
486 did_activate
487 }
488
489 pub fn deactivate_wakeup_source(&self, origin: &WakeupSourceOrigin) -> bool {
490 self.remove_wakeup_source(origin, false)
491 }
492
493 pub fn timeout_wakeup_source(&self, origin: &WakeupSourceOrigin) -> bool {
494 self.remove_wakeup_source(origin, true)
495 }
496
497 fn remove_wakeup_source(&self, origin: &WakeupSourceOrigin, timed_out: bool) -> bool {
498 let mut state = self.lock();
499 let removed = match state.wakeup_sources.get_mut(origin) {
500 Some(entry) if entry.active_since != zx::MonotonicInstant::ZERO => {
501 if timed_out {
502 entry.expire_count += 1;
503 }
504
505 let now = zx::MonotonicInstant::get();
506 let duration = now - entry.active_since;
507 entry.total_time += duration;
508 entry.max_time = std::cmp::max(duration, entry.max_time);
509 entry.last_change = now;
510 entry.active_since = zx::MonotonicInstant::ZERO;
511
512 true
513 }
514 _ => false,
515 };
516 if removed {
517 state.active_wakeup_source_count -= 1;
518 state.total_wakeup_source_event_count += 1;
519 state.signal_wake_events();
520 }
521 removed
522 }
523
524 pub fn add_message_counter(
525 &self,
526 name: &str,
527 counter: Option<zx::Counter>,
528 ) -> OwnedMessageCounterHandle {
529 let container_counter = OwnedMessageCounter::new(name, counter);
530 let mut message_counters = self.message_counters.lock();
531 message_counters.insert(WeakKey::from(&container_counter));
532 message_counters.retain(|c| c.0.upgrade().is_some());
533 container_counter
534 }
535
536 pub fn has_nonzero_message_counter(&self) -> bool {
537 self.message_counters.lock().iter().any(|c| {
538 let Some(c) = c.0.upgrade() else {
539 return false;
540 };
541 c.counter.as_ref().and_then(|counter| counter.read().ok()).map_or(false, |v| v != 0)
542 })
543 }
544
545 pub fn duplicate_lock_event(&self) -> zx::EventPair {
548 let state = self.lock();
549 state
550 .active_lock_reader
551 .duplicate_handle(zx::Rights::SAME_RIGHTS)
552 .expect("Failed to duplicate handle")
553 }
554
555 pub fn suspend_stats(&self) -> SuspendStats {
557 self.lock().suspend_stats.clone()
558 }
559
560 pub fn total_wakeup_events(&self) -> u64 {
561 let state = self.lock();
562 state.total_wakeup_source_event_count + state.suspend_stats.success_count
563 }
564
565 pub fn sync_on_suspend_enabled(&self) -> bool {
569 self.lock().sync_on_suspend_enabled.clone()
570 }
571
572 pub fn set_sync_on_suspend(&self, enable: bool) {
575 self.lock().sync_on_suspend_enabled = enable;
576 }
577
578 pub fn suspend_states(&self) -> HashSet<SuspendState> {
580 HashSet::from([SuspendState::Idle])
582 }
583
584 pub fn suspend(&self, kernel: &Kernel, suspend_state: SuspendState) -> Result<(), Errno> {
585 let suspend_start_time = zx::BootInstant::get();
586 let mut state = self.lock();
587 state.add_suspend_event(SuspendEvent::Attempt {
588 time: suspend_start_time,
589 state: suspend_state.to_string(),
590 });
591
592 if !state.can_suspend() {
594 self.report_failed_suspension(state, "kernel wake lock");
595 return error!(EINVAL);
596 }
597
598 let external_wake_source_abort = state.external_wake_sources.values().find_map(|source| {
600 if source.handle.wait_one(source.signals, zx::MonotonicInstant::INFINITE_PAST).is_ok() {
601 Some(source.name.clone())
602 } else {
603 None
604 }
605 });
606
607 if let Some(name) = external_wake_source_abort {
608 self.report_failed_suspension(state, &format!("external wake source: {}", name));
609 return error!(EINVAL);
610 }
611
612 std::mem::drop(state);
616
617 let _ebpf_lock = self.ebpf_suspend_lock.write();
620
621 let manager = connect_to_protocol_sync::<frunner::ManagerMarker>()
622 .expect("Failed to connect to manager");
623 fuchsia_trace::duration!("power", "suspend_container:fidl");
624
625 let container_job = Some(
626 fuchsia_runtime::job_default()
627 .duplicate_handle(zx::Rights::SAME_RIGHTS)
628 .expect("Failed to dup handle"),
629 );
630 let wake_lock_event = Some(self.duplicate_lock_event());
631
632 log_info!("Requesting container suspension.");
633
634 sample_cpu_c2_residency(kernel);
637 let suspend_result = manager.suspend_container(
638 frunner::ManagerSuspendContainerRequest {
639 container_job,
640 wake_locks: wake_lock_event,
641 ..Default::default()
642 },
643 zx::Instant::INFINITE,
644 );
645 sample_cpu_c2_residency(kernel);
646
647 match suspend_result {
648 Ok(Ok(res)) => {
649 self.report_container_resumed(suspend_start_time, res);
650 }
651 e => {
652 let state = self.lock();
653 self.report_failed_suspension(state, &format!("runner error {:?}", e));
654 return error!(EINVAL);
655 }
656 }
657 Ok(())
658 }
659
660 fn report_container_resumed(
661 &self,
662 suspend_start_time: zx::BootInstant,
663 res: frunner::ManagerSuspendContainerResponse,
664 ) {
665 let wake_time = zx::BootInstant::get();
666 let resume_reason = res.resume_reason.clone().map(|s| format!("0 {}", s));
669 log_info!("Resuming from container suspension: {:?}", resume_reason);
670 let mut state = self.lock();
671 state.update_suspend_stats(|suspend_stats| {
672 suspend_stats.success_count += 1;
673 suspend_stats.last_time_in_suspend_operations = (wake_time - suspend_start_time).into();
674 suspend_stats.last_time_in_sleep =
675 zx::BootDuration::from_nanos(res.suspend_time.unwrap_or(0));
676 suspend_stats.last_resume_reason = resume_reason.clone();
677 });
678 state.add_suspend_event(SuspendEvent::Resume {
679 time: wake_time,
680 reason: resume_reason.unwrap_or_default(),
681 });
682 fuchsia_trace::instant!("power", "suspend_container:done", fuchsia_trace::Scope::Process);
683 }
684
685 fn report_failed_suspension(
686 &self,
687 mut state: LockDepGuard<'_, SuspendResumeManagerInner>,
688 failure_reason: &str,
689 ) {
690 let wake_time = zx::BootInstant::get();
691 state.update_suspend_stats(|suspend_stats| {
692 suspend_stats.fail_count += 1;
693 suspend_stats.last_failed_errno = Some(errno!(EINVAL));
694 suspend_stats.last_resume_reason = None;
695 });
696
697 let mut wakeup_sources: Vec<String> = state
698 .wakeup_sources
699 .iter_mut()
700 .filter_map(|(origin, source)| {
701 if source.active_since > zx::MonotonicInstant::ZERO {
702 source.wakeup_count += 1;
703 Some(source.display_name(origin))
704 } else {
705 None
706 }
707 })
708 .collect();
709
710 for source in state.external_wake_sources.values() {
711 if source.handle.wait_one(source.signals, zx::MonotonicInstant::INFINITE_PAST).is_ok() {
712 wakeup_sources.push(source.name.clone());
713 }
714 }
715
716 let last_resume_reason = format!("Abort: {}", wakeup_sources.join(" "));
717 state.update_suspend_stats(|suspend_stats| {
718 suspend_stats.last_resume_reason = Some(last_resume_reason);
720 });
721
722 log_warn!(
724 "Suspend failed due to {:?}. Here are the active wakeup sources: {:?}",
725 failure_reason,
726 wakeup_sources,
727 );
728 state.add_suspend_event(SuspendEvent::Fail {
730 time: wake_time,
731 wakeup_sources: Some(wakeup_sources),
732 });
733 fuchsia_trace::instant!("power", "suspend_container:error", fuchsia_trace::Scope::Process);
734 }
735
736 pub fn acquire_ebpf_suspend_lock<'a>(&'a self) -> EbpfSuspendGuard<'a> {
737 self.ebpf_suspend_lock.read()
738 }
739}
740
741pub trait OnWakeOps: Send + Sync {
743 fn on_wake(&self, current_task: &CurrentTask, baton_lease: &zx::NullableHandle);
752}
753
754pub fn create_proxy_for_wake_events_counter_zero(
772 remote_channel: zx::Channel,
773 name: String,
774) -> (zx::Channel, zx::Counter) {
775 let (local_proxy, kernel_channel) = zx::Channel::create();
776 let counter = zx::Counter::create();
777
778 let local_counter =
779 counter.duplicate_handle(zx::Rights::SAME_RIGHTS).expect("Failed to duplicate counter");
780
781 let manager = fuchsia_component::client::connect_to_protocol_sync::<frunner::ManagerMarker>()
782 .expect("failed");
783 manager
784 .proxy_wake_channel(frunner::ManagerProxyWakeChannelRequest {
785 container_job: Some(
786 fuchsia_runtime::job_default()
787 .duplicate_handle(zx::Rights::SAME_RIGHTS)
788 .expect("Failed to dup handle"),
789 ),
790 container_channel: Some(kernel_channel),
791 remote_channel: Some(remote_channel),
792 counter: Some(counter),
793 name: Some(name),
794 ..Default::default()
795 })
796 .expect("Failed to create proxy");
797
798 (local_proxy, local_counter)
799}
800
801pub fn create_proxy_for_wake_events_counter(
820 remote_channel: zx::Channel,
821 name: String,
822) -> (zx::Channel, zx::Counter) {
823 let (proxy, counter) = create_proxy_for_wake_events_counter_zero(remote_channel, name);
824
825 counter.add(1).expect("Failed to add to counter");
828
829 (proxy, counter)
830}
831
832pub fn mark_proxy_message_handled(counter: &zx::Counter) {
837 counter.add(-1).expect("Failed to decrement counter");
838}
839
840pub fn mark_all_proxy_messages_handled(counter: &zx::Counter) {
842 counter.write(0).expect("Failed to decrement counter");
843}
844
845pub fn create_watcher_for_wake_events(watcher: zx::EventPair) {
849 let manager = fuchsia_component::client::connect_to_protocol_sync::<frunner::ManagerMarker>()
850 .expect("failed");
851 manager
852 .register_wake_watcher(
853 frunner::ManagerRegisterWakeWatcherRequest {
854 watcher: Some(watcher),
855 ..Default::default()
856 },
857 zx::Instant::INFINITE,
858 )
859 .expect("Failed to register wake watcher");
860}
861
862#[derive(Debug)]
871pub struct SharedMessageCounter(Weak<OwnedMessageCounter>);
872
873impl Drop for SharedMessageCounter {
874 fn drop(&mut self) {
875 if let Some(message_counter) = self.0.upgrade() {
876 message_counter.mark_handled();
877 }
878 }
879}
880
881pub struct OwnedMessageCounter {
886 name: String,
887 counter: Option<zx::Counter>,
888}
889pub type OwnedMessageCounterHandle = Arc<OwnedMessageCounter>;
890
891impl Drop for OwnedMessageCounter {
892 fn drop(&mut self) {
897 self.counter.as_ref().map(mark_all_proxy_messages_handled);
898 }
899}
900
901impl OwnedMessageCounter {
902 pub fn new(name: &str, counter: Option<zx::Counter>) -> OwnedMessageCounterHandle {
903 Arc::new(Self { name: name.to_string(), counter })
904 }
905
906 pub fn mark_handled(&self) {
911 self.counter.as_ref().map(mark_proxy_message_handled);
912 }
913
914 pub fn mark_pending(&self) {
918 self.counter.as_ref().map(|c| c.add(1).expect("Failed to increment counter"));
919 }
920
921 pub fn share(
925 self: &OwnedMessageCounterHandle,
926 new_pending_message: bool,
927 ) -> SharedMessageCounter {
928 if new_pending_message {
929 self.mark_pending();
930 }
931 SharedMessageCounter(Arc::downgrade(self))
932 }
933}
934
935impl fmt::Display for OwnedMessageCounter {
936 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
937 write!(f, "Counter({}): {:?}", self.name, self.counter.as_ref().map(|c| c.read()))
938 }
939}
940
941pub struct ContainerWakingProxy<P: Proxy> {
946 counter: OwnedMessageCounterHandle,
947 proxy: P,
948}
949
950impl<P: Proxy> ContainerWakingProxy<P> {
951 pub fn new(counter: OwnedMessageCounterHandle, proxy: P) -> Self {
952 Self { counter, proxy }
953 }
954
955 pub fn call<T, F, R>(&self, future: F) -> R
959 where
960 F: FnOnce(&P) -> R,
961 R: Future<Output = T>,
962 {
963 let f = future(&self.proxy);
971 self.counter.mark_handled();
972 f
973 }
974}
975
976pub struct ContainerWakingStream<S: FusedStream + Unpin> {
981 counter: OwnedMessageCounterHandle,
982 stream: S,
983}
984
985impl<S: FusedStream + Unpin> ContainerWakingStream<S> {
986 pub fn new(counter: OwnedMessageCounterHandle, stream: S) -> Self {
987 Self { counter, stream }
988 }
989
990 pub fn next(&mut self) -> Next<'_, S> {
994 let is_terminated = self.stream.is_terminated();
996 let next = self.stream.next();
997 if !is_terminated {
998 self.counter.mark_handled();
999 }
1000 next
1001 }
1002}
1003
1004#[cfg(test)]
1005mod test {
1006 use super::*;
1007 use diagnostics_assertions::assert_data_tree;
1008 use fidl::endpoints::create_proxy_and_stream;
1009 use fidl_test_placeholders::{EchoMarker, EchoRequest};
1010 use fuchsia_async as fasync;
1011 use fuchsia_inspect as inspect;
1012 use futures::StreamExt;
1013
1014 #[::fuchsia::test]
1015 fn test_counter_zero_initialization() {
1016 let (_endpoint, endpoint) = zx::Channel::create();
1017 let (_channel, counter) =
1018 super::create_proxy_for_wake_events_counter_zero(endpoint, "test".into());
1019 assert_eq!(counter.read(), Ok(0));
1020 }
1021
1022 #[::fuchsia::test]
1023 fn test_counter_initialization() {
1024 let (_endpoint, endpoint) = zx::Channel::create();
1025 let (_channel, counter) =
1026 super::create_proxy_for_wake_events_counter(endpoint, "test".into());
1027 assert_eq!(counter.read(), Ok(1));
1028 }
1029
1030 #[::fuchsia::test]
1031 async fn test_container_waking_proxy() {
1032 let (proxy, mut stream) = create_proxy_and_stream::<EchoMarker>();
1033 let server_task = fasync::Task::spawn(async move {
1034 let request = stream.next().await.unwrap().unwrap();
1035 match request {
1036 EchoRequest::EchoString { value, responder } => {
1037 responder.send(value.as_deref()).unwrap();
1038 }
1039 }
1040 });
1041
1042 let counter = zx::Counter::create();
1043 counter.add(5).unwrap();
1044 assert_eq!(counter.read(), Ok(5));
1045
1046 let waking_proxy = ContainerWakingProxy {
1047 counter: OwnedMessageCounter::new(
1048 "test_proxy",
1049 Some(counter.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap()),
1050 ),
1051 proxy,
1052 };
1053
1054 let response_future = waking_proxy.call(|p| p.echo_string(Some("hello")));
1055
1056 assert_eq!(counter.read(), Ok(4));
1058
1059 let response = response_future.await.unwrap();
1060 assert_eq!(response.as_deref(), Some("hello"));
1061
1062 server_task.await;
1063
1064 assert_eq!(counter.read(), Ok(4));
1065 drop(waking_proxy);
1066 assert_eq!(counter.read(), Ok(0));
1067 }
1068
1069 #[::fuchsia::test]
1070 async fn test_container_waking_stream() {
1071 let (proxy, stream) = create_proxy_and_stream::<EchoMarker>();
1072 let client_task = fasync::Task::spawn(async move {
1073 let response = proxy.echo_string(Some("hello")).await.unwrap();
1074 assert_eq!(response.as_deref(), Some("hello"));
1075 });
1076
1077 let counter = zx::Counter::create();
1078 counter.add(5).unwrap();
1079 assert_eq!(counter.read(), Ok(5));
1080
1081 let mut waking_stream = ContainerWakingStream {
1082 counter: OwnedMessageCounter::new(
1083 "test_stream",
1084 Some(counter.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap()),
1085 ),
1086 stream,
1087 };
1088
1089 let request_future = waking_stream.next();
1090
1091 assert_eq!(counter.read(), Ok(4));
1093
1094 let request = request_future.await.unwrap().unwrap();
1095 match request {
1096 EchoRequest::EchoString { value, responder } => {
1097 assert_eq!(value.as_deref(), Some("hello"));
1098 responder.send(value.as_deref()).unwrap();
1099 }
1100 }
1101
1102 client_task.await;
1103
1104 assert_eq!(counter.read(), Ok(4));
1105 drop(waking_stream);
1106 assert_eq!(counter.read(), Ok(0));
1107 }
1108
1109 #[::fuchsia::test]
1110 async fn test_message_counters_inspect() {
1111 let power_manager = SuspendResumeManager::default();
1112 let inspector = inspect::component::inspector();
1113
1114 let zx_counter = zx::Counter::create();
1115 let counter_handle = power_manager.add_message_counter(
1116 "test_counter",
1117 Some(zx_counter.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap()),
1118 );
1119
1120 zx_counter.add(1).unwrap();
1121
1122 assert_data_tree!(inspector, root: contains {
1123 message_counters: vec!["Counter(test_counter): Some(Ok(1))"],
1124 });
1125
1126 zx_counter.add(1).unwrap();
1127 assert_data_tree!(inspector, root: contains {
1128 message_counters: vec!["Counter(test_counter): Some(Ok(2))"],
1129 });
1130
1131 drop(counter_handle);
1132 assert_data_tree!(inspector, root: contains {
1133 message_counters: Vec::<String>::new(),
1134 });
1135 }
1136
1137 #[::fuchsia::test]
1138 fn test_shared_message_counter() {
1139 let zx_counter = zx::Counter::create();
1141 let owned_counter = OwnedMessageCounter::new(
1142 "test_shared_counter",
1143 Some(zx_counter.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap()),
1144 );
1145 zx_counter.add(5).unwrap();
1146 assert_eq!(zx_counter.read(), Ok(5));
1147
1148 let shared_counter = owned_counter.share(false);
1150 assert_eq!(zx_counter.read(), Ok(5));
1151
1152 drop(shared_counter);
1154 assert_eq!(zx_counter.read(), Ok(4));
1155
1156 let shared_counter_2 = owned_counter.share(true);
1158 assert_eq!(zx_counter.read(), Ok(5));
1159
1160 drop(shared_counter_2);
1162 assert_eq!(zx_counter.read(), Ok(4));
1163
1164 let shared_counter_3 = owned_counter.share(false);
1166 assert_eq!(zx_counter.read(), Ok(4));
1167
1168 drop(owned_counter);
1170 assert_eq!(zx_counter.read(), Ok(0));
1171
1172 drop(shared_counter_3);
1174 assert_eq!(zx_counter.read(), Ok(0));
1175 }
1176
1177 #[::fuchsia::test]
1178 async fn test_container_waking_event_termination() {
1179 let stream = futures::stream::iter(vec![0]).fuse();
1180 let counter = zx::Counter::create();
1181 counter.add(2).unwrap();
1182 assert_eq!(counter.read(), Ok(2));
1183 let mut waking_stream = ContainerWakingStream {
1184 counter: OwnedMessageCounter::new(
1185 "test_stream",
1186 Some(counter.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap()),
1187 ),
1188 stream,
1189 };
1190
1191 assert_eq!(waking_stream.next().await, Some(0));
1192 assert_eq!(counter.read(), Ok(1));
1193
1194 assert_eq!(waking_stream.next().await, None);
1195 assert_eq!(waking_stream.next().await, None);
1196 assert_eq!(counter.read(), Ok(0));
1198 }
1199
1200 #[::fuchsia::test]
1201 fn test_external_wake_source_aborts_suspend() {
1202 let manager = SuspendResumeManager::default();
1203 let event = zx::Event::create();
1204 let signals = zx::Signals::USER_0;
1205
1206 let res = manager.add_external_wake_source(
1211 event.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap().into_handle(),
1212 signals,
1213 "test_external".to_string(),
1214 );
1215
1216 if res.is_err() {
1217 println!(
1218 "Skipping test_external_wake_source_aborts_suspend because runner connection failed: {:?}",
1219 res
1220 );
1221 return;
1222 }
1223
1224 event.signal(zx::Signals::empty(), signals).unwrap();
1226
1227 let state = manager.lock();
1228 assert!(state.external_wake_sources.contains_key(&event.koid().unwrap()));
1229 }
1230}