OLD | NEW |
| (Empty) |
1 // Copyright 2014 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 "athena/resource_manager/public/resource_manager.h" | |
6 | |
7 #include <algorithm> | |
8 #include <vector> | |
9 | |
10 #include "athena/activity/public/activity.h" | |
11 #include "athena/activity/public/activity_manager.h" | |
12 #include "athena/activity/public/activity_manager_observer.h" | |
13 #include "athena/resource_manager/memory_pressure_notifier.h" | |
14 #include "athena/resource_manager/public/resource_manager_delegate.h" | |
15 #include "athena/wm/public/window_list_provider.h" | |
16 #include "athena/wm/public/window_list_provider_observer.h" | |
17 #include "athena/wm/public/window_manager.h" | |
18 #include "athena/wm/public/window_manager_observer.h" | |
19 #include "base/containers/adapters.h" | |
20 #include "base/logging.h" | |
21 #include "base/memory/scoped_ptr.h" | |
22 #include "base/time/time.h" | |
23 #include "ui/aura/window.h" | |
24 | |
25 namespace athena { | |
26 namespace { | |
27 | |
28 class ResourceManagerImpl : public ResourceManager, | |
29 public WindowManagerObserver, | |
30 public ActivityManagerObserver, | |
31 public MemoryPressureObserver, | |
32 public WindowListProviderObserver { | |
33 public: | |
34 ResourceManagerImpl(ResourceManagerDelegate* delegate); | |
35 ~ResourceManagerImpl() override; | |
36 | |
37 // ResourceManager: | |
38 virtual void SetMemoryPressureAndStopMonitoring( | |
39 MemoryPressure pressure) override; | |
40 virtual void SetWaitTimeBetweenResourceManageCalls(int time_in_ms) override { | |
41 wait_time_for_resource_deallocation_ = | |
42 base::TimeDelta::FromMilliseconds(time_in_ms); | |
43 // Reset the timeout to force the next resource call to execute immediately. | |
44 next_resource_management_time_ = base::Time::Now(); | |
45 } | |
46 | |
47 virtual void Pause(bool pause) override { | |
48 if (pause) { | |
49 if (!pause_) | |
50 queued_command_ = false; | |
51 ++pause_; | |
52 } else { | |
53 DCHECK(pause_); | |
54 --pause_; | |
55 if (!pause && queued_command_) | |
56 ManageResource(); | |
57 } | |
58 } | |
59 | |
60 // ActivityManagerObserver: | |
61 virtual void OnActivityStarted(Activity* activity) override; | |
62 virtual void OnActivityEnding(Activity* activity) override; | |
63 virtual void OnActivityOrderChanged() override; | |
64 | |
65 // WindowManagerObserver: | |
66 virtual void OnOverviewModeEnter() override; | |
67 virtual void OnOverviewModeExit() override; | |
68 virtual void OnSplitViewModeEnter() override; | |
69 virtual void OnSplitViewModeExit() override; | |
70 | |
71 // MemoryPressureObserver: | |
72 virtual void OnMemoryPressure(MemoryPressure pressure) override; | |
73 virtual ResourceManagerDelegate* GetDelegate() override; | |
74 | |
75 // WindowListProviderObserver: | |
76 virtual void OnWindowStackingChangedInList() override; | |
77 virtual void OnWindowAddedToList(aura::Window* added_window) override {} | |
78 virtual void OnWindowRemovedFromList(aura::Window* removed_window, | |
79 int index) override {} | |
80 | |
81 private: | |
82 // Manage the resources for our activities. | |
83 void ManageResource(); | |
84 | |
85 // Check that the visibility of activities is properly set. | |
86 void UpdateVisibilityStates(); | |
87 | |
88 // Check if activities can be unloaded to reduce memory pressure. | |
89 void TryToUnloadAnActivity(); | |
90 | |
91 // Resources were released and a quiet period is needed before we release | |
92 // more since it takes a while to trickle through the system. | |
93 void OnResourcesReleased(); | |
94 | |
95 // The memory pressure has increased, previously applied measures did not show | |
96 // effect and immediate action is required. | |
97 void OnMemoryPressureIncreased(); | |
98 | |
99 // Returns true when the previous memory release was long enough ago to try | |
100 // unloading another activity. | |
101 bool AllowedToUnloadActivity(); | |
102 | |
103 // The resource manager delegate. | |
104 scoped_ptr<ResourceManagerDelegate> delegate_; | |
105 | |
106 // Keeping a reference to the current memory pressure. | |
107 MemoryPressure current_memory_pressure_; | |
108 | |
109 // The memory pressure notifier. | |
110 scoped_ptr<MemoryPressureNotifier> memory_pressure_notifier_; | |
111 | |
112 // A ref counter. As long as not 0, the management is on hold. | |
113 int pause_; | |
114 | |
115 // If true, a command came in while the resource manager was paused. | |
116 bool queued_command_; | |
117 | |
118 // Used by ManageResource() to determine an activity state change while it | |
119 // changes Activity properties. | |
120 bool activity_order_changed_; | |
121 | |
122 // True if in overview mode - activity order changes will be ignored if true | |
123 // and postponed till after the overview mode is ending. | |
124 bool in_overview_mode_; | |
125 | |
126 // True if we are in split view mode. | |
127 bool in_split_view_mode_; | |
128 | |
129 // The last time the resource manager was called to release resources. | |
130 // Avoid too aggressive resource de-allocation by enforcing a wait time of | |
131 // |wait_time_for_resource_deallocation_| between executed calls. | |
132 base::Time next_resource_management_time_; | |
133 | |
134 // The wait time between two resource managing executions. | |
135 base::TimeDelta wait_time_for_resource_deallocation_; | |
136 | |
137 DISALLOW_COPY_AND_ASSIGN(ResourceManagerImpl); | |
138 }; | |
139 | |
140 namespace { | |
141 ResourceManagerImpl* instance = nullptr; | |
142 | |
143 // We allow this many activities to be visible. All others must be at state of | |
144 // invisible or below. | |
145 const int kMaxVisibleActivities = 3; | |
146 | |
147 } // namespace | |
148 | |
149 ResourceManagerImpl::ResourceManagerImpl(ResourceManagerDelegate* delegate) | |
150 : delegate_(delegate), | |
151 current_memory_pressure_(MEMORY_PRESSURE_UNKNOWN), | |
152 memory_pressure_notifier_(new MemoryPressureNotifier(this)), | |
153 pause_(false), | |
154 queued_command_(false), | |
155 activity_order_changed_(false), | |
156 in_overview_mode_(false), | |
157 in_split_view_mode_(false), | |
158 next_resource_management_time_(base::Time::Now()), | |
159 wait_time_for_resource_deallocation_(base::TimeDelta::FromMilliseconds( | |
160 delegate_->MemoryPressureIntervalInMS())) { | |
161 WindowManager::Get()->AddObserver(this); | |
162 WindowManager::Get()->GetWindowListProvider()->AddObserver(this); | |
163 ActivityManager::Get()->AddObserver(this); | |
164 } | |
165 | |
166 ResourceManagerImpl::~ResourceManagerImpl() { | |
167 ActivityManager::Get()->RemoveObserver(this); | |
168 WindowManager::Get()->GetWindowListProvider()->RemoveObserver(this); | |
169 WindowManager::Get()->RemoveObserver(this); | |
170 } | |
171 | |
172 void ResourceManagerImpl::SetMemoryPressureAndStopMonitoring( | |
173 MemoryPressure pressure) { | |
174 memory_pressure_notifier_->StopObserving(); | |
175 OnMemoryPressure(pressure); | |
176 } | |
177 | |
178 void ResourceManagerImpl::OnActivityStarted(Activity* activity) { | |
179 // Update the activity states. | |
180 ManageResource(); | |
181 activity_order_changed_ = true; | |
182 } | |
183 | |
184 void ResourceManagerImpl::OnActivityEnding(Activity* activity) { | |
185 activity_order_changed_ = true; | |
186 } | |
187 | |
188 void ResourceManagerImpl::OnActivityOrderChanged() { | |
189 activity_order_changed_ = true; | |
190 } | |
191 | |
192 void ResourceManagerImpl::OnOverviewModeEnter() { | |
193 in_overview_mode_ = true; | |
194 } | |
195 | |
196 void ResourceManagerImpl::OnOverviewModeExit() { | |
197 in_overview_mode_ = false; | |
198 ManageResource(); | |
199 } | |
200 | |
201 void ResourceManagerImpl::OnSplitViewModeEnter() { | |
202 // Re-apply the memory pressure to make sure enough items are visible. | |
203 in_split_view_mode_ = true; | |
204 ManageResource(); | |
205 } | |
206 | |
207 | |
208 void ResourceManagerImpl::OnSplitViewModeExit() { | |
209 // We don't do immediately something yet. The next ManageResource call will | |
210 // come soon. | |
211 in_split_view_mode_ = false; | |
212 } | |
213 | |
214 void ResourceManagerImpl::OnWindowStackingChangedInList() { | |
215 if (pause_) { | |
216 queued_command_ = true; | |
217 return; | |
218 } | |
219 | |
220 // No need to do anything while being in overview mode. | |
221 if (in_overview_mode_) | |
222 return; | |
223 | |
224 // Manage the resources of each activity. | |
225 ManageResource(); | |
226 } | |
227 | |
228 void ResourceManagerImpl::OnMemoryPressure(MemoryPressure pressure) { | |
229 if (pressure > current_memory_pressure_) | |
230 OnMemoryPressureIncreased(); | |
231 current_memory_pressure_ = pressure; | |
232 ManageResource(); | |
233 } | |
234 | |
235 ResourceManagerDelegate* ResourceManagerImpl::GetDelegate() { | |
236 return delegate_.get(); | |
237 } | |
238 | |
239 void ResourceManagerImpl::ManageResource() { | |
240 // If there is none or only one app running we cannot do anything. | |
241 if (ActivityManager::Get()->GetActivityList().size() <= 1U) | |
242 return; | |
243 | |
244 if (pause_) { | |
245 queued_command_ = true; | |
246 return; | |
247 } | |
248 | |
249 // Check that the visibility of items is properly set. Note that this might | |
250 // already trigger a release of resources. If this happens, | |
251 // AllowedToUnloadActivity() will return false. | |
252 UpdateVisibilityStates(); | |
253 | |
254 // Since resource deallocation takes time, we avoid to release more resources | |
255 // in short succession. Note that we come here periodically and if one call | |
256 // is not triggering an unload, the next one will. | |
257 if (AllowedToUnloadActivity()) | |
258 TryToUnloadAnActivity(); | |
259 } | |
260 | |
261 void ResourceManagerImpl::UpdateVisibilityStates() { | |
262 // The first n activities should be treated as "visible", means they updated | |
263 // in overview mode and will keep their layer resources for faster switch | |
264 // times. Usually we use |kMaxVisibleActivities| items, but when the memory | |
265 // pressure gets critical we only hold as many as are really visible. | |
266 size_t max_activities = kMaxVisibleActivities; | |
267 if (current_memory_pressure_ == MEMORY_PRESSURE_CRITICAL) | |
268 max_activities = in_split_view_mode_ ? 2 : 1; | |
269 | |
270 do { | |
271 activity_order_changed_ = false; | |
272 | |
273 // Change the visibility of our activities in a pre-processing step. This is | |
274 // required since it might change the order/number of activities. | |
275 // Note: We cannot use the order of items in the ActivityManager since it | |
276 // does not follow the order of windows. | |
277 size_t count = 0; | |
278 const aura::Window::Windows& windows = | |
279 WindowManager::Get()->GetWindowListProvider()->GetWindowList(); | |
280 for (aura::Window::Windows::const_reverse_iterator it = windows.rbegin(); | |
281 it != windows.rend(); it++) { | |
282 Activity* activity = ActivityManager::Get()->GetActivityForWindow(*it); | |
283 // It is possible that the window was not yet added to the ActivityManager | |
284 // in which case we are not interested in managing it yet. | |
285 if (!activity) | |
286 continue; | |
287 Activity::ActivityState state = activity->GetCurrentState(); | |
288 | |
289 // The first |kMaxVisibleActivities| entries should be visible, all others | |
290 // invisible or at a lower activity state. | |
291 if (count < max_activities || | |
292 (state == Activity::ACTIVITY_INVISIBLE || | |
293 state == Activity::ACTIVITY_VISIBLE)) { | |
294 Activity::ActivityState visiblity_state = | |
295 count < max_activities ? Activity::ACTIVITY_VISIBLE : | |
296 Activity::ACTIVITY_INVISIBLE; | |
297 // Only change the state when it changes. Note that when the memory | |
298 // pressure is critical, only the primary activities (1 or 2) are made | |
299 // visible. Furthermore, in relaxed mode we only want to turn visible, | |
300 // never invisible. | |
301 if (visiblity_state != state && | |
302 (current_memory_pressure_ != MEMORY_PRESSURE_LOW || | |
303 visiblity_state == Activity::ACTIVITY_VISIBLE)) { | |
304 activity->SetCurrentState(visiblity_state); | |
305 // If we turned an activity invisible, we are already releasing memory | |
306 // and can hold off releasing more for now. | |
307 if (visiblity_state == Activity::ACTIVITY_INVISIBLE) | |
308 OnResourcesReleased(); | |
309 } | |
310 } | |
311 | |
312 // See which count we should handle next. | |
313 if (activity_order_changed_) | |
314 break; | |
315 ++count; | |
316 } | |
317 // If we stopped iterating over the list of activities because of the change | |
318 // in ordering, then restart processing the activities from the beginning. | |
319 } while (activity_order_changed_); | |
320 } | |
321 | |
322 void ResourceManagerImpl::TryToUnloadAnActivity() { | |
323 // TODO(skuhne): This algorithm needs to take all kinds of predictive analysis | |
324 // and running applications into account. For this first patch we only do a | |
325 // very simple "floating window" algorithm which is surely not good enough. | |
326 size_t max_running_activities = 5; | |
327 switch (current_memory_pressure_) { | |
328 case MEMORY_PRESSURE_UNKNOWN: | |
329 // If we do not know how much memory we have we assume that it must be a | |
330 // high consumption. | |
331 // Fallthrough. | |
332 case MEMORY_PRESSURE_HIGH: | |
333 max_running_activities = 5; | |
334 break; | |
335 case MEMORY_PRESSURE_CRITICAL: | |
336 max_running_activities = 0; | |
337 break; | |
338 case MEMORY_PRESSURE_MODERATE: | |
339 max_running_activities = 7; | |
340 break; | |
341 case MEMORY_PRESSURE_LOW: | |
342 NOTREACHED(); | |
343 return; | |
344 } | |
345 | |
346 // Check if / which activity we want to unload. | |
347 Activity* oldest_media_activity = nullptr; | |
348 Activity* oldest_unloadable_activity = nullptr; | |
349 size_t unloadable_activity_count = 0; | |
350 const ActivityList& activity_list = ActivityManager::Get()->GetActivityList(); | |
351 for (Activity* activity : activity_list) { | |
352 Activity::ActivityState state = activity->GetCurrentState(); | |
353 // The activity should neither be unloaded nor visible. | |
354 if (state != Activity::ACTIVITY_UNLOADED && | |
355 state != Activity::ACTIVITY_VISIBLE) { | |
356 if (activity->GetMediaState() == Activity::ACTIVITY_MEDIA_STATE_NONE) { | |
357 // Does not play media - so we can unload this immediately. | |
358 ++unloadable_activity_count; | |
359 oldest_unloadable_activity = activity; | |
360 } else { | |
361 oldest_media_activity = activity; | |
362 } | |
363 } | |
364 } | |
365 | |
366 if (unloadable_activity_count > max_running_activities) { | |
367 CHECK(oldest_unloadable_activity); | |
368 OnResourcesReleased(); | |
369 oldest_unloadable_activity->SetCurrentState(Activity::ACTIVITY_UNLOADED); | |
370 return; | |
371 } else if (current_memory_pressure_ == MEMORY_PRESSURE_CRITICAL) { | |
372 if (oldest_media_activity) { | |
373 OnResourcesReleased(); | |
374 oldest_media_activity->SetCurrentState(Activity::ACTIVITY_UNLOADED); | |
375 LOG(WARNING) << "Unloading item to releave critical memory pressure"; | |
376 return; | |
377 } | |
378 LOG(ERROR) << "[ResourceManager]: Single activity uses too much memory."; | |
379 return; | |
380 } | |
381 | |
382 if (current_memory_pressure_ != MEMORY_PRESSURE_UNKNOWN) { | |
383 // Only show this warning when the memory pressure is actually known. This | |
384 // will suppress warnings in e.g. unit tests. | |
385 LOG(WARNING) << "[ResourceManager]: No way to release memory pressure (" << | |
386 current_memory_pressure_ << | |
387 "), Activities (running, allowed, unloadable)=(" << | |
388 activity_list.size() << ", " << | |
389 max_running_activities << ", " << | |
390 unloadable_activity_count << ")"; | |
391 } | |
392 } | |
393 | |
394 void ResourceManagerImpl::OnResourcesReleased() { | |
395 // Do not release too many activities in short succession since it takes time | |
396 // to release resources. As such wait the memory pressure interval before the | |
397 // next call. | |
398 next_resource_management_time_ = base::Time::Now() + | |
399 wait_time_for_resource_deallocation_; | |
400 } | |
401 | |
402 void ResourceManagerImpl::OnMemoryPressureIncreased() { | |
403 // By setting the timer to Now, the next call will immediately be performed. | |
404 next_resource_management_time_ = base::Time::Now(); | |
405 } | |
406 | |
407 bool ResourceManagerImpl::AllowedToUnloadActivity() { | |
408 return current_memory_pressure_ != MEMORY_PRESSURE_LOW && | |
409 base::Time::Now() >= next_resource_management_time_; | |
410 } | |
411 | |
412 } // namespace | |
413 | |
414 // static | |
415 void ResourceManager::Create() { | |
416 DCHECK(!instance); | |
417 instance = new ResourceManagerImpl( | |
418 ResourceManagerDelegate::CreateResourceManagerDelegate()); | |
419 } | |
420 | |
421 // static | |
422 ResourceManager* ResourceManager::Get() { | |
423 return instance; | |
424 } | |
425 | |
426 // static | |
427 void ResourceManager::Shutdown() { | |
428 DCHECK(instance); | |
429 delete instance; | |
430 instance = nullptr; | |
431 } | |
432 | |
433 ResourceManager::ResourceManager() {} | |
434 | |
435 ResourceManager::~ResourceManager() { | |
436 DCHECK(instance); | |
437 instance = nullptr; | |
438 } | |
439 | |
440 } // namespace athena | |
OLD | NEW |