Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(267)

Side by Side Diff: chrome/browser/memory/tab_manager.cc

Issue 1641813002: Provide renderers with memory pressure signals. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@contentapi
Patch Set: Cleaned up. Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698