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