Revert of Revert "Temporarily disabling WebRTC tests on android." (patchset #1 id...
[chromium-blink-merge.git] / athena / resource_manager / resource_manager_impl.cc
blob74a8aca5e7bb73e9658725380d34a4a74c313987
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/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"
25 namespace athena {
26 namespace {
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;
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();
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();
60 // ActivityManagerObserver:
61 virtual void OnActivityStarted(Activity* activity) override;
62 virtual void OnActivityEnding(Activity* activity) override;
63 virtual void OnActivityOrderChanged() 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 OnWindowStackingChangedInList() override;
77 virtual void OnWindowAddedToList(aura::Window* added_window) override {}
78 virtual void OnWindowRemovedFromList(aura::Window* removed_window,
79 int index) override {}
81 private:
82 // Manage the resources for our activities.
83 void ManageResource();
85 // Check that the visibility of activities is properly set.
86 void UpdateVisibilityStates();
88 // Check if activities can be unloaded to reduce memory pressure.
89 void TryToUnloadAnActivity();
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();
95 // The memory pressure has increased, previously applied measures did not show
96 // effect and immediate action is required.
97 void OnMemoryPressureIncreased();
99 // Returns true when the previous memory release was long enough ago to try
100 // unloading another activity.
101 bool AllowedToUnloadActivity();
103 // The resource manager delegate.
104 scoped_ptr<ResourceManagerDelegate> delegate_;
106 // Keeping a reference to the current memory pressure.
107 MemoryPressure current_memory_pressure_;
109 // The memory pressure notifier.
110 scoped_ptr<MemoryPressureNotifier> memory_pressure_notifier_;
112 // A ref counter. As long as not 0, the management is on hold.
113 int pause_;
115 // If true, a command came in while the resource manager was paused.
116 bool queued_command_;
118 // Used by ManageResource() to determine an activity state change while it
119 // changes Activity properties.
120 bool activity_order_changed_;
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_;
126 // True if we are in split view mode.
127 bool in_split_view_mode_;
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_;
134 // The wait time between two resource managing executions.
135 base::TimeDelta wait_time_for_resource_deallocation_;
137 DISALLOW_COPY_AND_ASSIGN(ResourceManagerImpl);
140 namespace {
141 ResourceManagerImpl* instance = nullptr;
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;
147 } // namespace
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);
166 ResourceManagerImpl::~ResourceManagerImpl() {
167 ActivityManager::Get()->RemoveObserver(this);
168 WindowManager::Get()->GetWindowListProvider()->RemoveObserver(this);
169 WindowManager::Get()->RemoveObserver(this);
172 void ResourceManagerImpl::SetMemoryPressureAndStopMonitoring(
173 MemoryPressure pressure) {
174 memory_pressure_notifier_->StopObserving();
175 OnMemoryPressure(pressure);
178 void ResourceManagerImpl::OnActivityStarted(Activity* activity) {
179 // Update the activity states.
180 ManageResource();
181 activity_order_changed_ = true;
184 void ResourceManagerImpl::OnActivityEnding(Activity* activity) {
185 activity_order_changed_ = true;
188 void ResourceManagerImpl::OnActivityOrderChanged() {
189 activity_order_changed_ = true;
192 void ResourceManagerImpl::OnOverviewModeEnter() {
193 in_overview_mode_ = true;
196 void ResourceManagerImpl::OnOverviewModeExit() {
197 in_overview_mode_ = false;
198 ManageResource();
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();
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;
214 void ResourceManagerImpl::OnWindowStackingChangedInList() {
215 if (pause_) {
216 queued_command_ = true;
217 return;
220 // No need to do anything while being in overview mode.
221 if (in_overview_mode_)
222 return;
224 // Manage the resources of each activity.
225 ManageResource();
228 void ResourceManagerImpl::OnMemoryPressure(MemoryPressure pressure) {
229 if (pressure > current_memory_pressure_)
230 OnMemoryPressureIncreased();
231 current_memory_pressure_ = pressure;
232 ManageResource();
235 ResourceManagerDelegate* ResourceManagerImpl::GetDelegate() {
236 return delegate_.get();
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;
244 if (pause_) {
245 queued_command_ = true;
246 return;
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();
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();
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;
270 do {
271 activity_order_changed_ = false;
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 size_t count = 0;
276 for (Activity* activity : ActivityManager::Get()->GetActivityList()) {
277 Activity::ActivityState state = activity->GetCurrentState();
279 // The first |kMaxVisibleActivities| entries should be visible, all others
280 // invisible or at a lower activity state.
281 if (count < max_activities ||
282 (state == Activity::ACTIVITY_INVISIBLE ||
283 state == Activity::ACTIVITY_VISIBLE)) {
284 Activity::ActivityState visiblity_state =
285 count < max_activities ? Activity::ACTIVITY_VISIBLE :
286 Activity::ACTIVITY_INVISIBLE;
287 // Only change the state when it changes. Note that when the memory
288 // pressure is critical, only the primary activities (1 or 2) are made
289 // visible. Furthermore, in relaxed mode we only want to turn visible,
290 // never invisible.
291 if (visiblity_state != state &&
292 (current_memory_pressure_ != MEMORY_PRESSURE_LOW ||
293 visiblity_state == Activity::ACTIVITY_VISIBLE)) {
294 activity->SetCurrentState(visiblity_state);
295 // If we turned an activity invisible, we are already releasing memory
296 // and can hold off releasing more for now.
297 if (visiblity_state == Activity::ACTIVITY_INVISIBLE)
298 OnResourcesReleased();
302 // See which count we should handle next.
303 if (activity_order_changed_)
304 break;
305 ++count;
307 // If we stopped iterating over the list of activities because of the change
308 // in ordering, then restart processing the activities from the beginning.
309 } while (activity_order_changed_);
312 void ResourceManagerImpl::TryToUnloadAnActivity() {
313 // TODO(skuhne): This algorithm needs to take all kinds of predictive analysis
314 // and running applications into account. For this first patch we only do a
315 // very simple "floating window" algorithm which is surely not good enough.
316 size_t max_running_activities = 5;
317 switch (current_memory_pressure_) {
318 case MEMORY_PRESSURE_UNKNOWN:
319 // If we do not know how much memory we have we assume that it must be a
320 // high consumption.
321 // Fallthrough.
322 case MEMORY_PRESSURE_HIGH:
323 max_running_activities = 5;
324 break;
325 case MEMORY_PRESSURE_CRITICAL:
326 max_running_activities = 0;
327 break;
328 case MEMORY_PRESSURE_MODERATE:
329 max_running_activities = 7;
330 break;
331 case MEMORY_PRESSURE_LOW:
332 NOTREACHED();
333 return;
336 // Check if / which activity we want to unload.
337 Activity* oldest_media_activity = nullptr;
338 Activity* oldest_unloadable_activity = nullptr;
339 size_t unloadable_activity_count = 0;
340 const ActivityList& activity_list = ActivityManager::Get()->GetActivityList();
341 for (Activity* activity : activity_list) {
342 Activity::ActivityState state = activity->GetCurrentState();
343 // The activity should neither be unloaded nor visible.
344 if (state != Activity::ACTIVITY_UNLOADED &&
345 state != Activity::ACTIVITY_VISIBLE) {
346 if (activity->GetMediaState() == Activity::ACTIVITY_MEDIA_STATE_NONE) {
347 // Does not play media - so we can unload this immediately.
348 ++unloadable_activity_count;
349 oldest_unloadable_activity = activity;
350 } else {
351 oldest_media_activity = activity;
356 if (unloadable_activity_count > max_running_activities) {
357 CHECK(oldest_unloadable_activity);
358 OnResourcesReleased();
359 oldest_unloadable_activity->SetCurrentState(Activity::ACTIVITY_UNLOADED);
360 return;
361 } else if (current_memory_pressure_ == MEMORY_PRESSURE_CRITICAL) {
362 if (oldest_media_activity) {
363 OnResourcesReleased();
364 oldest_media_activity->SetCurrentState(Activity::ACTIVITY_UNLOADED);
365 LOG(WARNING) << "Unloading item to releave critical memory pressure";
366 return;
368 LOG(ERROR) << "[ResourceManager]: Single activity uses too much memory.";
369 return;
372 if (current_memory_pressure_ != MEMORY_PRESSURE_UNKNOWN) {
373 // Only show this warning when the memory pressure is actually known. This
374 // will suppress warnings in e.g. unit tests.
375 LOG(WARNING) << "[ResourceManager]: No way to release memory pressure (" <<
376 current_memory_pressure_ <<
377 "), Activities (running, allowed, unloadable)=(" <<
378 activity_list.size() << ", " <<
379 max_running_activities << ", " <<
380 unloadable_activity_count << ")";
384 void ResourceManagerImpl::OnResourcesReleased() {
385 // Do not release too many activities in short succession since it takes time
386 // to release resources. As such wait the memory pressure interval before the
387 // next call.
388 next_resource_management_time_ = base::Time::Now() +
389 wait_time_for_resource_deallocation_;
392 void ResourceManagerImpl::OnMemoryPressureIncreased() {
393 // By setting the timer to Now, the next call will immediately be performed.
394 next_resource_management_time_ = base::Time::Now();
397 bool ResourceManagerImpl::AllowedToUnloadActivity() {
398 return current_memory_pressure_ != MEMORY_PRESSURE_LOW &&
399 base::Time::Now() >= next_resource_management_time_;
402 } // namespace
404 // static
405 void ResourceManager::Create() {
406 DCHECK(!instance);
407 instance = new ResourceManagerImpl(
408 ResourceManagerDelegate::CreateResourceManagerDelegate());
411 // static
412 ResourceManager* ResourceManager::Get() {
413 return instance;
416 // static
417 void ResourceManager::Shutdown() {
418 DCHECK(instance);
419 delete instance;
420 instance = nullptr;
423 ResourceManager::ResourceManager() {}
425 ResourceManager::~ResourceManager() {
426 DCHECK(instance);
427 instance = nullptr;
430 } // namespace athena