OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 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. | |
4 | |
5 #include "content/browser/renderer_host/render_process_host.h" | |
6 | |
7 #include "base/command_line.h" | |
8 #include "base/lazy_instance.h" | |
9 #include "base/rand_util.h" | |
10 #include "base/sys_info.h" | |
11 #include "content/browser/browser_main.h" | |
12 #include "content/browser/child_process_security_policy.h" | |
13 #include "content/browser/webui/web_ui_factory.h" | |
14 #include "content/common/child_process_info.h" | |
15 #include "content/public/browser/browser_thread.h" | |
16 #include "content/public/browser/content_browser_client.h" | |
17 #include "content/public/browser/notification_service.h" | |
18 #include "content/public/browser/notification_types.h" | |
19 #include "content/public/common/content_constants.h" | |
20 #include "content/public/common/content_switches.h" | |
21 | |
22 using content::BrowserThread; | |
23 | |
24 namespace { | |
25 | |
26 size_t max_renderer_count_override = 0; | |
27 | |
28 size_t GetMaxRendererProcessCount() { | |
29 if (max_renderer_count_override) | |
30 return max_renderer_count_override; | |
31 | |
32 // Defines the maximum number of renderer processes according to the | |
33 // amount of installed memory as reported by the OS. The table | |
34 // values are calculated by assuming that you want the renderers to | |
35 // use half of the installed ram and assuming that each tab uses | |
36 // ~40MB, however the curve is not linear but piecewise linear with | |
37 // interleaved slopes of 3 and 2. | |
38 // If you modify this table you need to adjust browser\browser_uitest.cc | |
39 // to match the expected number of processes. | |
40 | |
41 static const size_t kMaxRenderersByRamTier[] = { | |
42 3, // less than 256MB | |
43 6, // 256MB | |
44 9, // 512MB | |
45 12, // 768MB | |
46 14, // 1024MB | |
47 18, // 1280MB | |
48 20, // 1536MB | |
49 22, // 1792MB | |
50 24, // 2048MB | |
51 26, // 2304MB | |
52 29, // 2560MB | |
53 32, // 2816MB | |
54 35, // 3072MB | |
55 38, // 3328MB | |
56 40 // 3584MB | |
57 }; | |
58 | |
59 static size_t max_count = 0; | |
60 if (!max_count) { | |
61 size_t memory_tier = base::SysInfo::AmountOfPhysicalMemoryMB() / 256; | |
62 if (memory_tier >= arraysize(kMaxRenderersByRamTier)) | |
63 max_count = content::kMaxRendererProcessCount; | |
64 else | |
65 max_count = kMaxRenderersByRamTier[memory_tier]; | |
66 } | |
67 return max_count; | |
68 } | |
69 | |
70 // Returns true if the given host is suitable for launching a new view | |
71 // associated with the given browser context. | |
72 static bool IsSuitableHost(RenderProcessHost* host, | |
73 content::BrowserContext* browser_context, | |
74 const GURL& site_url) { | |
75 if (host->browser_context() != browser_context) | |
76 return false; | |
77 | |
78 if (ChildProcessSecurityPolicy::GetInstance()->HasWebUIBindings(host->id()) != | |
79 content::WebUIFactory::Get()->HasWebUIScheme(site_url)) | |
80 return false; | |
81 | |
82 return content::GetContentClient()->browser()->IsSuitableHost(host, site_url); | |
83 } | |
84 | |
85 // the global list of all renderer processes | |
86 base::LazyInstance<IDMap<RenderProcessHost>, | |
87 base::LeakyLazyInstanceTraits<IDMap<RenderProcessHost> > > | |
88 g_all_hosts = LAZY_INSTANCE_INITIALIZER; | |
89 | |
90 } // namespace | |
91 | |
92 // static | |
93 bool RenderProcessHost::run_renderer_in_process_ = false; | |
94 | |
95 // static | |
96 void RenderProcessHost::SetMaxRendererProcessCountForTest(size_t count) { | |
97 max_renderer_count_override = count; | |
98 } | |
99 | |
100 RenderProcessHost::RenderProcessHost(content::BrowserContext* browser_context) | |
101 : max_page_id_(-1), | |
102 fast_shutdown_started_(false), | |
103 deleting_soon_(false), | |
104 pending_views_(0), | |
105 id_(ChildProcessInfo::GenerateChildProcessUniqueId()), | |
106 browser_context_(browser_context), | |
107 sudden_termination_allowed_(true), | |
108 ignore_input_events_(false) { | |
109 CHECK(!content::ExitedMainMessageLoop()); | |
110 g_all_hosts.Get().AddWithID(this, id()); | |
111 g_all_hosts.Get().set_check_on_null_data(true); | |
112 // Initialize |child_process_activity_time_| to a reasonable value. | |
113 mark_child_process_activity_time(); | |
114 } | |
115 | |
116 RenderProcessHost::~RenderProcessHost() { | |
117 // In unit tests, Release() might not have been called. | |
118 if (g_all_hosts.Get().Lookup(id())) | |
119 g_all_hosts.Get().Remove(id()); | |
120 } | |
121 | |
122 bool RenderProcessHost::HasConnection() const { | |
123 return channel_.get() != NULL; | |
124 } | |
125 | |
126 void RenderProcessHost::Attach(IPC::Channel::Listener* listener, | |
127 int routing_id) { | |
128 listeners_.AddWithID(listener, routing_id); | |
129 } | |
130 | |
131 void RenderProcessHost::Release(int listener_id) { | |
132 DCHECK(listeners_.Lookup(listener_id) != NULL); | |
133 listeners_.Remove(listener_id); | |
134 | |
135 // Make sure that all associated resource requests are stopped. | |
136 CancelResourceRequests(listener_id); | |
137 | |
138 #if defined(OS_WIN) | |
139 // Dump the handle table if handle auditing is enabled. | |
140 const CommandLine& browser_command_line = | |
141 *CommandLine::ForCurrentProcess(); | |
142 if (browser_command_line.HasSwitch(switches::kAuditHandles) || | |
143 browser_command_line.HasSwitch(switches::kAuditAllHandles)) { | |
144 DumpHandles(); | |
145 | |
146 // We wait to close the channels until the child process has finished | |
147 // dumping handles and sends us ChildProcessHostMsg_DumpHandlesDone. | |
148 return; | |
149 } | |
150 #endif | |
151 Cleanup(); | |
152 } | |
153 | |
154 void RenderProcessHost::Cleanup() { | |
155 // When no other owners of this object, we can delete ourselves | |
156 if (listeners_.IsEmpty()) { | |
157 content::NotificationService::current()->Notify( | |
158 content::NOTIFICATION_RENDERER_PROCESS_TERMINATED, | |
159 content::Source<RenderProcessHost>(this), | |
160 content::NotificationService::NoDetails()); | |
161 MessageLoop::current()->DeleteSoon(FROM_HERE, this); | |
162 deleting_soon_ = true; | |
163 // It's important not to wait for the DeleteTask to delete the channel | |
164 // proxy. Kill it off now. That way, in case the profile is going away, the | |
165 // rest of the objects attached to this RenderProcessHost start going | |
166 // away first, since deleting the channel proxy will post a | |
167 // OnChannelClosed() to IPC::ChannelProxy::Context on the IO thread. | |
168 channel_.reset(); | |
169 | |
170 // Remove ourself from the list of renderer processes so that we can't be | |
171 // reused in between now and when the Delete task runs. | |
172 g_all_hosts.Get().Remove(id()); | |
173 } | |
174 } | |
175 | |
176 void RenderProcessHost::ReportExpectingClose(int32 listener_id) { | |
177 listeners_expecting_close_.insert(listener_id); | |
178 } | |
179 | |
180 void RenderProcessHost::AddPendingView() { | |
181 pending_views_++; | |
182 } | |
183 | |
184 void RenderProcessHost::RemovePendingView() { | |
185 DCHECK(pending_views_); | |
186 pending_views_--; | |
187 } | |
188 | |
189 void RenderProcessHost::UpdateMaxPageID(int32 page_id) { | |
190 if (page_id > max_page_id_) | |
191 max_page_id_ = page_id; | |
192 } | |
193 | |
194 bool RenderProcessHost::FastShutdownForPageCount(size_t count) { | |
195 if (listeners_.size() == count) | |
196 return FastShutdownIfPossible(); | |
197 return false; | |
198 } | |
199 | |
200 // static | |
201 RenderProcessHost::iterator RenderProcessHost::AllHostsIterator() { | |
202 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
203 return iterator(g_all_hosts.Pointer()); | |
204 } | |
205 | |
206 // static | |
207 RenderProcessHost* RenderProcessHost::FromID(int render_process_id) { | |
208 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
209 return g_all_hosts.Get().Lookup(render_process_id); | |
210 } | |
211 | |
212 // static | |
213 bool RenderProcessHost::ShouldTryToUseExistingProcessHost() { | |
214 size_t renderer_process_count = g_all_hosts.Get().size(); | |
215 | |
216 // NOTE: Sometimes it's necessary to create more render processes than | |
217 // GetMaxRendererProcessCount(), for instance when we want to create | |
218 // a renderer process for a browser context that has no existing | |
219 // renderers. This is OK in moderation, since the | |
220 // GetMaxRendererProcessCount() is conservative. | |
221 | |
222 return run_renderer_in_process() || | |
223 (renderer_process_count >= GetMaxRendererProcessCount()); | |
224 } | |
225 | |
226 // static | |
227 RenderProcessHost* RenderProcessHost::GetExistingProcessHost( | |
228 content::BrowserContext* browser_context, | |
229 const GURL& site_url) { | |
230 // First figure out which existing renderers we can use. | |
231 std::vector<RenderProcessHost*> suitable_renderers; | |
232 suitable_renderers.reserve(g_all_hosts.Get().size()); | |
233 | |
234 iterator iter(AllHostsIterator()); | |
235 while (!iter.IsAtEnd()) { | |
236 if (run_renderer_in_process() || | |
237 IsSuitableHost(iter.GetCurrentValue(), browser_context, site_url)) | |
238 suitable_renderers.push_back(iter.GetCurrentValue()); | |
239 | |
240 iter.Advance(); | |
241 } | |
242 | |
243 // Now pick a random suitable renderer, if we have any. | |
244 if (!suitable_renderers.empty()) { | |
245 int suitable_count = static_cast<int>(suitable_renderers.size()); | |
246 int random_index = base::RandInt(0, suitable_count - 1); | |
247 return suitable_renderers[random_index]; | |
248 } | |
249 | |
250 return NULL; | |
251 } | |
OLD | NEW |