OLD | NEW |
| (Empty) |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "components/memory_pressure/memory_pressure_monitor.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/location.h" | |
9 #include "base/task_runner.h" | |
10 #include "base/time/tick_clock.h" | |
11 #include "components/memory_pressure/memory_pressure_calculator.h" | |
12 #include "components/memory_pressure/memory_pressure_stats_collector.h" | |
13 | |
14 namespace memory_pressure { | |
15 | |
16 namespace { | |
17 | |
18 using MemoryPressureLevel = MemoryPressureMonitor::MemoryPressureLevel; | |
19 const MemoryPressureLevel MEMORY_PRESSURE_LEVEL_NONE = | |
20 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE; | |
21 const MemoryPressureLevel MEMORY_PRESSURE_LEVEL_MODERATE = | |
22 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE; | |
23 const MemoryPressureLevel MEMORY_PRESSURE_LEVEL_CRITICAL = | |
24 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL; | |
25 | |
26 // Returns the polling/notification interval for the given pressure level. | |
27 int GetPollingIntervalMs(MemoryPressureLevel level) { | |
28 switch (level) { | |
29 case MEMORY_PRESSURE_LEVEL_NONE: | |
30 return MemoryPressureMonitor::kDefaultPollingIntervalMs; | |
31 | |
32 case MEMORY_PRESSURE_LEVEL_MODERATE: | |
33 return MemoryPressureMonitor::kNotificationIntervalPressureModerateMs; | |
34 | |
35 case MEMORY_PRESSURE_LEVEL_CRITICAL: | |
36 return MemoryPressureMonitor::kNotificationIntervalPressureCriticalMs; | |
37 } | |
38 | |
39 NOTREACHED(); | |
40 return 0; | |
41 } | |
42 | |
43 base::TimeDelta GetPollingInterval(MemoryPressureLevel level) { | |
44 return base::TimeDelta::FromMilliseconds(GetPollingIntervalMs(level)); | |
45 } | |
46 | |
47 // Serial number reserved for unscheduled checks as a result of external calls | |
48 // to the monitor. | |
49 const int kUnscheduledCheckSerial = 0; | |
50 | |
51 } // namespace | |
52 | |
53 #if !defined(MEMORY_PRESSURE_IS_POLLING) | |
54 // Default definition of this class. | |
55 // TODO(chrisha): Implement useful versions of this for affected platforms. | |
56 class MemoryPressureMonitorImpl {}; | |
57 #endif | |
58 | |
59 #if defined(MEMORY_PRESSURE_IS_POLLING) | |
60 MemoryPressureMonitor::MemoryPressureMonitor( | |
61 const scoped_refptr<base::TaskRunner>& task_runner, | |
62 base::TickClock* tick_clock, | |
63 MemoryPressureStatsCollector* stats_collector, | |
64 MemoryPressureCalculator* calculator, | |
65 const DispatchCallback& dispatch_callback) | |
66 : task_runner_(task_runner), | |
67 tick_clock_(tick_clock), | |
68 stats_collector_(stats_collector), | |
69 calculator_(calculator), | |
70 dispatch_callback_(dispatch_callback), | |
71 current_memory_pressure_level_(MEMORY_PRESSURE_LEVEL_NONE), | |
72 serial_number_(kUnscheduledCheckSerial), | |
73 weak_ptr_factory_(this) { | |
74 DCHECK(task_runner_.get()); | |
75 DCHECK(tick_clock_); | |
76 DCHECK(stats_collector_); | |
77 DCHECK(calculator_); | |
78 DCHECK(!dispatch_callback_.is_null()); | |
79 | |
80 Start(); | |
81 } | |
82 #else // MEMORY_PRESSURE_IS_POLLING | |
83 MemoryPressureMonitor::MemoryPressureMonitor( | |
84 const scoped_refptr<base::TaskRunner>& task_runner, | |
85 base::TickClock* tick_clock, | |
86 MemoryPressureStatsCollector* stats_collector, | |
87 const DispatchCallback& dispatch_callback, | |
88 MemoryPressureLevel initial_pressure_level) | |
89 : task_runner_(task_runner), | |
90 tick_clock_(tick_clock), | |
91 stats_collector_(stats_collector), | |
92 dispatch_callback_(dispatch_callback), | |
93 current_memory_pressure_level_(initial_pressure_level), | |
94 serial_number_(kUnscheduledCheckSerial), | |
95 weak_ptr_factory_(this) { | |
96 DCHECK(task_runner_.get()); | |
97 DCHECK(tick_clock_); | |
98 DCHECK(stats_collector_); | |
99 DCHECK(!dispatch_callback_.is_null()); | |
100 | |
101 Start(); | |
102 } | |
103 #endif // !MEMORY_PRESSURE_IS_POLLING | |
104 | |
105 MemoryPressureMonitor::~MemoryPressureMonitor() {} | |
106 | |
107 MemoryPressureLevel MemoryPressureMonitor::GetCurrentPressureLevel() { | |
108 base::AutoLock lock(lock_); | |
109 | |
110 #if defined(MEMORY_PRESSURE_IS_POLLING) | |
111 // Force an immediate pressure check on polling platforms. On non-polling | |
112 // platforms the current memory pressure is always valid. | |
113 CheckPressureAndUpdateStatsLocked(kUnscheduledCheckSerial); | |
114 #endif | |
115 | |
116 return current_memory_pressure_level_; | |
117 } | |
118 | |
119 void MemoryPressureMonitor::CheckMemoryPressureSoon() { | |
120 // This function is a nop on non-polling platforms. | |
121 #if defined(MEMORY_PRESSURE_IS_POLLING) | |
122 // Schedule a check to run as soon as possible. | |
123 base::AutoLock lock(lock_); | |
124 base::TimeTicks now = tick_clock_->NowTicks(); | |
125 ScheduleTaskLocked(now); | |
126 #endif | |
127 } | |
128 | |
129 #if !defined(MEMORY_PRESSURE_IS_POLLING) | |
130 // This is the entry point for OS notifications of pressure level changes. | |
131 void MemoryPressureMonitor::OnMemoryPressureChanged( | |
132 MemoryPressureLevel level) { | |
133 base::AutoLock lock(lock_); | |
134 | |
135 // Do nothing if the level hasn't changed. | |
136 if (level == current_memory_pressure_level_) | |
137 return; | |
138 | |
139 // Update the level and the stats. | |
140 current_memory_pressure_level_ = level; | |
141 stats_collector_->UpdateStatistics(current_memory_pressure_level_); | |
142 | |
143 // Only dispatch notifications if there is memory pressure. | |
144 if (current_memory_pressure_level_ > MEMORY_PRESSURE_LEVEL_NONE) { | |
145 last_notification_ = tick_clock_->NowTicks(); | |
146 dispatch_callback_.Run(current_memory_pressure_level_); | |
147 } | |
148 | |
149 ScheduleTaskIfNeededLocked(kUnscheduledCheckSerial); | |
150 } | |
151 #endif | |
152 | |
153 void MemoryPressureMonitor::Start() { | |
154 base::AutoLock lock(lock_); | |
155 | |
156 base::TimeTicks now = tick_clock_->NowTicks(); | |
157 | |
158 // Get the statistics collector warmed up by measuring the pressure level. | |
159 // Don't immediately fire a signal if already under memory pressure as the | |
160 // system is quite busy with startup right now. Wait until the first | |
161 // renotification is required, allowing the browser time to get started. | |
162 // Non-polling implementations set the initial memory pressure level in the | |
163 // constructor. | |
164 #if defined(MEMORY_PRESSURE_IS_POLLING) | |
165 current_memory_pressure_level_ = calculator_->CalculateCurrentPressureLevel(); | |
166 last_check_ = now; | |
167 #endif | |
168 | |
169 last_notification_ = now; | |
170 stats_collector_->UpdateStatistics(current_memory_pressure_level_); | |
171 ScheduleTaskIfNeededLocked(kUnscheduledCheckSerial); | |
172 } | |
173 | |
174 void MemoryPressureMonitor::CheckPressureAndUpdateStats(int serial) { | |
175 // This should only ever be used by scheduled checks. | |
176 DCHECK_NE(kUnscheduledCheckSerial, serial); | |
177 base::AutoLock lock(lock_); | |
178 CheckPressureAndUpdateStatsLocked(serial); | |
179 } | |
180 | |
181 void MemoryPressureMonitor::CheckPressureAndUpdateStatsLocked(int serial) { | |
182 lock_.AssertAcquired(); | |
183 | |
184 base::TimeTicks now = tick_clock_->NowTicks(); | |
185 MemoryPressureLevel old_level = current_memory_pressure_level_; | |
186 | |
187 #if defined(MEMORY_PRESSURE_IS_POLLING) | |
188 // Don't check again if pressure was calculated too recently. | |
189 DCHECK(!last_check_.is_null()); | |
190 if ((now - last_check_) >= | |
191 base::TimeDelta::FromMilliseconds(kMinimumTimeBetweenSamplesMs)) { | |
192 // Calculate the current pressure level and update statistics. | |
193 last_check_ = now; | |
194 current_memory_pressure_level_ = | |
195 calculator_->CalculateCurrentPressureLevel(); | |
196 stats_collector_->UpdateStatistics(current_memory_pressure_level_); | |
197 } | |
198 #else // MEMORY_PRESSURE_IS_POLLING | |
199 // On non-polling platforms this function can only be invoked by scheduled | |
200 // callbacks for updating statistics and sending renotifications. Simply | |
201 // update the statistics and move on. | |
202 DCHECK_NE(kUnscheduledCheckSerial, serial); | |
203 stats_collector_->UpdateStatistics(current_memory_pressure_level_); | |
204 #endif // !MEMORY_PRESSURE_IS_POLLING | |
205 | |
206 // Check if the level has changed or a renotification is required. | |
207 DCHECK(!last_notification_.is_null()); | |
208 if (current_memory_pressure_level_ != old_level || | |
209 now - last_notification_ >= | |
210 GetPollingInterval(current_memory_pressure_level_)) { | |
211 last_notification_ = now; | |
212 | |
213 // Only dispatch notifications if there is memory pressure. | |
214 if (current_memory_pressure_level_ > MEMORY_PRESSURE_LEVEL_NONE) | |
215 dispatch_callback_.Run(current_memory_pressure_level_); | |
216 } | |
217 | |
218 ScheduleTaskIfNeededLocked(serial); | |
219 } | |
220 | |
221 void MemoryPressureMonitor::ScheduleTaskIfNeededLocked(int serial) { | |
222 lock_.AssertAcquired(); | |
223 | |
224 // Remove this check from the set of scheduled checks. | |
225 if (serial != kUnscheduledCheckSerial) { | |
226 size_t erased = scheduled_checks_.erase(serial); | |
227 DCHECK_EQ(1u, erased); | |
228 } | |
229 | |
230 // Get the time of the soonest scheduled check. This linear scan is quick | |
231 // because the map will contain at most 1 entry per pressure level, and most | |
232 // commonly only 1 entry. | |
233 base::TimeTicks next_check; | |
234 for (const auto& check : scheduled_checks_) { | |
235 if (next_check.is_null() || check.second < next_check) | |
236 next_check = check.second; | |
237 } | |
238 | |
239 // Get the time of the required next check. | |
240 base::TimeTicks required_check = | |
241 last_notification_ + GetPollingInterval(current_memory_pressure_level_); | |
242 | |
243 // If there's already a check scheduled in time then don't schedule | |
244 // another. This lets the number of scheduled checks shrink back down to 1 | |
245 // as pressure changes occur. | |
246 if (!next_check.is_null() && next_check <= required_check) | |
247 return; | |
248 | |
249 ScheduleTaskLocked(required_check); | |
250 } | |
251 | |
252 void MemoryPressureMonitor::ScheduleTaskLocked(base::TimeTicks when) { | |
253 lock_.AssertAcquired(); | |
254 int serial = ++serial_number_; | |
255 | |
256 // Handle overflow. For simplicity keep serial numbers positive. 2^31 leaves | |
257 // room for 20 years of uptime in the worst case scenario (poll every second, | |
258 // constantly swinging through all the pressure levels). But you know... | |
259 // better safe the sorry! | |
260 if (serial < 0) { | |
261 serial_number_ = 1; | |
262 serial = 1; | |
263 } | |
264 | |
265 // It's entirely possible for |when| to be in the past relative to |now|, so | |
266 // bound |delay| from below. | |
267 base::TimeTicks now = tick_clock_->NowTicks(); | |
268 base::TimeDelta delay; // Initializes to zero. | |
269 if (when > now) | |
270 delay = when - now; | |
271 | |
272 // Schedule another check. | |
273 if (task_runner_->PostDelayedTask( | |
274 FROM_HERE, | |
275 base::Bind(&MemoryPressureMonitor::CheckPressureAndUpdateStats, | |
276 weak_ptr_factory_.GetWeakPtr(), serial), | |
277 delay)) { | |
278 // If the task will run then add it to the map of scheduled checks. | |
279 scheduled_checks_[serial] = when; | |
280 } | |
281 } | |
282 | |
283 } // namespace memory_pressure | |
OLD | NEW |