Chromium Code Reviews| Index: chrome/browser/memory/tab_manager.cc |
| diff --git a/chrome/browser/memory/tab_manager.cc b/chrome/browser/memory/tab_manager.cc |
| index e761e81a7c43a1d31ef1ebd15c33c984e6faa98b..3223e39cd746bf4c21d58a88ae1ba1db23729b43 100644 |
| --- a/chrome/browser/memory/tab_manager.cc |
| +++ b/chrome/browser/memory/tab_manager.cc |
| @@ -84,6 +84,16 @@ const int kRecentTabDiscardIntervalSeconds = 60; |
| // machine was suspended and correct the timing statistics. |
| const int kSuspendThresholdSeconds = kAdjustmentIntervalSeconds * 4; |
| +// The backgrounded renderer is allowed to be suspended for this duration |
| +// before resume. |
|
haraken
2016/10/20 16:52:16
A suspended renderer is suspended for this duratio
|
| +constexpr base::TimeDelta kMaxTimeRendererAllowedToBeSuspendedBeforeResume = |
|
haraken
2016/10/20 16:52:16
kDurationOfRendererSuspension ?
|
| + base::TimeDelta::FromSeconds(120); |
| + |
| +// When the suspended backgrounded renderer is resumed, the renderer runs |
| +// for this duration. |
|
haraken
2016/10/20 16:52:16
A resumed renderer is resumed for this duration.
|
| +constexpr base::TimeDelta kSuspendedRendererLengthOfResumption = |
|
haraken
2016/10/20 16:52:16
kDurationOfRendererResumption ?
|
| + base::TimeDelta::FromSeconds(10); |
| + |
| // The time during which a tab is protected from discarding after it stops being |
| // audible. |
| const int kAudioProtectionTimeSeconds = 60; |
| @@ -418,17 +428,24 @@ void TabManager::SetTabAutoDiscardableState(content::WebContents* contents, |
| GetWebContentsData(contents)->SetAutoDiscardableState(state); |
| } |
| +content::WebContents* TabManager::GetWebContentsByContentsId( |
| + int64_t tab_contents_id) { |
| + TabStripModel* model; |
| + int index = FindTabStripModelById(tab_contents_id, &model); |
| + if (index == -1) |
| + return nullptr; |
| + return model->GetWebContentsAt(index); |
| +} |
| + |
| bool TabManager::CanSuspendBackgroundedRenderer(int render_process_id) { |
| // A renderer can be suspended if it's not playing media. |
| auto tab_stats = GetUnsortedTabStats(); |
| for (auto& tab : tab_stats) { |
| if (tab.child_process_host_id != render_process_id) |
| continue; |
| - TabStripModel* model; |
| - int index = FindTabStripModelById(tab.tab_contents_id, &model); |
| - if (index == -1) |
| + WebContents* web_contents = GetWebContentsByContentsId(tab.tab_contents_id); |
| + if (!web_contents) |
| return false; |
| - WebContents* web_contents = model->GetWebContentsAt(index); |
| if (IsMediaTab(web_contents)) |
| return false; |
| } |
| @@ -687,6 +704,47 @@ void TabManager::UpdateTimerCallback() { |
| PurgeAndSuspendBackgroundedTabs(); |
| } |
| +bool TabManager::ShouldUpdatePurgeAndSuspendState( |
| + const base::TimeTicks& current_time, |
| + const TabStats& tab, |
| + const base::TimeDelta& purge_and_suspend_threshold, |
| + PurgeAndSuspendState* next_state) { |
| + DCHECK(next_state); |
| + content::WebContents* content = |
| + GetWebContentsByContentsId(tab.tab_contents_id); |
| + if (!content) |
| + return false; |
| + |
| + base::TimeTicks last_modified_time = |
| + GetWebContentsData(content)->LastPurgeAndSuspendModifiedTime(); |
| + PurgeAndSuspendState state = |
| + GetWebContentsData(content)->GetPurgeAndSuspendState(); |
| + DCHECK(state == RUNNING || tab.last_active < last_modified_time); |
|
haraken
2016/10/20 16:52:16
Add a comment about what 'tab.last_active < last_m
|
| + |
| + auto time_passed = current_time - last_modified_time; |
| + switch (state) { |
| + case RUNNING: |
| + if (time_passed > purge_and_suspend_threshold) { |
| + *next_state = SUSPENDED; |
| + return true; |
| + } |
| + break; |
| + case RESUMED: |
| + if (time_passed > kSuspendedRendererLengthOfResumption) { |
| + *next_state = SUSPENDED; |
| + return true; |
| + } |
| + break; |
| + case SUSPENDED: |
| + if (time_passed > kMaxTimeRendererAllowedToBeSuspendedBeforeResume) { |
| + *next_state = RESUMED; |
| + return true; |
| + } |
| + break; |
| + } |
| + return false; |
| +} |
| + |
| void TabManager::PurgeAndSuspendBackgroundedTabs() { |
| const base::CommandLine& command_line = |
| *base::CommandLine::ForCurrentProcess(); |
| @@ -700,21 +758,36 @@ void TabManager::PurgeAndSuspendBackgroundedTabs() { |
| } |
| if (purge_and_suspend_time <= 0) |
| return; |
| - auto purge_and_suspend_time_threshold = |
| - NowTicks() - base::TimeDelta::FromSeconds(purge_and_suspend_time); |
| + base::TimeTicks current_time = NowTicks(); |
| + base::TimeDelta purge_and_suspend_threshold = |
| + base::TimeDelta::FromSeconds(purge_and_suspend_time); |
| auto tab_stats = GetUnsortedTabStats(); |
| for (auto& tab : tab_stats) { |
| if (!tab.render_process_host->IsProcessBackgrounded()) |
| continue; |
| + if (!CanSuspendBackgroundedRenderer(tab.child_process_host_id)) |
| + continue; |
| + |
| // TODO(hajimehoshi): Now calling PurgeAndSuspend is implemented without |
| // timers for simplicity, so PurgeAndSuspend is called even after the |
| // renderer is purged and suspended once. This should be replaced with |
| // timers if we want necessary and sufficient signals. |
| - if (tab.last_active > purge_and_suspend_time_threshold) |
| + PurgeAndSuspendState state = RUNNING; |
| + if (!ShouldUpdatePurgeAndSuspendState(current_time, tab, |
|
haraken
2016/10/20 16:52:16
How about just making this method return the next
|
| + purge_and_suspend_threshold, &state)) |
| continue; |
| - if (!CanSuspendBackgroundedRenderer(tab.child_process_host_id)) |
| - continue; |
| - tab.render_process_host->PurgeAndSuspend(); |
| + WebContents* content = GetWebContentsByContentsId(tab.tab_contents_id); |
| + GetWebContentsData(content)->SetPurgeAndSuspendState(state); |
| + switch (state) { |
| + case SUSPENDED: |
| + tab.render_process_host->PurgeAndSuspend(); |
| + break; |
| + case RESUMED: |
| + tab.render_process_host->Resume(); |
| + break; |
| + case RUNNING: |
| + NOTREACHED(); |
| + } |
| } |
| } |
| @@ -815,6 +888,7 @@ void TabManager::ActiveTabChanged(content::WebContents* old_contents, |
| int index, |
| int reason) { |
| GetWebContentsData(new_contents)->SetDiscardState(false); |
| + GetWebContentsData(new_contents)->SetPurgeAndSuspendState(RUNNING); |
| // If |old_contents| is set, that tab has switched from being active to |
| // inactive, so record the time of that transition. |
| if (old_contents) |