1use fsync::Mutex;
6use futures_util::StreamExt;
7use std::sync::Arc;
8use zx::MonotonicDuration;
9use {fuchsia_async as fasync, fuchsia_sync as fsync};
10
11#[derive(Clone)]
13pub struct MemoryStallRate {
14 pub interval: MonotonicDuration,
16 pub rate_some: zx::sys::zx_duration_mono_t,
18 pub rate_full: zx::sys::zx_duration_mono_t,
20}
21
22pub trait StallProviderTrait: Sync + Send {
23 fn get_stall_info(&self) -> Result<zx::MemoryStall, anyhow::Error>;
25 fn get_stall_rate(&self) -> Option<MemoryStallRate>;
27}
28
29pub struct StallProvider {
30 _monitoring_task: fasync::Task<Result<(), anyhow::Error>>,
32 last_stall_info: Arc<Mutex<StallInformation>>,
34 stall_resource: Arc<dyn StallResource>,
36}
37
38struct StallInformation {
39 last_stall_values: zx::MemoryStall,
40 stall_rate: Option<MemoryStallRate>,
41}
42
43pub trait StallResource: Sync + Send {
46 fn get_memory_stall(&self) -> Result<zx::MemoryStall, zx::Status>;
47}
48
49impl StallResource for zx::Resource {
50 fn get_memory_stall(&self) -> Result<zx::MemoryStall, zx::Status> {
51 self.memory_stall()
52 }
53}
54
55impl StallProvider {
56 pub fn new(
59 stall_rate_interval: MonotonicDuration,
60 stall_resource: Arc<dyn StallResource>,
61 ) -> Result<StallProvider, anyhow::Error> {
62 let last_stall_info = Arc::new(Mutex::new(StallInformation {
63 last_stall_values: stall_resource.get_memory_stall()?,
64 stall_rate: None,
65 }));
66 let stall_resource_clone = stall_resource.clone();
67 let last_stall_clone = last_stall_info.clone();
68 let monitoring_task = fasync::Task::spawn(async move {
69 let mut interval_timer = fasync::Interval::new(stall_rate_interval);
70 while let Some(()) = interval_timer.next().await {
71 let new_stall_values = stall_resource.get_memory_stall()?;
72
73 let mut stall_info = last_stall_clone.lock();
74 stall_info.stall_rate = Some(MemoryStallRate {
75 interval: stall_rate_interval,
76 rate_some: new_stall_values.stall_time_some
77 - stall_info.last_stall_values.stall_time_some,
78 rate_full: new_stall_values.stall_time_full
79 - stall_info.last_stall_values.stall_time_full,
80 });
81
82 stall_info.last_stall_values = new_stall_values;
83 }
84 Ok(())
85 });
86
87 Ok(StallProvider {
88 _monitoring_task: monitoring_task,
89 last_stall_info,
90 stall_resource: stall_resource_clone,
91 })
92 }
93}
94
95impl StallProviderTrait for StallProvider {
96 fn get_stall_info(&self) -> Result<zx::MemoryStall, anyhow::Error> {
97 Ok(self.stall_resource.get_memory_stall()?)
98 }
99
100 fn get_stall_rate(&self) -> Option<MemoryStallRate> {
101 self.last_stall_info.lock().stall_rate.clone()
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use fuchsia_async::MonotonicInstant;
108
109 use super::*;
110 use std::pin::pin;
111
112 struct FakeStallResource {
113 pub stall_value: Arc<Mutex<zx::MemoryStall>>,
114 }
115
116 impl StallResource for FakeStallResource {
117 fn get_memory_stall(&self) -> Result<zx::MemoryStall, zx::Status> {
118 Ok(self.stall_value.lock().clone())
119 }
120 }
121
122 #[test]
123 fn test_get_stall_rate() {
124 let mut exec = fasync::TestExecutor::new_with_fake_time();
125 exec.set_fake_time(MonotonicInstant::from_nanos(0));
126
127 let stall_value =
128 Arc::new(Mutex::new(zx::MemoryStall { stall_time_some: 0, stall_time_full: 0 }));
129
130 stall_value.lock().stall_time_some = 1;
131 stall_value.lock().stall_time_full = 2;
132
133 let stall_provider = StallProvider::new(
134 MonotonicDuration::from_minutes(1),
135 Arc::new(FakeStallResource { stall_value: stall_value.clone() }),
136 )
137 .expect("Failed to create StallProvider");
138
139 let current_stall_value = stall_provider.get_stall_info().expect("No stall info");
140 assert_eq!(stall_value.lock().stall_time_some, current_stall_value.stall_time_some);
141 assert_eq!(stall_value.lock().stall_time_full, current_stall_value.stall_time_full);
142 assert!(stall_provider.get_stall_rate().is_none());
143
144 assert!(exec
145 .run_until_stalled(&mut pin!(async {
146 fasync::TestExecutor::advance_to(MonotonicInstant::after(
147 MonotonicDuration::from_seconds(1),
148 ))
149 .await
150 }))
151 .is_ready());
152
153 assert!(stall_provider.get_stall_rate().is_none());
154
155 stall_value.lock().stall_time_some = 2;
156 stall_value.lock().stall_time_full = 4;
157
158 assert!(exec
159 .run_until_stalled(&mut pin!(async {
160 fasync::TestExecutor::advance_to(MonotonicInstant::after(
161 MonotonicDuration::from_seconds(60),
162 ))
163 .await
164 }))
165 .is_ready());
166
167 let rate = stall_provider.get_stall_rate().expect("No stall rate");
168
169 assert_eq!(1, rate.rate_some);
170 assert_eq!(2, rate.rate_full);
171
172 let current_stall_value = stall_provider.get_stall_info().expect("No stall info");
173 assert_eq!(stall_value.lock().stall_time_some, current_stall_value.stall_time_some);
174 assert_eq!(stall_value.lock().stall_time_full, current_stall_value.stall_time_full);
175 }
176}