Chromium Code Reviews| 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 "base/bind.h" | 13 #include "base/bind.h" |
| 14 #include "base/bind_helpers.h" | 14 #include "base/bind_helpers.h" |
| 15 #include "base/command_line.h" | 15 #include "base/command_line.h" |
| 16 #include "base/feature_list.h" | 16 #include "base/feature_list.h" |
| 17 #include "base/macros.h" | 17 #include "base/macros.h" |
| 18 #include "base/memory/memory_pressure_monitor.h" | 18 #include "base/memory/memory_pressure_monitor.h" |
| 19 #include "base/metrics/field_trial.h" | 19 #include "base/metrics/field_trial.h" |
| 20 #include "base/metrics/histogram_macros.h" | 20 #include "base/metrics/histogram_macros.h" |
| 21 #include "base/observer_list.h" | 21 #include "base/observer_list.h" |
| 22 #include "base/process/process.h" | 22 #include "base/process/process.h" |
| 23 #include "base/rand_util.h" | |
| 23 #include "base/strings/string16.h" | 24 #include "base/strings/string16.h" |
| 24 #include "base/strings/string_number_conversions.h" | 25 #include "base/strings/string_number_conversions.h" |
| 25 #include "base/strings/string_util.h" | 26 #include "base/strings/string_util.h" |
| 26 #include "base/strings/utf_string_conversions.h" | 27 #include "base/strings/utf_string_conversions.h" |
| 27 #include "base/threading/thread.h" | 28 #include "base/threading/thread.h" |
| 28 #include "base/threading/thread_task_runner_handle.h" | 29 #include "base/threading/thread_task_runner_handle.h" |
| 29 #include "base/time/tick_clock.h" | 30 #include "base/time/tick_clock.h" |
| 30 #include "build/build_config.h" | 31 #include "build/build_config.h" |
| 31 #include "chrome/browser/browser_process.h" | 32 #include "chrome/browser/browser_process.h" |
| 32 #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h" | 33 #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h" |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 77 // For each period of this length record a statistic to indicate whether or not | 78 // For each period of this length record a statistic to indicate whether or not |
| 78 // the user experienced a low memory event. If this interval is changed, | 79 // the user experienced a low memory event. If this interval is changed, |
| 79 // Tabs.Discard.DiscardInLastMinute must be replaced with a new statistic. | 80 // Tabs.Discard.DiscardInLastMinute must be replaced with a new statistic. |
| 80 const int kRecentTabDiscardIntervalSeconds = 60; | 81 const int kRecentTabDiscardIntervalSeconds = 60; |
| 81 #endif | 82 #endif |
| 82 | 83 |
| 83 // If there has been no priority adjustment in this interval, assume the | 84 // If there has been no priority adjustment in this interval, assume the |
| 84 // machine was suspended and correct the timing statistics. | 85 // machine was suspended and correct the timing statistics. |
| 85 const int kSuspendThresholdSeconds = kAdjustmentIntervalSeconds * 4; | 86 const int kSuspendThresholdSeconds = kAdjustmentIntervalSeconds * 4; |
| 86 | 87 |
| 87 // A suspended renderer is suspended for this duration. | 88 // Within the following range after the tab is backgrounded and |
| 88 constexpr base::TimeDelta kDurationOfRendererSuspension = | 89 // time_to_first_purge_ passes, purge the tab's cache if the tab is still |
| 89 base::TimeDelta::FromSeconds(1200); | 90 // backgrounded and in-active. |
|
Wez
2017/02/27 22:20:55
This comment is pretty confusingly worded!
Would
tasak
2017/02/28 09:45:37
Done.
| |
| 90 | 91 const unsigned int kRangeOfTimeToFirstPurgeInMinutes = 30; |
|
chrisha
2017/02/27 13:31:06
How is this a range? Or is it implicitly lower bou
tasak
2017/02/28 09:45:37
Done.
| |
| 91 // A resumed renderer is resumed for this duration. | |
| 92 constexpr base::TimeDelta kDurationOfRendererResumption = | |
| 93 base::TimeDelta::FromSeconds(10); | |
| 94 | 92 |
| 95 // The time during which a tab is protected from discarding after it stops being | 93 // The time during which a tab is protected from discarding after it stops being |
| 96 // audible. | 94 // audible. |
| 97 const int kAudioProtectionTimeSeconds = 60; | 95 const int kAudioProtectionTimeSeconds = 60; |
| 98 | 96 |
| 99 int FindTabStripModelById(int64_t target_web_contents_id, | 97 int FindTabStripModelById(int64_t target_web_contents_id, |
| 100 TabStripModel** model) { | 98 TabStripModel** model) { |
| 101 DCHECK(model); | 99 DCHECK(model); |
| 102 for (auto* browser : *BrowserList::GetInstance()) { | 100 for (auto* browser : *BrowserList::GetInstance()) { |
| 103 TabStripModel* local_model = browser->tab_strip_model(); | 101 TabStripModel* local_model = browser->tab_strip_model(); |
| (...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 224 } | 222 } |
| 225 #endif | 223 #endif |
| 226 // purge-and-suspend param is used for Purge+Suspend finch experiment | 224 // purge-and-suspend param is used for Purge+Suspend finch experiment |
| 227 // in the following way: | 225 // in the following way: |
| 228 // https://docs.google.com/document/d/1hPHkKtXXBTlsZx9s-9U17XC-ofEIzPo9FYbBEc7 PPbk/edit?usp=sharing | 226 // https://docs.google.com/document/d/1hPHkKtXXBTlsZx9s-9U17XC-ofEIzPo9FYbBEc7 PPbk/edit?usp=sharing |
| 229 std::string purge_and_suspend_time = variations::GetVariationParamValue( | 227 std::string purge_and_suspend_time = variations::GetVariationParamValue( |
| 230 "PurgeAndSuspend", "purge-and-suspend-time"); | 228 "PurgeAndSuspend", "purge-and-suspend-time"); |
| 231 unsigned int time_to_first_purge_sec = 0; | 229 unsigned int time_to_first_purge_sec = 0; |
| 232 if (purge_and_suspend_time.empty() || | 230 if (purge_and_suspend_time.empty() || |
| 233 !base::StringToUint(purge_and_suspend_time, &time_to_first_purge_sec)) | 231 !base::StringToUint(purge_and_suspend_time, &time_to_first_purge_sec)) |
| 234 time_to_first_suspension_ = kDefaultTimeToFirstPurge; | 232 time_to_first_purge_ = kDefaultTimeToFirstPurge; |
| 235 else | 233 else |
| 236 time_to_first_suspension_ = | 234 time_to_first_purge_ = |
| 237 base::TimeDelta::FromSeconds(time_to_first_purge_sec); | 235 base::TimeDelta::FromSeconds(time_to_first_purge_sec); |
| 238 } | 236 } |
| 239 | 237 |
| 240 void TabManager::Stop() { | 238 void TabManager::Stop() { |
| 241 update_timer_.Stop(); | 239 update_timer_.Stop(); |
| 242 recent_tab_discard_timer_.Stop(); | 240 recent_tab_discard_timer_.Stop(); |
| 243 memory_pressure_listener_.reset(); | 241 memory_pressure_listener_.reset(); |
| 244 } | 242 } |
| 245 | 243 |
| 246 TabStatsList TabManager::GetTabStats() const { | 244 TabStatsList TabManager::GetTabStats() const { |
| (...skipping 433 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 680 stats.title = contents->GetTitle(); | 678 stats.title = contents->GetTitle(); |
| 681 stats.tab_contents_id = IdFromWebContents(contents); | 679 stats.tab_contents_id = IdFromWebContents(contents); |
| 682 stats_list->push_back(stats); | 680 stats_list->push_back(stats); |
| 683 } | 681 } |
| 684 } | 682 } |
| 685 } | 683 } |
| 686 | 684 |
| 687 // This function is called when |update_timer_| fires. It will adjust the clock | 685 // This function is called when |update_timer_| fires. It will adjust the clock |
| 688 // if needed (if it detects that the machine was asleep) and will fire the stats | 686 // if needed (if it detects that the machine was asleep) and will fire the stats |
| 689 // updating on ChromeOS via the delegate. This function also tries to purge | 687 // updating on ChromeOS via the delegate. This function also tries to purge |
| 690 // cache memory and suspend tabs which becomes and keeps backgrounded for a | 688 // cache memory and suspend tabs which becomes and keeps backgrounded for a |
|
Wez
2017/02/27 22:20:55
nit: Looks like this comment regarding purging is
tasak
2017/02/28 09:45:37
Done.
| |
| 691 // while. | 689 // while. |
| 692 void TabManager::UpdateTimerCallback() { | 690 void TabManager::UpdateTimerCallback() { |
| 693 // If Chrome is shutting down, do not do anything. | 691 // If Chrome is shutting down, do not do anything. |
| 694 if (g_browser_process->IsShuttingDown()) | 692 if (g_browser_process->IsShuttingDown()) |
| 695 return; | 693 return; |
| 696 | 694 |
| 697 if (BrowserList::GetInstance()->empty()) | 695 if (BrowserList::GetInstance()->empty()) |
| 698 return; | 696 return; |
| 699 | 697 |
| 700 // Check for a discontinuity in time caused by the machine being suspended. | 698 // Check for a discontinuity in time caused by the machine being suspended. |
| 701 if (!last_adjust_time_.is_null()) { | 699 if (!last_adjust_time_.is_null()) { |
| 702 TimeDelta suspend_time = NowTicks() - last_adjust_time_; | 700 TimeDelta suspend_time = NowTicks() - last_adjust_time_; |
| 703 if (suspend_time.InSeconds() > kSuspendThresholdSeconds) { | 701 if (suspend_time.InSeconds() > kSuspendThresholdSeconds) { |
| 704 // System was probably suspended, move the event timers forward in time so | 702 // System was probably suspended, move the event timers forward in time so |
| 705 // when they get subtracted out later, "uptime" is being counted. | 703 // when they get subtracted out later, "uptime" is being counted. |
| 706 start_time_ += suspend_time; | 704 start_time_ += suspend_time; |
| 707 if (!last_discard_time_.is_null()) | 705 if (!last_discard_time_.is_null()) |
| 708 last_discard_time_ += suspend_time; | 706 last_discard_time_ += suspend_time; |
| 709 } | 707 } |
|
tasak
2017/02/28 09:45:37
Now we don't suspend any renderers. So we can remo
| |
| 710 } | 708 } |
| 711 last_adjust_time_ = NowTicks(); | 709 last_adjust_time_ = NowTicks(); |
| 712 | 710 |
| 713 #if defined(OS_CHROMEOS) | 711 #if defined(OS_CHROMEOS) |
| 714 TabStatsList stats_list = GetTabStats(); | 712 TabStatsList stats_list = GetTabStats(); |
| 715 // This starts the CrOS specific OOM adjustments in /proc/<pid>/oom_score_adj. | 713 // This starts the CrOS specific OOM adjustments in /proc/<pid>/oom_score_adj. |
| 716 delegate_->AdjustOomPriorities(stats_list); | 714 delegate_->AdjustOomPriorities(stats_list); |
| 717 #endif | 715 #endif |
| 718 | 716 |
| 719 PurgeAndSuspendBackgroundedTabs(); | 717 PurgeBackgroundedTabs(); |
| 720 } | 718 } |
| 721 | 719 |
| 722 TabManager::PurgeAndSuspendState TabManager::GetNextPurgeAndSuspendState( | 720 TabManager::PurgeState TabManager::GetNextPurgeState( |
| 723 content::WebContents* content, | 721 content::WebContents* content, |
| 724 base::TimeTicks current_time, | 722 base::TimeTicks current_time) const { |
| 725 const base::TimeDelta& time_to_first_suspension) const { | |
| 726 DCHECK(content); | 723 DCHECK(content); |
|
Wez
2017/02/27 22:20:55
You don't need a DCHECK here, since you're about t
tasak
2017/02/28 09:45:37
Done.
| |
| 727 PurgeAndSuspendState state = | 724 PurgeState state = GetWebContentsData(content)->GetPurgeState(); |
| 728 GetWebContentsData(content)->GetPurgeAndSuspendState(); | 725 if (state == PURGED) |
| 726 return state; | |
| 729 | 727 |
| 730 auto time_passed = current_time - | 728 DCHECK(state == NOT_PURGED); |
|
Wez
2017/02/27 22:20:55
This is a tautologous DCHECK, since there are only
tasak
2017/02/28 09:45:37
Done.
| |
| 731 GetWebContentsData(content)->LastPurgeAndSuspendModifiedTime(); | 729 auto time_passed = |
|
Wez
2017/02/27 22:20:55
There's no need to use |auto| here; it makes the c
tasak
2017/02/28 09:45:37
Done.
| |
| 732 switch (state) { | 730 current_time - GetWebContentsData(content)->LastInactiveTime(); |
| 733 case RUNNING: | 731 if (time_passed > GetWebContentsData(content)->TimeToFirstPurge()) |
| 734 if (time_passed > time_to_first_suspension) | 732 return PURGED; |
| 735 return SUSPENDED; | |
| 736 break; | |
| 737 case RESUMED: | |
| 738 if (time_passed > kDurationOfRendererResumption) | |
| 739 return SUSPENDED; | |
| 740 break; | |
| 741 case SUSPENDED: | |
| 742 if (time_passed > kDurationOfRendererSuspension) | |
| 743 return RESUMED; | |
| 744 break; | |
| 745 } | |
| 746 return state; | 733 return state; |
| 747 } | 734 } |
| 748 | 735 |
| 749 void TabManager::PurgeAndSuspendBackgroundedTabs() { | 736 void TabManager::PurgeBackgroundedTabs() { |
| 750 base::TimeTicks current_time = NowTicks(); | 737 base::TimeTicks current_time = NowTicks(); |
| 751 auto tab_stats = GetUnsortedTabStats(); | 738 auto tab_stats = GetUnsortedTabStats(); |
| 752 for (auto& tab : tab_stats) { | 739 for (auto& tab : tab_stats) { |
| 753 if (!tab.render_process_host->IsProcessBackgrounded()) | 740 if (!tab.render_process_host->IsProcessBackgrounded()) |
| 754 continue; | 741 continue; |
| 755 if (!CanSuspendBackgroundedRenderer(tab.child_process_host_id)) | 742 if (!CanSuspendBackgroundedRenderer(tab.child_process_host_id)) |
| 756 continue; | 743 continue; |
| 757 | 744 |
| 758 WebContents* content = GetWebContentsById(tab.tab_contents_id); | 745 WebContents* content = GetWebContentsById(tab.tab_contents_id); |
| 759 if (!content) | 746 if (!content) |
| 760 continue; | 747 continue; |
| 761 | 748 |
| 762 PurgeAndSuspendState current_state = | 749 PurgeState current_state = GetWebContentsData(content)->GetPurgeState(); |
| 763 GetWebContentsData(content)->GetPurgeAndSuspendState(); | 750 PurgeState next_state = GetNextPurgeState(content, current_time); |
| 764 // If the tab's purge-and-suspend state is not RUNNING, the tab should be | |
| 765 // backgrounded. Since tab.last_hidden is updated everytime the tab is | |
| 766 // hidden, we should see tab.last_hidden < last_modified_time. | |
| 767 DCHECK(current_state == RUNNING || | |
| 768 tab.last_hidden < | |
| 769 GetWebContentsData(content)->LastPurgeAndSuspendModifiedTime()); | |
| 770 PurgeAndSuspendState next_state = GetNextPurgeAndSuspendState( | |
| 771 content, current_time, time_to_first_suspension_); | |
| 772 if (current_state == next_state) | 751 if (current_state == next_state) |
| 773 continue; | 752 continue; |
| 774 | 753 |
| 775 // TODO(hajimehoshi): Now calling PurgeAndSuspend is implemented without | 754 // TODO(hajimehoshi): Now calling PurgeAndSuspend is implemented without |
| 776 // timers for simplicity, so PurgeAndSuspend is called even after the | 755 // timers for simplicity, so PurgeAndSuspend is called even after the |
| 777 // renderer is purged and suspended once. This should be replaced with | 756 // renderer is purged and suspended once. This should be replaced with |
| 778 // timers if we want necessary and sufficient signals. | 757 // timers if we want necessary and sufficient signals. |
|
Wez
2017/02/27 22:20:55
It's not clear what this (pre-existing) comment is
tasak
2017/02/28 09:45:37
Done.
| |
| 779 GetWebContentsData(content)->SetPurgeAndSuspendState(next_state); | 758 GetWebContentsData(content)->SetPurgeState(next_state); |
| 780 switch (next_state) { | 759 DCHECK(next_state == PURGED); |
|
Wez
2017/02/27 22:20:55
As noted in the previous function, you can replace
tasak
2017/02/28 09:45:37
Done.
| |
| 781 case SUSPENDED: | 760 // TODO(tasak): rename PurgeAndSuspend with a better name, e.g. |
| 782 tab.render_process_host->PurgeAndSuspend(); | 761 // RequestPurgeCache, because we don't suspend any renderers. |
| 783 break; | 762 tab.render_process_host->PurgeAndSuspend(); |
| 784 case RESUMED: | |
| 785 tab.render_process_host->Resume(); | |
| 786 break; | |
| 787 case RUNNING: | |
| 788 NOTREACHED(); | |
| 789 } | |
| 790 } | 763 } |
| 791 } | 764 } |
| 792 | 765 |
| 793 WebContents* TabManager::DiscardWebContentsAt(int index, TabStripModel* model) { | 766 WebContents* TabManager::DiscardWebContentsAt(int index, TabStripModel* model) { |
| 794 // Can't discard active index. | 767 // Can't discard active index. |
| 795 if (model->active_index() == index) | 768 if (model->active_index() == index) |
| 796 return nullptr; | 769 return nullptr; |
| 797 | 770 |
| 798 WebContents* old_contents = model->GetWebContentsAt(index); | 771 WebContents* old_contents = model->GetWebContentsAt(index); |
| 799 | 772 |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 880 data->SetRecentlyAudible(current_state); | 853 data->SetRecentlyAudible(current_state); |
| 881 data->SetLastAudioChangeTime(NowTicks()); | 854 data->SetLastAudioChangeTime(NowTicks()); |
| 882 } | 855 } |
| 883 } | 856 } |
| 884 | 857 |
| 885 void TabManager::ActiveTabChanged(content::WebContents* old_contents, | 858 void TabManager::ActiveTabChanged(content::WebContents* old_contents, |
| 886 content::WebContents* new_contents, | 859 content::WebContents* new_contents, |
| 887 int index, | 860 int index, |
| 888 int reason) { | 861 int reason) { |
| 889 GetWebContentsData(new_contents)->SetDiscardState(false); | 862 GetWebContentsData(new_contents)->SetDiscardState(false); |
| 890 GetWebContentsData(new_contents)->SetPurgeAndSuspendState(RUNNING); | 863 // To avoid purging many processes' cache at the same time, we will use |
| 864 // time_to_first_purge_ + random value with in the range | |
| 865 // kRangeOfTimeToFirstPurgeInMinutes to invoke purge. | |
|
Wez
2017/02/27 22:20:55
I think the more important thing to get across in
tasak
2017/02/28 09:45:37
Moved the code into the statement: if(old_contents
| |
| 866 base::TimeDelta time_to_first_purge = | |
| 867 time_to_first_purge_ + | |
| 868 base::TimeDelta::FromMinutes( | |
| 869 base::RandGenerator(kRangeOfTimeToFirstPurgeInMinutes)); | |
| 870 GetWebContentsData(new_contents)->SetTimeToFirstPurge(time_to_first_purge); | |
|
Wez
2017/02/27 22:20:55
nit: I'd suggest calling this SetTimeToPurge(), so
tasak
2017/02/28 09:45:37
Done.
| |
| 871 GetWebContentsData(new_contents)->SetPurgeState(NOT_PURGED); | |
| 891 // If |old_contents| is set, that tab has switched from being active to | 872 // If |old_contents| is set, that tab has switched from being active to |
| 892 // inactive, so record the time of that transition. | 873 // inactive, so record the time of that transition. |
| 893 if (old_contents) | 874 if (old_contents) |
| 894 GetWebContentsData(old_contents)->SetLastInactiveTime(NowTicks()); | 875 GetWebContentsData(old_contents)->SetLastInactiveTime(NowTicks()); |
|
Wez
2017/02/27 22:20:55
Again, this is pre-existing code, but shouldn't th
tasak
2017/02/28 09:45:37
As far as I understand, new_contents is a tab whic
| |
| 895 } | 876 } |
| 896 | 877 |
| 897 void TabManager::TabInsertedAt(TabStripModel* tab_strip_model, | 878 void TabManager::TabInsertedAt(TabStripModel* tab_strip_model, |
| 898 content::WebContents* contents, | 879 content::WebContents* contents, |
| 899 int index, | 880 int index, |
| 900 bool foreground) { | 881 bool foreground) { |
| 901 // Only interested in background tabs, as foreground tabs get taken care of by | 882 // Only interested in background tabs, as foreground tabs get taken care of by |
| 902 // ActiveTabChanged. | 883 // ActiveTabChanged. |
| 903 if (foreground) | 884 if (foreground) |
| 904 return; | 885 return; |
| (...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1032 // platform. | 1013 // platform. |
| 1033 std::string allow_multiple_discards = variations::GetVariationParamValue( | 1014 std::string allow_multiple_discards = variations::GetVariationParamValue( |
| 1034 features::kAutomaticTabDiscarding.name, "AllowMultipleDiscards"); | 1015 features::kAutomaticTabDiscarding.name, "AllowMultipleDiscards"); |
| 1035 return (allow_multiple_discards != "true"); | 1016 return (allow_multiple_discards != "true"); |
| 1036 #else | 1017 #else |
| 1037 return false; | 1018 return false; |
| 1038 #endif | 1019 #endif |
| 1039 } | 1020 } |
| 1040 | 1021 |
| 1041 } // namespace memory | 1022 } // namespace memory |
| OLD | NEW |