Index: athena/resource_manager/resource_manager_impl.cc |
diff --git a/athena/resource_manager/resource_manager_impl.cc b/athena/resource_manager/resource_manager_impl.cc |
index 74e5baf75329a2c6fa703856de34e782fb82223a..42351a0a6121fc75105487f56eb478a1218b9a2b 100644 |
--- a/athena/resource_manager/resource_manager_impl.cc |
+++ b/athena/resource_manager/resource_manager_impl.cc |
@@ -17,13 +17,15 @@ |
#include "base/logging.h" |
#include "base/memory/scoped_ptr.h" |
#include "ui/aura/window.h" |
+#include "ui/aura/window_observer.h" |
namespace athena { |
class ResourceManagerImpl : public ResourceManager, |
public WindowManagerObserver, |
public ActivityManagerObserver, |
- public MemoryPressureObserver { |
+ public MemoryPressureObserver, |
+ public aura::WindowObserver { |
public: |
ResourceManagerImpl(ResourceManagerDelegate* delegate); |
virtual ~ResourceManagerImpl(); |
@@ -31,6 +33,20 @@ class ResourceManagerImpl : public ResourceManager, |
// ResourceManager: |
virtual void SetMemoryPressureAndStopMonitoring( |
MemoryPressureObserver::MemoryPressure pressure) OVERRIDE; |
+ virtual void Pause(bool pause) OVERRIDE { |
+ if (pause) { |
+ if (!pause_) |
+ queued_command_ = false; |
+ ++pause_; |
+ } else { |
+ DCHECK(pause_); |
+ --pause_; |
+ if (!pause && queued_command_) { |
+ UpdateActivityOrder(); |
+ ManageResource(); |
+ } |
+ } |
+ } |
// ActivityManagerObserver: |
virtual void OnActivityStarted(Activity* activity) OVERRIDE; |
@@ -39,13 +55,17 @@ class ResourceManagerImpl : public ResourceManager, |
// WindowManagerObserver: |
virtual void OnOverviewModeEnter() OVERRIDE; |
virtual void OnOverviewModeExit() OVERRIDE; |
- virtual void OnActivityOrderHasChanged() OVERRIDE; |
+ virtual void OnSplitViewModeEnter() OVERRIDE; |
+ virtual void OnSplitViewModeExit() OVERRIDE; |
// MemoryPressureObserver: |
virtual void OnMemoryPressure( |
MemoryPressureObserver::MemoryPressure pressure) OVERRIDE; |
virtual ResourceManagerDelegate* GetDelegate() OVERRIDE; |
+ // aura::WindowObserver: |
+ virtual void OnWindowStackingChanged(aura::Window* window) OVERRIDE; |
+ |
private: |
// Manage the resources for our activities. |
void ManageResource(); |
@@ -69,17 +89,44 @@ class ResourceManagerImpl : public ResourceManager, |
// The memory pressure notifier. |
scoped_ptr<MemoryPressureNotifier> memory_pressure_notifier_; |
+ // A ref counter. As long as not 0, the management is on hold. |
+ int pause_; |
+ |
+ // If true, a command came in while the resource manager was paused. |
+ bool queued_command_; |
+ |
+ // Used by ManageResource() to determine an activity state change while it |
+ // changes Activity properties. |
+ bool activity_order_changed_; |
+ |
+ // True if in overview mode - activity order changes will be ignored if true |
+ // and postponed till after the overview mode is ending. |
+ bool in_overview_mode_; |
+ |
+ // True if we are in split view mode. |
+ bool in_splitview_mode_; |
+ |
DISALLOW_COPY_AND_ASSIGN(ResourceManagerImpl); |
}; |
namespace { |
ResourceManagerImpl* instance = NULL; |
+ |
+// We allow this many activities to be visible. All others must be at state of |
+// invisible or below. |
+const int kMaxVisibleActivities = 3; |
+ |
} // namespace |
ResourceManagerImpl::ResourceManagerImpl(ResourceManagerDelegate* delegate) |
: delegate_(delegate), |
current_memory_pressure_(MemoryPressureObserver::MEMORY_PRESSURE_UNKNOWN), |
- memory_pressure_notifier_(new MemoryPressureNotifier(this)) { |
+ memory_pressure_notifier_(new MemoryPressureNotifier(this)), |
+ pause_(false), |
+ queued_command_(false), |
+ activity_order_changed_(false), |
+ in_overview_mode_(false), |
+ in_splitview_mode_(false) { |
WindowManager::GetInstance()->AddObserver(this); |
ActivityManager::Get()->AddObserver(this); |
} |
@@ -87,6 +134,9 @@ ResourceManagerImpl::ResourceManagerImpl(ResourceManagerDelegate* delegate) |
ResourceManagerImpl::~ResourceManagerImpl() { |
ActivityManager::Get()->RemoveObserver(this); |
WindowManager::GetInstance()->RemoveObserver(this); |
+ |
+ while (!activity_list_.empty()) |
+ OnActivityEnding(activity_list_.front()); |
} |
void ResourceManagerImpl::SetMemoryPressureAndStopMonitoring( |
@@ -102,28 +152,58 @@ void ResourceManagerImpl::OnActivityStarted(Activity* activity) { |
UpdateActivityOrder(); |
// Update the activity states. |
ManageResource(); |
+ // Remember that the activity order has changed. |
+ activity_order_changed_ = true; |
+ activity->GetWindow()->AddObserver(this); |
} |
void ResourceManagerImpl::OnActivityEnding(Activity* activity) { |
+ DCHECK(activity->GetWindow()); |
+ activity->GetWindow()->RemoveObserver(this); |
// Remove the activity from the list again. |
std::vector<Activity*>::iterator it = |
std::find(activity_list_.begin(), activity_list_.end(), activity); |
DCHECK(it != activity_list_.end()); |
activity_list_.erase(it); |
+ // Remember that the activity order has changed. |
+ activity_order_changed_ = true; |
} |
void ResourceManagerImpl::OnOverviewModeEnter() { |
- // Nothing to do here. |
+ in_overview_mode_ = true; |
} |
void ResourceManagerImpl::OnOverviewModeExit() { |
- // Nothing to do here. |
+ in_overview_mode_ = false; |
+ // Reorder the activities. |
+ UpdateActivityOrder(); |
+} |
+ |
+void ResourceManagerImpl::OnSplitViewModeEnter() { |
+ // Re-apply the memory pressure to make sure enough items are visible. |
+ in_splitview_mode_ = true; |
+ ManageResource(); |
} |
-void ResourceManagerImpl::OnActivityOrderHasChanged() { |
+ |
+void ResourceManagerImpl::OnSplitViewModeExit() { |
+ // We don't do immediately something yet. The next ManageResource call will |
+ // come soon. |
+ in_splitview_mode_ = false; |
+} |
+ |
+void ResourceManagerImpl::OnWindowStackingChanged(aura::Window* window) { |
+ // TODO(skuhne): This needs to be changed to some WindowListProvider observer |
+ // if we decouple window order from activity order. |
+ |
+ // No need to do anything while being in overview mode. |
+ if (in_overview_mode_) |
+ return; |
+ |
// As long as we have to manage the list of activities ourselves, we need to |
// order it here. |
UpdateActivityOrder(); |
+ |
// Manage the resources of each activity. |
ManageResource(); |
} |
@@ -142,6 +222,12 @@ void ResourceManagerImpl::ManageResource() { |
// If there is none or only one app running we cannot do anything. |
if (activity_list_.size() <= 1U) |
return; |
+ |
+ if (pause_) { |
+ queued_command_ = true; |
+ return; |
+ } |
+ |
// TODO(skuhne): This algorithm needs to take all kinds of predictive analysis |
// and running applications into account. For this first patch we only do a |
// very simple "floating window" algorithm which is surely not good enough. |
@@ -161,16 +247,69 @@ void ResourceManagerImpl::ManageResource() { |
max_running_activities = 7; |
break; |
case MEMORY_PRESSURE_LOW: |
- // No need to do anything yet. |
- return; |
+ // This doesn't really matter. We do not change anything but turning |
+ // activities visible. |
+ max_running_activities = 10000; |
+ break; |
+ } |
+ |
+ // The first n activities should be trated as "visible", means they updated |
+ // in overview mode and will keep their layer resources for faster switch |
+ // times. Usually we use |kMaxVisibleActivities| items, but when the memory |
+ // pressure gets critical we only hold as many as are really visible. |
+ size_t max_activities = kMaxVisibleActivities; |
+ if (current_memory_pressure_ == MEMORY_PRESSURE_CRITICAL) |
+ max_activities = 1 + (in_splitview_mode_ ? 1 : 0); |
+ |
+ // Restart and / or bail if the order of activities changes due to our calls. |
+ activity_order_changed_ = false; |
+ |
+ // Change the visibility of our activities in a pre-processing step. This is |
+ // required since it might change the order/number of activities. |
+ size_t index = 0; |
+ while (index < activity_list_.size()) { |
+ Activity* activity = activity_list_[index]; |
+ Activity::ActivityState state = activity->GetCurrentState(); |
+ |
+ // The first |kMaxVisibleActivities| entries should be visible, all others |
+ // invisible or at a lower activity state. |
+ if (index < max_activities || |
+ (state == Activity::ACTIVITY_INVISIBLE || |
+ state == Activity::ACTIVITY_VISIBLE)) { |
+ Activity::ActivityState visiblity_state = |
+ index < max_activities ? Activity::ACTIVITY_VISIBLE : |
+ Activity::ACTIVITY_INVISIBLE; |
+ // Only change the state when it changes. Note that when the memory |
+ // pressure is critical, only the primary activities (1 or 2) are made |
+ // visible. Furthermore, in relaxed mode we only want to make visible. |
+ if (visiblity_state != state && |
+ (current_memory_pressure_ != MEMORY_PRESSURE_LOW || |
+ visiblity_state == Activity::ACTIVITY_VISIBLE)) |
+ activity->SetCurrentState(visiblity_state); |
+ } |
+ |
+ // See which index we should handle next. |
+ if (activity_order_changed_) { |
+ activity_order_changed_ = false; |
+ index = 0; |
+ } else { |
+ ++index; |
+ } |
} |
+ |
+ // No need to remove anything. |
+ if (current_memory_pressure_ == MEMORY_PRESSURE_LOW) |
+ return; |
+ |
+ // Check if/which activity we want to unload. |
Activity* oldest_media_activity = NULL; |
std::vector<Activity*> unloadable_activities; |
for (std::vector<Activity*>::iterator it = activity_list_.begin(); |
it != activity_list_.end(); ++it) { |
- // The activity should not be unloaded or visible. |
- if ((*it)->GetCurrentState() != Activity::ACTIVITY_UNLOADED && |
- !(*it)->IsVisible()) { |
+ Activity::ActivityState state = (*it)->GetCurrentState(); |
+ // The activity should neither be unloaded nor visible. |
+ if (state != Activity::ACTIVITY_UNLOADED && |
+ state != Activity::ACTIVITY_VISIBLE) { |
if ((*it)->GetMediaState() == Activity::ACTIVITY_MEDIA_STATE_NONE) { |
// Does not play media - so we can unload this immediately. |
unloadable_activities.push_back(*it); |
@@ -179,17 +318,20 @@ void ResourceManagerImpl::ManageResource() { |
} |
} |
} |
+ |
if (unloadable_activities.size() > max_running_activities) { |
unloadable_activities.back()->SetCurrentState(Activity::ACTIVITY_UNLOADED); |
return; |
} else if (current_memory_pressure_ == MEMORY_PRESSURE_CRITICAL) { |
if (oldest_media_activity) { |
oldest_media_activity->SetCurrentState(Activity::ACTIVITY_UNLOADED); |
+ LOG(WARNING) << "Unloading item to releave critical memory pressure"; |
return; |
} |
LOG(ERROR) << "[ResourceManager]: Single activity uses too much memory."; |
return; |
} |
+ |
if (current_memory_pressure_ != MEMORY_PRESSURE_UNKNOWN) { |
// Only show this warning when the memory pressure is actually known. This |
// will suppress warnings in e.g. unit tests. |
@@ -203,14 +345,16 @@ void ResourceManagerImpl::ManageResource() { |
} |
void ResourceManagerImpl::UpdateActivityOrder() { |
+ queued_command_ = true; |
if (activity_list_.empty()) |
return; |
std::vector<Activity*> new_activity_list; |
const aura::Window::Windows children = |
activity_list_[0]->GetWindow()->parent()->children(); |
// Find the first window in the container which is part of the application. |
- for (aura::Window::Windows::const_iterator child_iterator = children.begin(); |
- child_iterator != children.end(); ++child_iterator) { |
+ for (aura::Window::Windows::const_reverse_iterator child_iterator = |
+ children.rbegin(); |
+ child_iterator != children.rend(); ++child_iterator) { |
for (std::vector<Activity*>::iterator activity_iterator = |
activity_list_.begin(); |
activity_iterator != activity_list_.end(); ++activity_iterator) { |
@@ -224,6 +368,9 @@ void ResourceManagerImpl::UpdateActivityOrder() { |
// At this point the old list should be empty and we can swap the lists. |
DCHECK(!activity_list_.size()); |
activity_list_ = new_activity_list; |
+ |
+ // Remember that the activity order has changed. |
+ activity_order_changed_ = true; |
} |
// static |