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