Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chrome / browser / memory / oom_priority_manager_delegate_chromeos.cc
blobe7f5c1aadcca42ee02e7dabc1a83f098dea32ae1
1 // Copyright 2015 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.
5 #include "chrome/browser/memory/oom_priority_manager_delegate_chromeos.h"
7 #include "base/memory/memory_pressure_monitor_chromeos.h"
8 #include "base/strings/string16.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/string_util.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "base/synchronization/lock.h"
13 #include "chrome/browser/memory/tab_stats.h"
14 #include "chrome/browser/ui/browser_list.h"
15 #include "chrome/common/chrome_constants.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "content/public/browser/notification_service.h"
18 #include "content/public/browser/notification_types.h"
19 #include "content/public/browser/render_process_host.h"
20 #include "content/public/browser/render_widget_host.h"
21 #include "content/public/browser/zygote_host_linux.h"
23 using base::TimeDelta;
24 using content::BrowserThread;
26 namespace memory {
27 namespace {
29 // When switching to a new tab the tab's renderer's OOM score needs to be
30 // updated to reflect its front-most status and protect it from discard.
31 // However, doing this immediately might slow down tab switch time, so wait
32 // a little while before doing the adjustment.
33 const int kFocusedTabScoreAdjustIntervalMs = 500;
35 } // namespace
37 OomPriorityManagerDelegate::OomPriorityManagerDelegate()
38 : focused_tab_process_info_(std::make_pair(0, 0)) {
39 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
40 content::NotificationService::AllBrowserContextsAndSources());
41 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
42 content::NotificationService::AllBrowserContextsAndSources());
43 registrar_.Add(this, content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
44 content::NotificationService::AllBrowserContextsAndSources());
47 OomPriorityManagerDelegate::~OomPriorityManagerDelegate() {
50 int OomPriorityManagerDelegate::GetOomScore(int child_process_host_id) {
51 base::AutoLock oom_score_autolock(oom_score_lock_);
52 int score = oom_score_map_[child_process_host_id];
53 return score;
56 void OomPriorityManagerDelegate::AdjustFocusedTabScoreOnFileThread() {
57 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
58 base::AutoLock oom_score_autolock(oom_score_lock_);
59 base::ProcessHandle pid = focused_tab_process_info_.second;
60 content::ZygoteHost::GetInstance()->AdjustRendererOOMScore(
61 pid, chrome::kLowestRendererOomScore);
62 oom_score_map_[focused_tab_process_info_.first] =
63 chrome::kLowestRendererOomScore;
66 void OomPriorityManagerDelegate::OnFocusTabScoreAdjustmentTimeout() {
67 BrowserThread::PostTask(
68 BrowserThread::FILE, FROM_HERE,
69 base::Bind(&OomPriorityManagerDelegate::AdjustFocusedTabScoreOnFileThread,
70 base::Unretained(this)));
72 void OomPriorityManagerDelegate::Observe(
73 int type,
74 const content::NotificationSource& source,
75 const content::NotificationDetails& details) {
76 base::AutoLock oom_score_autolock(oom_score_lock_);
77 switch (type) {
78 case content::NOTIFICATION_RENDERER_PROCESS_CLOSED:
79 case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: {
80 content::RenderProcessHost* host =
81 content::Source<content::RenderProcessHost>(source).ptr();
82 oom_score_map_.erase(host->GetID());
83 // Coming here we know that a renderer was just killed and memory should
84 // come back into the pool. However - the memory pressure observer did
85 // not yet update its status and therefore we ask it to redo the
86 // measurement, calling us again if we have to release more.
87 // Note: We do not only accelerate the discarding speed by doing another
88 // check in short succession - we also accelerate it because the timer
89 // driven MemoryPressureMonitor will continue to produce timed events
90 // on top. So the longer the cleanup phase takes, the more tabs will
91 // get discarded in parallel.
92 base::chromeos::MemoryPressureMonitor* monitor =
93 base::chromeos::MemoryPressureMonitor::Get();
94 if (monitor)
95 monitor->ScheduleEarlyCheck();
96 break;
98 case content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED: {
99 bool visible = *content::Details<bool>(details).ptr();
100 if (visible) {
101 content::RenderProcessHost* render_host =
102 content::Source<content::RenderWidgetHost>(source)
103 .ptr()
104 ->GetProcess();
105 focused_tab_process_info_ =
106 std::make_pair(render_host->GetID(), render_host->GetHandle());
108 // If the currently focused tab already has a lower score, do not
109 // set it. This can happen in case the newly focused tab is script
110 // connected to the previous tab.
111 ProcessScoreMap::iterator it;
112 it = oom_score_map_.find(focused_tab_process_info_.first);
113 if (it == oom_score_map_.end() ||
114 it->second != chrome::kLowestRendererOomScore) {
115 // By starting a timer we guarantee that the tab is focused for
116 // certain amount of time. Secondly, it also does not add overhead
117 // to the tab switching time.
118 if (focus_tab_score_adjust_timer_.IsRunning())
119 focus_tab_score_adjust_timer_.Reset();
120 else
121 focus_tab_score_adjust_timer_.Start(
122 FROM_HERE,
123 TimeDelta::FromMilliseconds(kFocusedTabScoreAdjustIntervalMs),
124 this,
125 &OomPriorityManagerDelegate::OnFocusTabScoreAdjustmentTimeout);
128 break;
130 default:
131 NOTREACHED() << "Received unexpected notification";
132 break;
136 // Here we collect most of the information we need to sort the existing
137 // renderers in priority order, and hand out oom_score_adj scores based on that
138 // sort order.
140 // Things we need to collect on the browser thread (because
141 // TabStripModel isn't thread safe):
142 // 1) whether or not a tab is pinned
143 // 2) last time a tab was selected
144 // 3) is the tab currently selected
145 void OomPriorityManagerDelegate::AdjustOomPriorities(
146 const TabStatsList& stats_list) {
147 BrowserThread::PostTask(
148 BrowserThread::FILE, FROM_HERE,
149 base::Bind(&OomPriorityManagerDelegate::AdjustOomPrioritiesOnFileThread,
150 base::Unretained(this), stats_list));
153 // static
154 std::vector<OomPriorityManagerDelegate::ProcessInfo>
155 OomPriorityManagerDelegate::GetChildProcessInfos(
156 const TabStatsList& stats_list) {
157 std::vector<ProcessInfo> process_infos;
158 std::set<base::ProcessHandle> already_seen;
159 for (TabStatsList::const_iterator iterator = stats_list.begin();
160 iterator != stats_list.end(); ++iterator) {
161 // stats_list contains entries for already-discarded tabs. If the PID
162 // (renderer_handle) is zero, we don't need to adjust the oom_score.
163 if (iterator->renderer_handle == 0)
164 continue;
166 bool inserted = already_seen.insert(iterator->renderer_handle).second;
167 if (!inserted) {
168 // We've already seen this process handle.
169 continue;
172 process_infos.push_back(std::make_pair(iterator->child_process_host_id,
173 iterator->renderer_handle));
175 return process_infos;
178 void OomPriorityManagerDelegate::AdjustOomPrioritiesOnFileThread(
179 TabStatsList stats_list) {
180 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
181 base::AutoLock oom_score_autolock(oom_score_lock_);
183 // Remove any duplicate PIDs. Order of the list is maintained, so each
184 // renderer process will take on the oom_score_adj of the most important
185 // (least likely to be killed) tab.
186 std::vector<ProcessInfo> process_infos = GetChildProcessInfos(stats_list);
188 // Now we assign priorities based on the sorted list. We're assigning
189 // priorities in the range of kLowestRendererOomScore to
190 // kHighestRendererOomScore (defined in chrome_constants.h). oom_score_adj
191 // takes values from -1000 to 1000. Negative values are reserved for system
192 // processes, and we want to give some room below the range we're using to
193 // allow for things that want to be above the renderers in priority, so the
194 // defined range gives us some variation in priority without taking up the
195 // whole range. In the end, however, it's a pretty arbitrary range to use.
196 // Higher values are more likely to be killed by the OOM killer.
197 float priority = chrome::kLowestRendererOomScore;
198 const int kPriorityRange =
199 chrome::kHighestRendererOomScore - chrome::kLowestRendererOomScore;
200 float priority_increment =
201 static_cast<float>(kPriorityRange) / process_infos.size();
202 for (const auto& process_info : process_infos) {
203 int score = static_cast<int>(priority + 0.5f);
204 ProcessScoreMap::iterator it = oom_score_map_.find(process_info.first);
205 // If a process has the same score as the newly calculated value,
206 // do not set it.
207 if (it == oom_score_map_.end() || it->second != score) {
208 content::ZygoteHost::GetInstance()->AdjustRendererOOMScore(
209 process_info.second, score);
210 oom_score_map_[process_info.first] = score;
212 priority += priority_increment;
216 } // namespace memory