OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/memory/tab_manager.h" | 5 #include "chrome/browser/memory/tab_manager.h" |
6 | 6 |
7 #include <stddef.h> | 7 #include <stddef.h> |
8 | 8 |
9 #include <algorithm> | 9 #include <algorithm> |
10 #include <set> | 10 #include <set> |
11 #include <vector> | 11 #include <vector> |
12 | 12 |
13 #include "ash/multi_profile_uma.h" | 13 #include "ash/multi_profile_uma.h" |
14 #include "ash/session/session_state_delegate.h" | 14 #include "ash/session/session_state_delegate.h" |
15 #include "ash/shell.h" | 15 #include "ash/shell.h" |
16 #include "base/bind.h" | 16 #include "base/bind.h" |
17 #include "base/bind_helpers.h" | 17 #include "base/bind_helpers.h" |
18 #include "base/command_line.h" | 18 #include "base/command_line.h" |
19 #include "base/feature_list.h" | 19 #include "base/feature_list.h" |
20 #include "base/macros.h" | 20 #include "base/macros.h" |
21 #include "base/memory/memory_pressure_monitor.h" | 21 #include "base/memory/memory_pressure_monitor.h" |
22 #include "base/metrics/field_trial.h" | 22 #include "base/metrics/field_trial.h" |
23 #include "base/metrics/histogram.h" | 23 #include "base/metrics/histogram.h" |
24 #include "base/process/process.h" | 24 #include "base/process/process.h" |
25 #include "base/strings/string16.h" | 25 #include "base/strings/string16.h" |
26 #include "base/strings/string_number_conversions.h" | 26 #include "base/strings/string_number_conversions.h" |
27 #include "base/strings/string_util.h" | 27 #include "base/strings/string_util.h" |
28 #include "base/strings/utf_string_conversions.h" | 28 #include "base/strings/utf_string_conversions.h" |
29 #include "base/thread_task_runner_handle.h" | |
29 #include "base/threading/thread.h" | 30 #include "base/threading/thread.h" |
30 #include "base/time/tick_clock.h" | 31 #include "base/time/tick_clock.h" |
31 #include "build/build_config.h" | 32 #include "build/build_config.h" |
32 #include "chrome/browser/browser_process.h" | 33 #include "chrome/browser/browser_process.h" |
33 #include "chrome/browser/media/media_capture_devices_dispatcher.h" | 34 #include "chrome/browser/media/media_capture_devices_dispatcher.h" |
34 #include "chrome/browser/media/media_stream_capture_indicator.h" | 35 #include "chrome/browser/media/media_stream_capture_indicator.h" |
35 #include "chrome/browser/memory/oom_memory_details.h" | 36 #include "chrome/browser/memory/oom_memory_details.h" |
36 #include "chrome/browser/memory/tab_manager_web_contents_data.h" | 37 #include "chrome/browser/memory/tab_manager_web_contents_data.h" |
37 #include "chrome/browser/profiles/profile.h" | 38 #include "chrome/browser/profiles/profile.h" |
38 #include "chrome/browser/ui/browser.h" | 39 #include "chrome/browser/ui/browser.h" |
39 #include "chrome/browser/ui/browser_iterator.h" | 40 #include "chrome/browser/ui/browser_iterator.h" |
40 #include "chrome/browser/ui/browser_list.h" | 41 #include "chrome/browser/ui/browser_list.h" |
41 #include "chrome/browser/ui/host_desktop.h" | 42 #include "chrome/browser/ui/host_desktop.h" |
42 #include "chrome/browser/ui/tab_contents/tab_contents_iterator.h" | 43 #include "chrome/browser/ui/tab_contents/tab_contents_iterator.h" |
43 #include "chrome/browser/ui/tabs/tab_strip_model.h" | 44 #include "chrome/browser/ui/tabs/tab_strip_model.h" |
44 #include "chrome/browser/ui/tabs/tab_utils.h" | 45 #include "chrome/browser/ui/tabs/tab_utils.h" |
45 #include "chrome/common/chrome_constants.h" | 46 #include "chrome/common/chrome_constants.h" |
46 #include "chrome/common/chrome_features.h" | 47 #include "chrome/common/chrome_features.h" |
47 #include "chrome/common/url_constants.h" | 48 #include "chrome/common/url_constants.h" |
48 #include "components/metrics/system_memory_stats_recorder.h" | 49 #include "components/metrics/system_memory_stats_recorder.h" |
49 #include "components/variations/variations_associated_data.h" | 50 #include "components/variations/variations_associated_data.h" |
50 #include "content/public/browser/browser_thread.h" | 51 #include "content/public/browser/browser_thread.h" |
52 #include "content/public/browser/memory_pressure.h" | |
51 #include "content/public/browser/navigation_controller.h" | 53 #include "content/public/browser/navigation_controller.h" |
52 #include "content/public/browser/render_process_host.h" | 54 #include "content/public/browser/render_process_host.h" |
53 #include "content/public/browser/web_contents.h" | 55 #include "content/public/browser/web_contents.h" |
54 #include "content/public/common/page_importance_signals.h" | 56 #include "content/public/common/page_importance_signals.h" |
55 | 57 |
56 #if defined(OS_CHROMEOS) | 58 #if defined(OS_CHROMEOS) |
57 #include "chrome/browser/memory/tab_manager_delegate_chromeos.h" | 59 #include "chrome/browser/memory/tab_manager_delegate_chromeos.h" |
58 #endif | 60 #endif |
59 | 61 |
60 using base::TimeDelta; | 62 using base::TimeDelta; |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
100 if (web_contents_id == target_web_contents_id) { | 102 if (web_contents_id == target_web_contents_id) { |
101 *model = local_model; | 103 *model = local_model; |
102 return idx; | 104 return idx; |
103 } | 105 } |
104 } | 106 } |
105 } | 107 } |
106 | 108 |
107 return -1; | 109 return -1; |
108 } | 110 } |
109 | 111 |
112 // A wrapper around base::MemoryPressureMonitor::GetCurrentPressureLevel. | |
113 // TODO(chrisha): Move this do the default implementation of a delegate. | |
114 base::MemoryPressureListener::MemoryPressureLevel | |
115 GetCurrentPressureLevel() { | |
116 auto monitor = base::MemoryPressureMonitor::Get(); | |
117 if (monitor) | |
118 return monitor->GetCurrentPressureLevel(); | |
119 return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE; | |
120 } | |
121 | |
122 // A wrapper to content::SendPressureNotification that doesn't have overloaded | |
123 // type ambiguity. Makes use of Bind easier. | |
124 // TODO(chrisha): Move this do the default implementation of a delegate. | |
125 void NotifyRendererProcess( | |
126 const content::RenderProcessHost* render_process_host, | |
127 base::MemoryPressureListener::MemoryPressureLevel level) { | |
128 content::SendPressureNotification(render_process_host, level); | |
129 } | |
130 | |
110 } // namespace | 131 } // namespace |
111 | 132 |
112 //////////////////////////////////////////////////////////////////////////////// | 133 //////////////////////////////////////////////////////////////////////////////// |
113 // TabManager | 134 // TabManager |
114 | 135 |
115 TabManager::TabManager() | 136 TabManager::TabManager() |
116 : discard_count_(0), | 137 : discard_count_(0), |
117 recent_tab_discard_(false), | 138 recent_tab_discard_(false), |
118 discard_once_(false), | 139 discard_once_(false), |
119 browser_tab_strip_tracker_(this, nullptr, nullptr), | 140 browser_tab_strip_tracker_(this, nullptr, nullptr), |
120 test_tick_clock_(nullptr) { | 141 test_tick_clock_(nullptr), |
142 under_memory_pressure_(false), | |
143 weak_ptr_factory_(this) { | |
121 #if defined(OS_CHROMEOS) | 144 #if defined(OS_CHROMEOS) |
122 delegate_.reset(new TabManagerDelegate); | 145 delegate_.reset(new TabManagerDelegate); |
123 #endif | 146 #endif |
124 browser_tab_strip_tracker_.Init( | 147 browser_tab_strip_tracker_.Init( |
125 BrowserTabStripTracker::InitWith::ALL_BROWERS); | 148 BrowserTabStripTracker::InitWith::ALL_BROWERS); |
149 | |
150 // Set up default callbacks. These may be overridden post-construction as | |
151 // testing seams. | |
152 get_current_pressure_level_ = base::Bind(&GetCurrentPressureLevel); | |
153 notify_renderer_process_ = base::Bind(&NotifyRendererProcess); | |
126 } | 154 } |
127 | 155 |
128 TabManager::~TabManager() { | 156 TabManager::~TabManager() { |
129 Stop(); | 157 Stop(); |
130 } | 158 } |
131 | 159 |
132 void TabManager::Start() { | 160 void TabManager::Start() { |
133 #if defined(OS_WIN) || defined(OS_MACOSX) | 161 #if defined(OS_WIN) || defined(OS_MACOSX) |
134 // If the feature is not enabled, do nothing. | 162 // If the feature is not enabled, do nothing. |
135 if (!base::FeatureList::IsEnabled(features::kAutomaticTabDiscarding)) | 163 if (!base::FeatureList::IsEnabled(features::kAutomaticTabDiscarding)) |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
196 } | 224 } |
197 | 225 |
198 // Things to collect on the browser thread (because TabStripModel isn't thread | 226 // Things to collect on the browser thread (because TabStripModel isn't thread |
199 // safe): | 227 // safe): |
200 // 1) whether or not a tab is pinned | 228 // 1) whether or not a tab is pinned |
201 // 2) last time a tab was selected | 229 // 2) last time a tab was selected |
202 // 3) is the tab currently selected | 230 // 3) is the tab currently selected |
203 TabStatsList TabManager::GetTabStats() { | 231 TabStatsList TabManager::GetTabStats() { |
204 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 232 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
205 TabStatsList stats_list; | 233 TabStatsList stats_list; |
206 stats_list.reserve(32); // 99% of users have < 30 tabs open | 234 stats_list.reserve(32); // 99% of users have < 30 tabs open. |
207 | 235 |
208 // Go through each window to get all the tabs. Depending on the platform, | 236 // TODO(chrisha): Move this code to a TabStripModel enumeration delegate! |
209 // windows are either native or ash or both. The goal is to make sure to go | 237 if (!test_tab_strip_models_.empty()) { |
210 // through them all, starting with the active window first (use | 238 for (size_t i = 0; i < test_tab_strip_models_.size(); ++i) { |
211 // chrome::GetActiveDesktop to get the current used type). | 239 AddTabStats(test_tab_strip_models_[i].first, // tab_strip_model |
212 AddTabStats(BrowserList::GetInstance(chrome::GetActiveDesktop()), true, | 240 test_tab_strip_models_[i].second, // is_app |
213 &stats_list); | 241 i == 0, // is_active |
214 if (chrome::GetActiveDesktop() != chrome::HOST_DESKTOP_TYPE_NATIVE) { | 242 &stats_list); |
215 AddTabStats(BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_NATIVE), | 243 } |
216 false, &stats_list); | 244 } else { |
217 } else if (chrome::GetActiveDesktop() != chrome::HOST_DESKTOP_TYPE_ASH) { | 245 // The code here can only be tested under a full browser test. |
218 AddTabStats(BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH), false, | 246 |
247 // Go through each window to get all the tabs. Depending on the platform, | |
248 // windows are either native or ash or both. The goal is to make sure to go | |
249 // through them all, starting with the active window first (use | |
250 // chrome::GetActiveDesktop to get the current used type). | |
251 AddTabStats(BrowserList::GetInstance(chrome::GetActiveDesktop()), true, | |
219 &stats_list); | 252 &stats_list); |
253 if (chrome::GetActiveDesktop() != chrome::HOST_DESKTOP_TYPE_NATIVE) { | |
254 AddTabStats(BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_NATIVE), | |
255 false, &stats_list); | |
256 } else if (chrome::GetActiveDesktop() != chrome::HOST_DESKTOP_TYPE_ASH) { | |
257 AddTabStats(BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH), | |
258 false, &stats_list); | |
259 } | |
220 } | 260 } |
221 | 261 |
222 // Sort the collected data so that least desirable to be killed is first, most | 262 // Sort the collected data so that least desirable to be killed is first, most |
223 // desirable is last. | 263 // desirable is last. |
224 std::sort(stats_list.begin(), stats_list.end(), CompareTabStats); | 264 std::sort(stats_list.begin(), stats_list.end(), CompareTabStats); |
225 return stats_list; | 265 return stats_list; |
226 } | 266 } |
227 | 267 |
268 std::vector<content::RenderProcessHost*> TabManager::GetOrderedRenderers() { | |
269 // Get the tab stats. | |
270 auto tab_stats = GetTabStats(); | |
271 | |
272 std::vector<content::RenderProcessHost*> sorted_renderers; | |
273 std::set<content::RenderProcessHost*> seen_renderers; | |
274 sorted_renderers.reserve(tab_stats.size()); | |
275 | |
276 // Convert the tab sort order to a process sort order. The process inherits | |
277 // the priority of its highest priority tab. | |
278 for (auto& tab : tab_stats) { | |
279 // Skip renderers that have already been encountered. This can occur when | |
280 // multiple tabs are folded into a single renderer process. In this case the | |
281 // process takes the priority of its highest priority contained tab. | |
282 auto renderer = tab.render_process_host; | |
283 if (!seen_renderers.insert(renderer).second) | |
284 continue; | |
285 | |
286 sorted_renderers.push_back(renderer); | |
287 } | |
288 | |
289 return sorted_renderers; | |
290 } | |
291 | |
228 bool TabManager::IsTabDiscarded(content::WebContents* contents) const { | 292 bool TabManager::IsTabDiscarded(content::WebContents* contents) const { |
229 return GetWebContentsData(contents)->IsDiscarded(); | 293 return GetWebContentsData(contents)->IsDiscarded(); |
230 } | 294 } |
231 | 295 |
232 // TODO(jamescook): This should consider tabs with references to other tabs, | 296 // TODO(jamescook): This should consider tabs with references to other tabs, |
233 // such as tabs created with JavaScript window.open(). Potentially consider | 297 // such as tabs created with JavaScript window.open(). Potentially consider |
234 // discarding the entire set together, or use that in the priority computation. | 298 // discarding the entire set together, or use that in the priority computation. |
235 bool TabManager::DiscardTab() { | 299 bool TabManager::DiscardTab() { |
236 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 300 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
237 TabStatsList stats = GetTabStats(); | 301 TabStatsList stats = GetTabStats(); |
(...skipping 177 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
415 // If it's the active desktop, the first window will be the active one. | 479 // If it's the active desktop, the first window will be the active one. |
416 // Otherwise, assume no active windows. | 480 // Otherwise, assume no active windows. |
417 bool browser_active = active_desktop; | 481 bool browser_active = active_desktop; |
418 for (BrowserList::const_reverse_iterator browser_iterator = | 482 for (BrowserList::const_reverse_iterator browser_iterator = |
419 browser_list->begin_last_active(); | 483 browser_list->begin_last_active(); |
420 browser_iterator != browser_list->end_last_active(); | 484 browser_iterator != browser_list->end_last_active(); |
421 ++browser_iterator) { | 485 ++browser_iterator) { |
422 Browser* browser = *browser_iterator; | 486 Browser* browser = *browser_iterator; |
423 bool is_browser_for_app = browser->is_app(); | 487 bool is_browser_for_app = browser->is_app(); |
424 const TabStripModel* model = browser->tab_strip_model(); | 488 const TabStripModel* model = browser->tab_strip_model(); |
425 for (int i = 0; i < model->count(); i++) { | 489 AddTabStats(model, is_browser_for_app, active_desktop, stats_list); |
426 WebContents* contents = model->GetWebContentsAt(i); | |
427 if (!contents->IsCrashed()) { | |
428 TabStats stats; | |
429 stats.is_app = is_browser_for_app; | |
430 stats.is_internal_page = | |
431 IsInternalPage(contents->GetLastCommittedURL()); | |
432 stats.is_media = IsMediaTab(contents); | |
433 stats.is_pinned = model->IsTabPinned(i); | |
434 stats.is_selected = browser_active && model->IsTabSelected(i); | |
435 stats.is_discarded = GetWebContentsData(contents)->IsDiscarded(); | |
436 stats.has_form_entry = | |
437 contents->GetPageImportanceSignals().had_form_interaction; | |
438 stats.discard_count = GetWebContentsData(contents)->DiscardCount(); | |
439 stats.last_active = contents->GetLastActiveTime(); | |
440 stats.renderer_handle = contents->GetRenderProcessHost()->GetHandle(); | |
441 stats.child_process_host_id = contents->GetRenderProcessHost()->GetID(); | |
442 #if defined(OS_CHROMEOS) | |
443 stats.oom_score = delegate_->GetOomScore(stats.child_process_host_id); | |
444 #endif | |
445 stats.title = contents->GetTitle(); | |
446 stats.tab_contents_id = IdFromWebContents(contents); | |
447 stats_list->push_back(stats); | |
448 } | |
449 } | |
450 // The active browser window is processed in the first iteration. | 490 // The active browser window is processed in the first iteration. |
451 browser_active = false; | 491 browser_active = false; |
452 } | 492 } |
453 } | 493 } |
454 | 494 |
495 void TabManager::AddTabStats(const TabStripModel* model, | |
496 bool is_app, | |
497 bool active_model, | |
498 TabStatsList* stats_list) { | |
499 for (int i = 0; i < model->count(); i++) { | |
500 WebContents* contents = model->GetWebContentsAt(i); | |
501 if (!contents->IsCrashed()) { | |
502 TabStats stats; | |
503 stats.is_app = is_app; | |
504 stats.is_internal_page = | |
505 IsInternalPage(contents->GetLastCommittedURL()); | |
506 stats.is_media = IsMediaTab(contents); | |
507 stats.is_pinned = model->IsTabPinned(i); | |
508 stats.is_selected = active_model && model->IsTabSelected(i); | |
509 stats.is_discarded = GetWebContentsData(contents)->IsDiscarded(); | |
510 stats.has_form_entry = | |
511 contents->GetPageImportanceSignals().had_form_interaction; | |
512 stats.discard_count = GetWebContentsData(contents)->DiscardCount(); | |
513 stats.last_active = contents->GetLastActiveTime(); | |
514 stats.render_process_host = contents->GetRenderProcessHost(); | |
515 stats.renderer_handle = contents->GetRenderProcessHost()->GetHandle(); | |
516 stats.child_process_host_id = contents->GetRenderProcessHost()->GetID(); | |
517 #if defined(OS_CHROMEOS) | |
518 stats.oom_score = delegate_->GetOomScore(stats.child_process_host_id); | |
519 #endif | |
520 stats.title = contents->GetTitle(); | |
521 stats.tab_contents_id = IdFromWebContents(contents); | |
522 stats_list->push_back(stats); | |
523 } | |
524 } | |
525 } | |
526 | |
455 // This function is called when |update_timer_| fires. It will adjust the clock | 527 // This function is called when |update_timer_| fires. It will adjust the clock |
456 // if needed (if it detects that the machine was asleep) and will fire the stats | 528 // if needed (if it detects that the machine was asleep) and will fire the stats |
457 // updating on ChromeOS via the delegate. | 529 // updating on ChromeOS via the delegate. |
458 void TabManager::UpdateTimerCallback() { | 530 void TabManager::UpdateTimerCallback() { |
459 // If Chrome is shutting down, do not do anything. | 531 // If Chrome is shutting down, do not do anything. |
460 if (g_browser_process->IsShuttingDown()) | 532 if (g_browser_process->IsShuttingDown()) |
461 return; | 533 return; |
462 | 534 |
463 if (BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH)->empty() && | 535 if (BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH)->empty() && |
464 BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_NATIVE)->empty()) | 536 BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_NATIVE)->empty()) |
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
581 | 653 |
582 return null_contents; | 654 return null_contents; |
583 } | 655 } |
584 | 656 |
585 void TabManager::OnMemoryPressure( | 657 void TabManager::OnMemoryPressure( |
586 base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) { | 658 base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) { |
587 // If Chrome is shutting down, do not do anything. | 659 // If Chrome is shutting down, do not do anything. |
588 if (g_browser_process->IsShuttingDown()) | 660 if (g_browser_process->IsShuttingDown()) |
589 return; | 661 return; |
590 | 662 |
591 // For the moment only do something when critical state is reached. | 663 // If no task runner has been set, then use the same one that the memory |
664 // pressure subsystem uses. | |
665 if (!task_runner_.get()) | |
666 task_runner_ = base::ThreadTaskRunnerHandle::Get(); | |
667 | |
668 // Under critical pressure try to discard a tab. | |
592 if (memory_pressure_level == | 669 if (memory_pressure_level == |
593 base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) { | 670 base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) { |
594 LogMemoryAndDiscardTab(); | 671 LogMemoryAndDiscardTab(); |
595 } | 672 } |
596 // TODO(skuhne): If more memory pressure levels are introduced, consider | 673 // TODO(skuhne): If more memory pressure levels are introduced, consider |
597 // calling PurgeBrowserMemory() before CRITICAL is reached. | 674 // calling PurgeBrowserMemory() before CRITICAL is reached. |
675 | |
676 // If this is the beginning of a period of memory pressure then kick off | |
677 // notification of child processes. | |
678 // NOTE: This mechanism relies on having a MemoryPressureMonitor | |
679 // implementation that supports "CurrentPressureLevel". This is true on all | |
680 // platforms on which TabManager is used. | |
681 if (!under_memory_pressure_) | |
682 DoChildProcessDispatch(); | |
598 } | 683 } |
599 | 684 |
600 bool TabManager::IsMediaTab(WebContents* contents) const { | 685 bool TabManager::IsMediaTab(WebContents* contents) const { |
601 if (contents->WasRecentlyAudible()) | 686 if (contents->WasRecentlyAudible()) |
602 return true; | 687 return true; |
603 | 688 |
604 scoped_refptr<MediaStreamCaptureIndicator> media_indicator = | 689 scoped_refptr<MediaStreamCaptureIndicator> media_indicator = |
605 MediaCaptureDevicesDispatcher::GetInstance() | 690 MediaCaptureDevicesDispatcher::GetInstance() |
606 ->GetMediaStreamCaptureIndicator(); | 691 ->GetMediaStreamCaptureIndicator(); |
607 if (media_indicator->IsCapturingUserMedia(contents) || | 692 if (media_indicator->IsCapturingUserMedia(contents) || |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
661 return first.last_active > second.last_active; | 746 return first.last_active > second.last_active; |
662 } | 747 } |
663 | 748 |
664 TimeTicks TabManager::NowTicks() const { | 749 TimeTicks TabManager::NowTicks() const { |
665 if (!test_tick_clock_) | 750 if (!test_tick_clock_) |
666 return TimeTicks::Now(); | 751 return TimeTicks::Now(); |
667 | 752 |
668 return test_tick_clock_->NowTicks(); | 753 return test_tick_clock_->NowTicks(); |
669 } | 754 } |
670 | 755 |
756 void TabManager::DoChildProcessDispatch() { | |
757 if (!under_memory_pressure_) | |
758 under_memory_pressure_ = true; | |
759 | |
760 // If the memory pressure condition has ended then stop dispatching messages. | |
761 auto level = get_current_pressure_level_.Run(); | |
762 if (level == base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE) { | |
763 under_memory_pressure_ = false; | |
764 notified_renderers_.clear(); | |
765 return; | |
766 } | |
767 | |
768 // Get a vector of active renderers, from highest to lowest priority. | |
769 auto renderers = GetOrderedRenderers(); | |
770 CHECK(!renderers.empty()); | |
Georges Khalil
2016/01/28 15:41:02
Could this be triggered while shutting down and no
chrisha
2016/01/29 16:17:54
Very good point, as that's a possible race conditi
| |
771 | |
772 // Notify a single renderer of memory pressure. | |
773 bool notified = false; | |
774 while (!notified) { | |
775 // Notify the lowest priority renderer that hasn't been notified yet. | |
776 for (auto rit = renderers.rbegin(); rit != renderers.rend(); ++rit) { | |
777 // If this renderer has already been notified then look at the next one. | |
778 if (!notified_renderers_.insert(*rit).second) | |
779 continue; | |
780 | |
781 // Notify the renderer. | |
782 notify_renderer_process_.Run(*rit, level); | |
783 notified = true; | |
784 break; | |
785 } | |
786 | |
787 // If all renderers were processed and none were notified, then all | |
788 // renderers have already been notified. Clear the list and start again. | |
789 if (!notified) | |
790 notified_renderers_.clear(); | |
791 | |
792 // This loop can only run at most twice. If it doesn't exit the first time | |
793 // through, by the second time through |notified_renderers_| will be empty. | |
794 // Since |renderers| is always non-empty, the first renderer encountered | |
795 // during the second pass will be notified. | |
796 } | |
797 | |
798 // Schedule another notification. Use a weak pointer so this doesn't explode | |
799 // during tear down. | |
800 task_runner_->PostDelayedTask( | |
801 FROM_HERE, | |
802 base::Bind(&TabManager::DoChildProcessDispatch, | |
803 weak_ptr_factory_.GetWeakPtr()), | |
804 base::TimeDelta::FromSeconds(kRendererNotificationDelayInSeconds)); | |
805 } | |
806 | |
671 } // namespace memory | 807 } // namespace memory |
OLD | NEW |