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