Home | History | Annotate | Download | only in resource_manager
      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;
     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 
     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 
    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 
    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 
    185 void ResourceManagerImpl::SetMemoryPressureAndStopMonitoring(
    186     MemoryPressureObserver::MemoryPressure pressure) {
    187   memory_pressure_notifier_->StopObserving();
    188   OnMemoryPressure(pressure);
    189 }
    190 
    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 
    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 
    213 void ResourceManagerImpl::OnOverviewModeEnter() {
    214   in_overview_mode_ = true;
    215 }
    216 
    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 
    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 
    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 
    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 
    257 void ResourceManagerImpl::OnWindowRemoved(aura::Window* removed_window,
    258                                           int index) {
    259 }
    260 
    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 
    269 ResourceManagerDelegate* ResourceManagerImpl::GetDelegate() {
    270   return delegate_.get();
    271 }
    272 
    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 
    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 
    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 
    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 
    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 
    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 
    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
    466 void ResourceManager::Create() {
    467   DCHECK(!instance);
    468   instance = new ResourceManagerImpl(
    469       ResourceManagerDelegate::CreateResourceManagerDelegate());
    470 }
    471 
    472 // static
    473 ResourceManager* ResourceManager::Get() {
    474   return instance;
    475 }
    476 
    477 // static
    478 void ResourceManager::Shutdown() {
    479   DCHECK(instance);
    480   delete instance;
    481   instance = NULL;
    482 }
    483 
    484 ResourceManager::ResourceManager() {}
    485 
    486 ResourceManager::~ResourceManager() {
    487   DCHECK(instance);
    488   instance = NULL;
    489 }
    490 
    491 }  // namespace athena
    492