1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
|
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "athena/resource_manager/public/resource_manager.h"
#include <algorithm>
#include <vector>
#include "athena/activity/public/activity.h"
#include "athena/activity/public/activity_manager.h"
#include "athena/activity/public/activity_manager_observer.h"
#include "athena/resource_manager/memory_pressure_notifier.h"
#include "athena/resource_manager/public/resource_manager_delegate.h"
#include "athena/wm/public/window_list_provider.h"
#include "athena/wm/public/window_list_provider_observer.h"
#include "athena/wm/public/window_manager.h"
#include "athena/wm/public/window_manager_observer.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/time/time.h"
#include "ui/aura/window.h"
namespace athena {
namespace {
class ResourceManagerImpl : public ResourceManager,
public WindowManagerObserver,
public ActivityManagerObserver,
public MemoryPressureObserver,
public WindowListProviderObserver {
public:
ResourceManagerImpl(ResourceManagerDelegate* delegate);
~ResourceManagerImpl() override;
// ResourceManager:
virtual void SetMemoryPressureAndStopMonitoring(
MemoryPressure pressure) override;
virtual void SetWaitTimeBetweenResourceManageCalls(int time_in_ms) override {
wait_time_for_resource_deallocation_ =
base::TimeDelta::FromMilliseconds(time_in_ms);
// Reset the timeout to force the next resource call to execute immediately.
next_resource_management_time_ = base::Time::Now();
}
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;
virtual void OnActivityEnding(Activity* activity) override;
// WindowManagerObserver:
virtual void OnOverviewModeEnter() override;
virtual void OnOverviewModeExit() override;
virtual void OnSplitViewModeEnter() override;
virtual void OnSplitViewModeExit() override;
// MemoryPressureObserver:
virtual void OnMemoryPressure(MemoryPressure pressure) override;
virtual ResourceManagerDelegate* GetDelegate() override;
// WindowListProviderObserver:
virtual void OnWindowStackingChangedInList() override;
virtual void OnWindowAddedToList(aura::Window* added_window) override {}
virtual void OnWindowRemovedFromList(aura::Window* removed_window,
int index) override {}
private:
// Manage the resources for our activities.
void ManageResource();
// Check that the visibility of activities is properly set.
void UpdateVisibilityStates();
// Check if activities can be unloaded to reduce memory pressure.
void TryToUnloadAnActivity();
// Order our activity list to the order of activities of the stream.
// TODO(skuhne): Once the ActivityManager is responsible to create this list
// for us, we can remove this code here.
void UpdateActivityOrder();
// Resources were released and a quiet period is needed before we release
// more since it takes a while to trickle through the system.
void OnResourcesReleased();
// The memory pressure has increased, previously applied measures did not show
// effect and immediate action is required.
void OnMemoryPressureIncreased();
// Returns true when the previous memory release was long enough ago to try
// unloading another activity.
bool AllowedToUnloadActivity();
// The sorted (new(front) -> old(back)) activity list.
// TODO(skuhne): Once the ActivityManager is responsible to create this list
// for us, we can remove this code here.
std::vector<Activity*> activity_list_;
// The resource manager delegate.
scoped_ptr<ResourceManagerDelegate> delegate_;
// Keeping a reference to the current memory pressure.
MemoryPressure current_memory_pressure_;
// 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_split_view_mode_;
// The last time the resource manager was called to release resources.
// Avoid too aggressive resource de-allocation by enforcing a wait time of
// |wait_time_for_resource_deallocation_| between executed calls.
base::Time next_resource_management_time_;
// The wait time between two resource managing executions.
base::TimeDelta wait_time_for_resource_deallocation_;
DISALLOW_COPY_AND_ASSIGN(ResourceManagerImpl);
};
namespace {
ResourceManagerImpl* instance = nullptr;
// 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_(MEMORY_PRESSURE_UNKNOWN),
memory_pressure_notifier_(new MemoryPressureNotifier(this)),
pause_(false),
queued_command_(false),
activity_order_changed_(false),
in_overview_mode_(false),
in_split_view_mode_(false),
next_resource_management_time_(base::Time::Now()),
wait_time_for_resource_deallocation_(base::TimeDelta::FromMilliseconds(
delegate_->MemoryPressureIntervalInMS())) {
WindowManager::Get()->AddObserver(this);
WindowManager::Get()->GetWindowListProvider()->AddObserver(this);
ActivityManager::Get()->AddObserver(this);
}
ResourceManagerImpl::~ResourceManagerImpl() {
ActivityManager::Get()->RemoveObserver(this);
WindowManager::Get()->GetWindowListProvider()->RemoveObserver(this);
WindowManager::Get()->RemoveObserver(this);
while (!activity_list_.empty())
OnActivityEnding(activity_list_.front());
}
void ResourceManagerImpl::SetMemoryPressureAndStopMonitoring(
MemoryPressure pressure) {
memory_pressure_notifier_->StopObserving();
OnMemoryPressure(pressure);
}
void ResourceManagerImpl::OnActivityStarted(Activity* activity) {
// As long as we have to manage the list of activities ourselves, we need to
// order it here.
activity_list_.push_back(activity);
UpdateActivityOrder();
// Update the activity states.
ManageResource();
// Remember that the activity order has changed.
activity_order_changed_ = true;
}
void ResourceManagerImpl::OnActivityEnding(Activity* activity) {
DCHECK(activity->GetWindow());
// 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() {
in_overview_mode_ = true;
}
void ResourceManagerImpl::OnOverviewModeExit() {
in_overview_mode_ = false;
// Reorder the activities and manage the resources again since an order change
// might have caused a visibility change.
UpdateActivityOrder();
ManageResource();
}
void ResourceManagerImpl::OnSplitViewModeEnter() {
// Re-apply the memory pressure to make sure enough items are visible.
in_split_view_mode_ = true;
ManageResource();
}
void ResourceManagerImpl::OnSplitViewModeExit() {
// We don't do immediately something yet. The next ManageResource call will
// come soon.
in_split_view_mode_ = false;
}
void ResourceManagerImpl::OnWindowStackingChangedInList() {
activity_order_changed_ = true;
if (pause_) {
queued_command_ = true;
return;
}
// 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();
}
void ResourceManagerImpl::OnMemoryPressure(MemoryPressure pressure) {
if (pressure > current_memory_pressure_)
OnMemoryPressureIncreased();
current_memory_pressure_ = pressure;
ManageResource();
}
ResourceManagerDelegate* ResourceManagerImpl::GetDelegate() {
return delegate_.get();
}
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;
}
// Check that the visibility of items is properly set. Note that this might
// already trigger a release of resources. If this happens,
// AllowedToUnloadActivity() will return false.
UpdateVisibilityStates();
// Since resource deallocation takes time, we avoid to release more resources
// in short succession. Note that we come here periodically and if one call
// is not triggering an unload, the next one will.
if (AllowedToUnloadActivity())
TryToUnloadAnActivity();
}
void ResourceManagerImpl::UpdateVisibilityStates() {
// The first n activities should be treated 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 = in_split_view_mode_ ? 2 : 1;
// 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 turn visible,
// never invisible.
if (visiblity_state != state &&
(current_memory_pressure_ != MEMORY_PRESSURE_LOW ||
visiblity_state == Activity::ACTIVITY_VISIBLE)) {
activity->SetCurrentState(visiblity_state);
// If we turned an activity invisible, we are already releasing memory
// and can hold off releasing more for now.
if (visiblity_state == Activity::ACTIVITY_INVISIBLE)
OnResourcesReleased();
}
}
// See which index we should handle next.
if (activity_order_changed_) {
activity_order_changed_ = false;
index = 0;
} else {
++index;
}
}
}
void ResourceManagerImpl::TryToUnloadAnActivity() {
// 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.
size_t max_running_activities = 5;
switch (current_memory_pressure_) {
case MEMORY_PRESSURE_UNKNOWN:
// If we do not know how much memory we have we assume that it must be a
// high consumption.
// Fallthrough.
case MEMORY_PRESSURE_HIGH:
max_running_activities = 5;
break;
case MEMORY_PRESSURE_CRITICAL:
max_running_activities = 0;
break;
case MEMORY_PRESSURE_MODERATE:
max_running_activities = 7;
break;
case MEMORY_PRESSURE_LOW:
NOTREACHED();
return;
}
// Check if / which activity we want to unload.
Activity* oldest_media_activity = nullptr;
std::vector<Activity*> unloadable_activities;
for (std::vector<Activity*>::iterator it = activity_list_.begin();
it != activity_list_.end(); ++it) {
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);
} else {
oldest_media_activity = *it;
}
}
}
if (unloadable_activities.size() > max_running_activities) {
OnResourcesReleased();
unloadable_activities.back()->SetCurrentState(Activity::ACTIVITY_UNLOADED);
return;
} else if (current_memory_pressure_ == MEMORY_PRESSURE_CRITICAL) {
if (oldest_media_activity) {
OnResourcesReleased();
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.
LOG(WARNING) << "[ResourceManager]: No way to release memory pressure (" <<
current_memory_pressure_ <<
"), Activities (running, allowed, unloadable)=(" <<
activity_list_.size() << ", " <<
max_running_activities << ", " <<
unloadable_activities.size() << ")";
}
}
void ResourceManagerImpl::UpdateActivityOrder() {
queued_command_ = true;
if (activity_list_.empty())
return;
std::vector<Activity*> new_activity_list;
const aura::Window::Windows children =
WindowManager::Get()->GetWindowListProvider()->GetWindowList();
// Find the first window in the container which is part of the application.
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) {
if (*child_iterator == (*activity_iterator)->GetWindow()) {
new_activity_list.push_back(*activity_iterator);
activity_list_.erase(activity_iterator);
break;
}
}
}
// 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;
}
void ResourceManagerImpl::OnResourcesReleased() {
// Do not release too many activities in short succession since it takes time
// to release resources. As such wait the memory pressure interval before the
// next call.
next_resource_management_time_ = base::Time::Now() +
wait_time_for_resource_deallocation_;
}
void ResourceManagerImpl::OnMemoryPressureIncreased() {
// By setting the timer to Now, the next call will immediately be performed.
next_resource_management_time_ = base::Time::Now();
}
bool ResourceManagerImpl::AllowedToUnloadActivity() {
return current_memory_pressure_ != MEMORY_PRESSURE_LOW &&
base::Time::Now() >= next_resource_management_time_;
}
} // namespace
// static
void ResourceManager::Create() {
DCHECK(!instance);
instance = new ResourceManagerImpl(
ResourceManagerDelegate::CreateResourceManagerDelegate());
}
// static
ResourceManager* ResourceManager::Get() {
return instance;
}
// static
void ResourceManager::Shutdown() {
DCHECK(instance);
delete instance;
instance = nullptr;
}
ResourceManager::ResourceManager() {}
ResourceManager::~ResourceManager() {
DCHECK(instance);
instance = nullptr;
}
} // namespace athena
|