• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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;
SetWaitTimeBetweenResourceManageCalls(int time_in_ms)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 
Pause(bool pause)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 
ResourceManagerImpl(ResourceManagerDelegate * delegate)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 
~ResourceManagerImpl()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 
SetMemoryPressureAndStopMonitoring(MemoryPressureObserver::MemoryPressure pressure)185 void ResourceManagerImpl::SetMemoryPressureAndStopMonitoring(
186     MemoryPressureObserver::MemoryPressure pressure) {
187   memory_pressure_notifier_->StopObserving();
188   OnMemoryPressure(pressure);
189 }
190 
OnActivityStarted(Activity * activity)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 
OnActivityEnding(Activity * activity)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 
OnOverviewModeEnter()213 void ResourceManagerImpl::OnOverviewModeEnter() {
214   in_overview_mode_ = true;
215 }
216 
OnOverviewModeExit()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 
OnSplitViewModeEnter()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 
OnSplitViewModeExit()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 
OnWindowStackingChanged()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 
OnWindowRemoved(aura::Window * removed_window,int index)257 void ResourceManagerImpl::OnWindowRemoved(aura::Window* removed_window,
258                                           int index) {
259 }
260 
OnMemoryPressure(MemoryPressureObserver::MemoryPressure pressure)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 
GetDelegate()269 ResourceManagerDelegate* ResourceManagerImpl::GetDelegate() {
270   return delegate_.get();
271 }
272 
ManageResource()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 
UpdateVisibilityStates()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 
TryToUnloadAnActivity()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 
UpdateActivityOrder()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 
OnResourcesReleased()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 
OnMemoryPressureIncreased()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 
AllowedToUnloadActivity()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
Create()466 void ResourceManager::Create() {
467   DCHECK(!instance);
468   instance = new ResourceManagerImpl(
469       ResourceManagerDelegate::CreateResourceManagerDelegate());
470 }
471 
472 // static
Get()473 ResourceManager* ResourceManager::Get() {
474   return instance;
475 }
476 
477 // static
Shutdown()478 void ResourceManager::Shutdown() {
479   DCHECK(instance);
480   delete instance;
481   instance = NULL;
482 }
483 
ResourceManager()484 ResourceManager::ResourceManager() {}
485 
~ResourceManager()486 ResourceManager::~ResourceManager() {
487   DCHECK(instance);
488   instance = NULL;
489 }
490 
491 }  // namespace athena
492