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"
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"
28 class ResourceManagerImpl
: public ResourceManager
,
29 public WindowManagerObserver
,
30 public ActivityManagerObserver
,
31 public MemoryPressureObserver
,
32 public WindowListProviderObserver
{
34 ResourceManagerImpl(ResourceManagerDelegate
* delegate
);
35 ~ResourceManagerImpl() override
;
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
{
50 queued_command_
= false;
55 if (!pause
&& queued_command_
)
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
{}
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.
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
);
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;
149 ResourceManagerImpl::ResourceManagerImpl(ResourceManagerDelegate
* delegate
)
150 : delegate_(delegate
),
151 current_memory_pressure_(MEMORY_PRESSURE_UNKNOWN
),
152 memory_pressure_notifier_(new MemoryPressureNotifier(this)),
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.
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;
201 void ResourceManagerImpl::OnSplitViewModeEnter() {
202 // Re-apply the memory pressure to make sure enough items are visible.
203 in_split_view_mode_
= true;
208 void ResourceManagerImpl::OnSplitViewModeExit() {
209 // We don't do immediately something yet. The next ManageResource call will
211 in_split_view_mode_
= false;
214 void ResourceManagerImpl::OnWindowStackingChangedInList() {
216 queued_command_
= true;
220 // No need to do anything while being in overview mode.
221 if (in_overview_mode_
)
224 // Manage the resources of each activity.
228 void ResourceManagerImpl::OnMemoryPressure(MemoryPressure pressure
) {
229 if (pressure
> current_memory_pressure_
)
230 OnMemoryPressureIncreased();
231 current_memory_pressure_
= pressure
;
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)
245 queued_command_
= true;
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;
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.
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,
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_
)
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
322 case MEMORY_PRESSURE_HIGH
:
323 max_running_activities
= 5;
325 case MEMORY_PRESSURE_CRITICAL
:
326 max_running_activities
= 0;
328 case MEMORY_PRESSURE_MODERATE
:
329 max_running_activities
= 7;
331 case MEMORY_PRESSURE_LOW
:
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
;
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
);
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";
368 LOG(ERROR
) << "[ResourceManager]: Single activity uses too much memory.";
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
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_
;
405 void ResourceManager::Create() {
407 instance
= new ResourceManagerImpl(
408 ResourceManagerDelegate::CreateResourceManagerDelegate());
412 ResourceManager
* ResourceManager::Get() {
417 void ResourceManager::Shutdown() {
423 ResourceManager::ResourceManager() {}
425 ResourceManager::~ResourceManager() {
430 } // namespace athena