74a8aca5e7bb73e9658725380d34a4a74c313987
[platform/framework/web/crosswalk.git] / src / athena / resource_manager / resource_manager_impl.cc
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/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"
24
25 namespace athena {
26 namespace {
27
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;
36
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();
45   }
46
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();
57     }
58   }
59
60   // ActivityManagerObserver:
61   virtual void OnActivityStarted(Activity* activity) override;
62   virtual void OnActivityEnding(Activity* activity) override;
63   virtual void OnActivityOrderChanged() 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(MemoryPressure pressure) override;
73   virtual ResourceManagerDelegate* GetDelegate() override;
74
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 {}
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   // 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();
94
95   // The memory pressure has increased, previously applied measures did not show
96   // effect and immediate action is required.
97   void OnMemoryPressureIncreased();
98
99   // Returns true when the previous memory release was long enough ago to try
100   // unloading another activity.
101   bool AllowedToUnloadActivity();
102
103   // The resource manager delegate.
104   scoped_ptr<ResourceManagerDelegate> delegate_;
105
106   // Keeping a reference to the current memory pressure.
107   MemoryPressure current_memory_pressure_;
108
109   // The memory pressure notifier.
110   scoped_ptr<MemoryPressureNotifier> memory_pressure_notifier_;
111
112   // A ref counter. As long as not 0, the management is on hold.
113   int pause_;
114
115   // If true, a command came in while the resource manager was paused.
116   bool queued_command_;
117
118   // Used by ManageResource() to determine an activity state change while it
119   // changes Activity properties.
120   bool activity_order_changed_;
121
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_;
125
126   // True if we are in split view mode.
127   bool in_split_view_mode_;
128
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_;
133
134   // The wait time between two resource managing executions.
135   base::TimeDelta wait_time_for_resource_deallocation_;
136
137   DISALLOW_COPY_AND_ASSIGN(ResourceManagerImpl);
138 };
139
140 namespace {
141 ResourceManagerImpl* instance = nullptr;
142
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;
146
147 }  // namespace
148
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);
164 }
165
166 ResourceManagerImpl::~ResourceManagerImpl() {
167   ActivityManager::Get()->RemoveObserver(this);
168   WindowManager::Get()->GetWindowListProvider()->RemoveObserver(this);
169   WindowManager::Get()->RemoveObserver(this);
170 }
171
172 void ResourceManagerImpl::SetMemoryPressureAndStopMonitoring(
173     MemoryPressure pressure) {
174   memory_pressure_notifier_->StopObserving();
175   OnMemoryPressure(pressure);
176 }
177
178 void ResourceManagerImpl::OnActivityStarted(Activity* activity) {
179   // Update the activity states.
180   ManageResource();
181   activity_order_changed_ = true;
182 }
183
184 void ResourceManagerImpl::OnActivityEnding(Activity* activity) {
185   activity_order_changed_ = true;
186 }
187
188 void ResourceManagerImpl::OnActivityOrderChanged() {
189   activity_order_changed_ = true;
190 }
191
192 void ResourceManagerImpl::OnOverviewModeEnter() {
193   in_overview_mode_ = true;
194 }
195
196 void ResourceManagerImpl::OnOverviewModeExit() {
197   in_overview_mode_ = false;
198   ManageResource();
199 }
200
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();
205 }
206
207
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;
212 }
213
214 void ResourceManagerImpl::OnWindowStackingChangedInList() {
215   if (pause_) {
216     queued_command_ = true;
217     return;
218   }
219
220   // No need to do anything while being in overview mode.
221   if (in_overview_mode_)
222     return;
223
224   // Manage the resources of each activity.
225   ManageResource();
226 }
227
228 void ResourceManagerImpl::OnMemoryPressure(MemoryPressure pressure) {
229   if (pressure > current_memory_pressure_)
230     OnMemoryPressureIncreased();
231   current_memory_pressure_ = pressure;
232   ManageResource();
233 }
234
235 ResourceManagerDelegate* ResourceManagerImpl::GetDelegate() {
236   return delegate_.get();
237 }
238
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;
243
244   if (pause_) {
245     queued_command_ = true;
246     return;
247   }
248
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();
253
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();
259 }
260
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;
269
270   do {
271     activity_order_changed_ = false;
272
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();
278
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();
299         }
300       }
301
302       // See which count we should handle next.
303       if (activity_order_changed_)
304         break;
305       ++count;
306     }
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_);
310 }
311
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;
334   }
335
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;
352       }
353     }
354   }
355
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;
367     }
368     LOG(ERROR) << "[ResourceManager]: Single activity uses too much memory.";
369     return;
370   }
371
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 << ")";
381   }
382 }
383
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_;
390 }
391
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();
395 }
396
397 bool ResourceManagerImpl::AllowedToUnloadActivity() {
398   return current_memory_pressure_ != MEMORY_PRESSURE_LOW &&
399          base::Time::Now() >= next_resource_management_time_;
400 }
401
402 }  // namespace
403
404 // static
405 void ResourceManager::Create() {
406   DCHECK(!instance);
407   instance = new ResourceManagerImpl(
408       ResourceManagerDelegate::CreateResourceManagerDelegate());
409 }
410
411 // static
412 ResourceManager* ResourceManager::Get() {
413   return instance;
414 }
415
416 // static
417 void ResourceManager::Shutdown() {
418   DCHECK(instance);
419   delete instance;
420   instance = nullptr;
421 }
422
423 ResourceManager::ResourceManager() {}
424
425 ResourceManager::~ResourceManager() {
426   DCHECK(instance);
427   instance = nullptr;
428 }
429
430 }  // namespace athena