OLD | NEW |
| (Empty) |
1 // Copyright (c) 2010 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 "chrome/browser/gpu_process_host.h" | |
6 | |
7 #include "app/app_switches.h" | |
8 #include "base/metrics/histogram.h" | |
9 #include "base/ref_counted.h" | |
10 #include "base/string_piece.h" | |
11 #include "base/threading/thread.h" | |
12 #include "chrome/browser/browser_thread.h" | |
13 #include "chrome/browser/gpu_process_host_ui_shim.h" | |
14 #include "chrome/browser/renderer_host/render_widget_host.h" | |
15 #include "chrome/browser/renderer_host/render_widget_host_view.h" | |
16 #include "chrome/browser/tab_contents/render_view_host_delegate_helper.h" | |
17 #include "chrome/common/chrome_switches.h" | |
18 #include "chrome/common/gpu_feature_flags.h" | |
19 #include "chrome/common/gpu_info.h" | |
20 #include "chrome/common/gpu_messages.h" | |
21 #include "chrome/common/render_messages.h" | |
22 #include "chrome/gpu/gpu_thread.h" | |
23 #include "ipc/ipc_channel_handle.h" | |
24 #include "ipc/ipc_switches.h" | |
25 #include "media/base/media_switches.h" | |
26 | |
27 #if defined(OS_LINUX) | |
28 #include "ui/gfx/gtk_native_view_id_manager.h" | |
29 #endif // defined(OS_LINUX) | |
30 | |
31 namespace { | |
32 | |
33 enum GPUProcessLifetimeEvent { | |
34 LAUNCHED, | |
35 DIED_FIRST_TIME, | |
36 DIED_SECOND_TIME, | |
37 DIED_THIRD_TIME, | |
38 DIED_FOURTH_TIME, | |
39 GPU_PROCESS_LIFETIME_EVENT_MAX | |
40 }; | |
41 | |
42 class RouteOnUIThreadTask : public Task { | |
43 public: | |
44 RouteOnUIThreadTask(int host_id, const IPC::Message& msg) | |
45 : host_id_(host_id), | |
46 msg_(msg) { | |
47 } | |
48 | |
49 private: | |
50 virtual void Run() { | |
51 GpuProcessHostUIShim* ui_shim = GpuProcessHostUIShim::FromID(host_id_); | |
52 if (ui_shim) | |
53 ui_shim->OnMessageReceived(msg_); | |
54 } | |
55 | |
56 int host_id_; | |
57 IPC::Message msg_; | |
58 }; | |
59 | |
60 // A global map from GPU process host ID to GpuProcessHost. | |
61 static IDMap<GpuProcessHost> g_hosts_by_id; | |
62 | |
63 // Number of times the gpu process has crashed in the current browser session. | |
64 static int g_gpu_crash_count = 0; | |
65 | |
66 // Maximum number of times the gpu process is allowed to crash in a session. | |
67 // Once this limit is reached, any request to launch the gpu process will fail. | |
68 static const int kGpuMaxCrashCount = 3; | |
69 | |
70 } // anonymous namespace | |
71 | |
72 class GpuMainThread : public base::Thread { | |
73 public: | |
74 explicit GpuMainThread(const std::string& channel_id) | |
75 : base::Thread("CrGpuMain"), | |
76 channel_id_(channel_id) { | |
77 } | |
78 | |
79 ~GpuMainThread() { | |
80 Stop(); | |
81 } | |
82 | |
83 protected: | |
84 virtual void Init() { | |
85 // Must be created on GPU thread. | |
86 gpu_thread_.reset(new GpuThread(channel_id_)); | |
87 gpu_thread_->Init(base::Time::Now()); | |
88 } | |
89 | |
90 virtual void CleanUp() { | |
91 // Must be destroyed on GPU thread. | |
92 gpu_thread_.reset(); | |
93 } | |
94 | |
95 private: | |
96 scoped_ptr<GpuThread> gpu_thread_; | |
97 std::string channel_id_; | |
98 DISALLOW_COPY_AND_ASSIGN(GpuMainThread); | |
99 }; | |
100 | |
101 // static | |
102 GpuProcessHost* GpuProcessHost::Create(int host_id) { | |
103 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
104 | |
105 GpuProcessHost* host = new GpuProcessHost(host_id); | |
106 if (!host->Init()) { | |
107 delete host; | |
108 return NULL; | |
109 } | |
110 | |
111 return host; | |
112 } | |
113 | |
114 // static | |
115 GpuProcessHost* GpuProcessHost::FromID(int host_id) { | |
116 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
117 | |
118 if (host_id == 0) | |
119 return NULL; | |
120 | |
121 return g_hosts_by_id.Lookup(host_id); | |
122 } | |
123 | |
124 GpuProcessHost::GpuProcessHost(int host_id) | |
125 : BrowserChildProcessHost(GPU_PROCESS, NULL), | |
126 host_id_(host_id) { | |
127 g_hosts_by_id.AddWithID(this, host_id_); | |
128 } | |
129 | |
130 GpuProcessHost::~GpuProcessHost() { | |
131 | |
132 DCHECK(CalledOnValidThread()); | |
133 | |
134 g_hosts_by_id.Remove(host_id_); | |
135 | |
136 BrowserThread::PostTask(BrowserThread::UI, | |
137 FROM_HERE, | |
138 NewRunnableFunction(GpuProcessHostUIShim::Destroy, | |
139 host_id_)); | |
140 } | |
141 | |
142 bool GpuProcessHost::Init() { | |
143 if (!CreateChannel()) | |
144 return false; | |
145 | |
146 if (!CanLaunchGpuProcess()) | |
147 return false; | |
148 | |
149 if (!LaunchGpuProcess()) | |
150 return false; | |
151 | |
152 return Send(new GpuMsg_Initialize()); | |
153 } | |
154 | |
155 void GpuProcessHost::RouteOnUIThread(const IPC::Message& message) { | |
156 BrowserThread::PostTask(BrowserThread::UI, | |
157 FROM_HERE, | |
158 new RouteOnUIThreadTask(host_id_, message)); | |
159 } | |
160 | |
161 bool GpuProcessHost::Send(IPC::Message* msg) { | |
162 DCHECK(CalledOnValidThread()); | |
163 return BrowserChildProcessHost::Send(msg); | |
164 } | |
165 | |
166 bool GpuProcessHost::OnMessageReceived(const IPC::Message& message) { | |
167 DCHECK(CalledOnValidThread()); | |
168 RouteOnUIThread(message); | |
169 return true; | |
170 } | |
171 | |
172 bool GpuProcessHost::CanShutdown() { | |
173 return true; | |
174 } | |
175 | |
176 namespace { | |
177 | |
178 void SendOutstandingRepliesDispatcher(int host_id) { | |
179 GpuProcessHostUIShim *ui_shim = GpuProcessHostUIShim::FromID(host_id); | |
180 DCHECK(ui_shim); | |
181 ui_shim->SendOutstandingReplies(); | |
182 } | |
183 | |
184 void SendOutstandingReplies(int host_id) { | |
185 BrowserThread::PostTask( | |
186 BrowserThread::UI, FROM_HERE, | |
187 NewRunnableFunction(&SendOutstandingRepliesDispatcher, host_id)); | |
188 } | |
189 | |
190 } // namespace | |
191 | |
192 void GpuProcessHost::OnChildDied() { | |
193 SendOutstandingReplies(host_id_); | |
194 // Located in OnChildDied because OnProcessCrashed suffers from a race | |
195 // condition on Linux. | |
196 UMA_HISTOGRAM_ENUMERATION("GPU.GPUProcessLifetimeEvents", | |
197 DIED_FIRST_TIME + g_gpu_crash_count, | |
198 GPU_PROCESS_LIFETIME_EVENT_MAX); | |
199 BrowserChildProcessHost::OnChildDied(); | |
200 } | |
201 | |
202 void GpuProcessHost::OnProcessCrashed(int exit_code) { | |
203 SendOutstandingReplies(host_id_); | |
204 if (++g_gpu_crash_count >= kGpuMaxCrashCount) { | |
205 // The gpu process is too unstable to use. Disable it for current session. | |
206 RenderViewHostDelegateHelper::set_gpu_enabled(false); | |
207 } | |
208 BrowserChildProcessHost::OnProcessCrashed(exit_code); | |
209 } | |
210 | |
211 bool GpuProcessHost::CanLaunchGpuProcess() const { | |
212 return RenderViewHostDelegateHelper::gpu_enabled(); | |
213 } | |
214 | |
215 bool GpuProcessHost::LaunchGpuProcess() { | |
216 if (g_gpu_crash_count >= kGpuMaxCrashCount) | |
217 return false; | |
218 | |
219 const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess(); | |
220 | |
221 // If the single-process switch is present, just launch the GPU service in a | |
222 // new thread in the browser process. | |
223 if (browser_command_line.HasSwitch(switches::kSingleProcess)) { | |
224 GpuMainThread* thread = new GpuMainThread(channel_id()); | |
225 | |
226 base::Thread::Options options; | |
227 #if defined(OS_LINUX) | |
228 options.message_loop_type = MessageLoop::TYPE_IO; | |
229 #else | |
230 options.message_loop_type = MessageLoop::TYPE_UI; | |
231 #endif | |
232 | |
233 if (!thread->StartWithOptions(options)) | |
234 return false; | |
235 | |
236 return true; | |
237 } | |
238 | |
239 CommandLine::StringType gpu_launcher = | |
240 browser_command_line.GetSwitchValueNative(switches::kGpuLauncher); | |
241 | |
242 FilePath exe_path = ChildProcessHost::GetChildPath(gpu_launcher.empty()); | |
243 if (exe_path.empty()) | |
244 return false; | |
245 | |
246 CommandLine* cmd_line = new CommandLine(exe_path); | |
247 cmd_line->AppendSwitchASCII(switches::kProcessType, switches::kGpuProcess); | |
248 cmd_line->AppendSwitchASCII(switches::kProcessChannelID, channel_id()); | |
249 | |
250 SetCrashReporterCommandLine(cmd_line); | |
251 | |
252 // Propagate relevant command line switches. | |
253 static const char* const kSwitchNames[] = { | |
254 switches::kUseGL, | |
255 switches::kDisableGpuVsync, | |
256 switches::kDisableGpuWatchdog, | |
257 switches::kDisableLogging, | |
258 switches::kEnableAcceleratedDecoding, | |
259 switches::kEnableLogging, | |
260 #if defined(OS_MACOSX) | |
261 switches::kEnableSandboxLogging, | |
262 #endif | |
263 switches::kGpuStartupDialog, | |
264 switches::kLoggingLevel, | |
265 switches::kNoGpuSandbox, | |
266 switches::kNoSandbox, | |
267 }; | |
268 cmd_line->CopySwitchesFrom(browser_command_line, kSwitchNames, | |
269 arraysize(kSwitchNames)); | |
270 | |
271 // If specified, prepend a launcher program to the command line. | |
272 if (!gpu_launcher.empty()) | |
273 cmd_line->PrependWrapper(gpu_launcher); | |
274 | |
275 Launch( | |
276 #if defined(OS_WIN) | |
277 FilePath(), | |
278 #elif defined(OS_POSIX) | |
279 false, // Never use the zygote (GPU plugin can't be sandboxed). | |
280 base::environment_vector(), | |
281 #endif | |
282 cmd_line); | |
283 | |
284 UMA_HISTOGRAM_ENUMERATION("GPU.GPUProcessLifetimeEvents", | |
285 LAUNCHED, GPU_PROCESS_LIFETIME_EVENT_MAX); | |
286 return true; | |
287 } | |
OLD | NEW |