| OLD | NEW |
| (Empty) |
| 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 // Note: We cannot use the order of items in the ActivityManager since it | |
| 276 // does not follow the order of windows. | |
| 277 size_t count = 0; | |
| 278 const aura::Window::Windows& windows = | |
| 279 WindowManager::Get()->GetWindowListProvider()->GetWindowList(); | |
| 280 for (aura::Window::Windows::const_reverse_iterator it = windows.rbegin(); | |
| 281 it != windows.rend(); it++) { | |
| 282 Activity* activity = ActivityManager::Get()->GetActivityForWindow(*it); | |
| 283 // It is possible that the window was not yet added to the ActivityManager | |
| 284 // in which case we are not interested in managing it yet. | |
| 285 if (!activity) | |
| 286 continue; | |
| 287 Activity::ActivityState state = activity->GetCurrentState(); | |
| 288 | |
| 289 // The first |kMaxVisibleActivities| entries should be visible, all others | |
| 290 // invisible or at a lower activity state. | |
| 291 if (count < max_activities || | |
| 292 (state == Activity::ACTIVITY_INVISIBLE || | |
| 293 state == Activity::ACTIVITY_VISIBLE)) { | |
| 294 Activity::ActivityState visiblity_state = | |
| 295 count < max_activities ? Activity::ACTIVITY_VISIBLE : | |
| 296 Activity::ACTIVITY_INVISIBLE; | |
| 297 // Only change the state when it changes. Note that when the memory | |
| 298 // pressure is critical, only the primary activities (1 or 2) are made | |
| 299 // visible. Furthermore, in relaxed mode we only want to turn visible, | |
| 300 // never invisible. | |
| 301 if (visiblity_state != state && | |
| 302 (current_memory_pressure_ != MEMORY_PRESSURE_LOW || | |
| 303 visiblity_state == Activity::ACTIVITY_VISIBLE)) { | |
| 304 activity->SetCurrentState(visiblity_state); | |
| 305 // If we turned an activity invisible, we are already releasing memory | |
| 306 // and can hold off releasing more for now. | |
| 307 if (visiblity_state == Activity::ACTIVITY_INVISIBLE) | |
| 308 OnResourcesReleased(); | |
| 309 } | |
| 310 } | |
| 311 | |
| 312 // See which count we should handle next. | |
| 313 if (activity_order_changed_) | |
| 314 break; | |
| 315 ++count; | |
| 316 } | |
| 317 // If we stopped iterating over the list of activities because of the change | |
| 318 // in ordering, then restart processing the activities from the beginning. | |
| 319 } while (activity_order_changed_); | |
| 320 } | |
| 321 | |
| 322 void ResourceManagerImpl::TryToUnloadAnActivity() { | |
| 323 // TODO(skuhne): This algorithm needs to take all kinds of predictive analysis | |
| 324 // and running applications into account. For this first patch we only do a | |
| 325 // very simple "floating window" algorithm which is surely not good enough. | |
| 326 size_t max_running_activities = 5; | |
| 327 switch (current_memory_pressure_) { | |
| 328 case MEMORY_PRESSURE_UNKNOWN: | |
| 329 // If we do not know how much memory we have we assume that it must be a | |
| 330 // high consumption. | |
| 331 // Fallthrough. | |
| 332 case MEMORY_PRESSURE_HIGH: | |
| 333 max_running_activities = 5; | |
| 334 break; | |
| 335 case MEMORY_PRESSURE_CRITICAL: | |
| 336 max_running_activities = 0; | |
| 337 break; | |
| 338 case MEMORY_PRESSURE_MODERATE: | |
| 339 max_running_activities = 7; | |
| 340 break; | |
| 341 case MEMORY_PRESSURE_LOW: | |
| 342 NOTREACHED(); | |
| 343 return; | |
| 344 } | |
| 345 | |
| 346 // Check if / which activity we want to unload. | |
| 347 Activity* oldest_media_activity = nullptr; | |
| 348 Activity* oldest_unloadable_activity = nullptr; | |
| 349 size_t unloadable_activity_count = 0; | |
| 350 const ActivityList& activity_list = ActivityManager::Get()->GetActivityList(); | |
| 351 for (Activity* activity : activity_list) { | |
| 352 Activity::ActivityState state = activity->GetCurrentState(); | |
| 353 // The activity should neither be unloaded nor visible. | |
| 354 if (state != Activity::ACTIVITY_UNLOADED && | |
| 355 state != Activity::ACTIVITY_VISIBLE) { | |
| 356 if (activity->GetMediaState() == Activity::ACTIVITY_MEDIA_STATE_NONE) { | |
| 357 // Does not play media - so we can unload this immediately. | |
| 358 ++unloadable_activity_count; | |
| 359 oldest_unloadable_activity = activity; | |
| 360 } else { | |
| 361 oldest_media_activity = activity; | |
| 362 } | |
| 363 } | |
| 364 } | |
| 365 | |
| 366 if (unloadable_activity_count > max_running_activities) { | |
| 367 CHECK(oldest_unloadable_activity); | |
| 368 OnResourcesReleased(); | |
| 369 oldest_unloadable_activity->SetCurrentState(Activity::ACTIVITY_UNLOADED); | |
| 370 return; | |
| 371 } else if (current_memory_pressure_ == MEMORY_PRESSURE_CRITICAL) { | |
| 372 if (oldest_media_activity) { | |
| 373 OnResourcesReleased(); | |
| 374 oldest_media_activity->SetCurrentState(Activity::ACTIVITY_UNLOADED); | |
| 375 LOG(WARNING) << "Unloading item to releave critical memory pressure"; | |
| 376 return; | |
| 377 } | |
| 378 LOG(ERROR) << "[ResourceManager]: Single activity uses too much memory."; | |
| 379 return; | |
| 380 } | |
| 381 | |
| 382 if (current_memory_pressure_ != MEMORY_PRESSURE_UNKNOWN) { | |
| 383 // Only show this warning when the memory pressure is actually known. This | |
| 384 // will suppress warnings in e.g. unit tests. | |
| 385 LOG(WARNING) << "[ResourceManager]: No way to release memory pressure (" << | |
| 386 current_memory_pressure_ << | |
| 387 "), Activities (running, allowed, unloadable)=(" << | |
| 388 activity_list.size() << ", " << | |
| 389 max_running_activities << ", " << | |
| 390 unloadable_activity_count << ")"; | |
| 391 } | |
| 392 } | |
| 393 | |
| 394 void ResourceManagerImpl::OnResourcesReleased() { | |
| 395 // Do not release too many activities in short succession since it takes time | |
| 396 // to release resources. As such wait the memory pressure interval before the | |
| 397 // next call. | |
| 398 next_resource_management_time_ = base::Time::Now() + | |
| 399 wait_time_for_resource_deallocation_; | |
| 400 } | |
| 401 | |
| 402 void ResourceManagerImpl::OnMemoryPressureIncreased() { | |
| 403 // By setting the timer to Now, the next call will immediately be performed. | |
| 404 next_resource_management_time_ = base::Time::Now(); | |
| 405 } | |
| 406 | |
| 407 bool ResourceManagerImpl::AllowedToUnloadActivity() { | |
| 408 return current_memory_pressure_ != MEMORY_PRESSURE_LOW && | |
| 409 base::Time::Now() >= next_resource_management_time_; | |
| 410 } | |
| 411 | |
| 412 } // namespace | |
| 413 | |
| 414 // static | |
| 415 void ResourceManager::Create() { | |
| 416 DCHECK(!instance); | |
| 417 instance = new ResourceManagerImpl( | |
| 418 ResourceManagerDelegate::CreateResourceManagerDelegate()); | |
| 419 } | |
| 420 | |
| 421 // static | |
| 422 ResourceManager* ResourceManager::Get() { | |
| 423 return instance; | |
| 424 } | |
| 425 | |
| 426 // static | |
| 427 void ResourceManager::Shutdown() { | |
| 428 DCHECK(instance); | |
| 429 delete instance; | |
| 430 instance = nullptr; | |
| 431 } | |
| 432 | |
| 433 ResourceManager::ResourceManager() {} | |
| 434 | |
| 435 ResourceManager::~ResourceManager() { | |
| 436 DCHECK(instance); | |
| 437 instance = nullptr; | |
| 438 } | |
| 439 | |
| 440 } // namespace athena | |
| OLD | NEW |