| 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 |