diff options
Diffstat (limited to 'content/browser/gpu_process_host.cc')
-rw-r--r-- | content/browser/gpu_process_host.cc | 287 |
1 files changed, 287 insertions, 0 deletions
diff --git a/content/browser/gpu_process_host.cc b/content/browser/gpu_process_host.cc new file mode 100644 index 0000000..4ef366a --- /dev/null +++ b/content/browser/gpu_process_host.cc @@ -0,0 +1,287 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/gpu_process_host.h" + +#include "app/app_switches.h" +#include "base/metrics/histogram.h" +#include "base/ref_counted.h" +#include "base/string_piece.h" +#include "base/threading/thread.h" +#include "chrome/browser/gpu_process_host_ui_shim.h" +#include "chrome/browser/tab_contents/render_view_host_delegate_helper.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/gpu_feature_flags.h" +#include "chrome/common/gpu_info.h" +#include "chrome/common/gpu_messages.h" +#include "chrome/common/render_messages.h" +#include "chrome/gpu/gpu_thread.h" +#include "content/browser/browser_thread.h" +#include "content/browser/renderer_host/render_widget_host.h" +#include "content/browser/renderer_host/render_widget_host_view.h" +#include "ipc/ipc_channel_handle.h" +#include "ipc/ipc_switches.h" +#include "media/base/media_switches.h" + +#if defined(OS_LINUX) +#include "ui/gfx/gtk_native_view_id_manager.h" +#endif // defined(OS_LINUX) + +namespace { + +enum GPUProcessLifetimeEvent { + LAUNCHED, + DIED_FIRST_TIME, + DIED_SECOND_TIME, + DIED_THIRD_TIME, + DIED_FOURTH_TIME, + GPU_PROCESS_LIFETIME_EVENT_MAX + }; + +class RouteOnUIThreadTask : public Task { + public: + RouteOnUIThreadTask(int host_id, const IPC::Message& msg) + : host_id_(host_id), + msg_(msg) { + } + + private: + virtual void Run() { + GpuProcessHostUIShim* ui_shim = GpuProcessHostUIShim::FromID(host_id_); + if (ui_shim) + ui_shim->OnMessageReceived(msg_); + } + + int host_id_; + IPC::Message msg_; +}; + +// A global map from GPU process host ID to GpuProcessHost. +static IDMap<GpuProcessHost> g_hosts_by_id; + +// Number of times the gpu process has crashed in the current browser session. +static int g_gpu_crash_count = 0; + +// Maximum number of times the gpu process is allowed to crash in a session. +// Once this limit is reached, any request to launch the gpu process will fail. +static const int kGpuMaxCrashCount = 3; + +} // anonymous namespace + +class GpuMainThread : public base::Thread { + public: + explicit GpuMainThread(const std::string& channel_id) + : base::Thread("CrGpuMain"), + channel_id_(channel_id) { + } + + ~GpuMainThread() { + Stop(); + } + + protected: + virtual void Init() { + // Must be created on GPU thread. + gpu_thread_.reset(new GpuThread(channel_id_)); + gpu_thread_->Init(base::Time::Now()); + } + + virtual void CleanUp() { + // Must be destroyed on GPU thread. + gpu_thread_.reset(); + } + + private: + scoped_ptr<GpuThread> gpu_thread_; + std::string channel_id_; + DISALLOW_COPY_AND_ASSIGN(GpuMainThread); +}; + +// static +GpuProcessHost* GpuProcessHost::Create(int host_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + GpuProcessHost* host = new GpuProcessHost(host_id); + if (!host->Init()) { + delete host; + return NULL; + } + + return host; +} + +// static +GpuProcessHost* GpuProcessHost::FromID(int host_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + if (host_id == 0) + return NULL; + + return g_hosts_by_id.Lookup(host_id); +} + +GpuProcessHost::GpuProcessHost(int host_id) + : BrowserChildProcessHost(GPU_PROCESS, NULL), + host_id_(host_id) { + g_hosts_by_id.AddWithID(this, host_id_); +} + +GpuProcessHost::~GpuProcessHost() { + + DCHECK(CalledOnValidThread()); + + g_hosts_by_id.Remove(host_id_); + + BrowserThread::PostTask(BrowserThread::UI, + FROM_HERE, + NewRunnableFunction(GpuProcessHostUIShim::Destroy, + host_id_)); +} + +bool GpuProcessHost::Init() { + if (!CreateChannel()) + return false; + + if (!CanLaunchGpuProcess()) + return false; + + if (!LaunchGpuProcess()) + return false; + + return Send(new GpuMsg_Initialize()); +} + +void GpuProcessHost::RouteOnUIThread(const IPC::Message& message) { + BrowserThread::PostTask(BrowserThread::UI, + FROM_HERE, + new RouteOnUIThreadTask(host_id_, message)); +} + +bool GpuProcessHost::Send(IPC::Message* msg) { + DCHECK(CalledOnValidThread()); + return BrowserChildProcessHost::Send(msg); +} + +bool GpuProcessHost::OnMessageReceived(const IPC::Message& message) { + DCHECK(CalledOnValidThread()); + RouteOnUIThread(message); + return true; +} + +bool GpuProcessHost::CanShutdown() { + return true; +} + +namespace { + +void SendOutstandingRepliesDispatcher(int host_id) { + GpuProcessHostUIShim *ui_shim = GpuProcessHostUIShim::FromID(host_id); + DCHECK(ui_shim); + ui_shim->SendOutstandingReplies(); +} + +void SendOutstandingReplies(int host_id) { + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + NewRunnableFunction(&SendOutstandingRepliesDispatcher, host_id)); +} + +} // namespace + +void GpuProcessHost::OnChildDied() { + SendOutstandingReplies(host_id_); + // Located in OnChildDied because OnProcessCrashed suffers from a race + // condition on Linux. + UMA_HISTOGRAM_ENUMERATION("GPU.GPUProcessLifetimeEvents", + DIED_FIRST_TIME + g_gpu_crash_count, + GPU_PROCESS_LIFETIME_EVENT_MAX); + BrowserChildProcessHost::OnChildDied(); +} + +void GpuProcessHost::OnProcessCrashed(int exit_code) { + SendOutstandingReplies(host_id_); + if (++g_gpu_crash_count >= kGpuMaxCrashCount) { + // The gpu process is too unstable to use. Disable it for current session. + RenderViewHostDelegateHelper::set_gpu_enabled(false); + } + BrowserChildProcessHost::OnProcessCrashed(exit_code); +} + +bool GpuProcessHost::CanLaunchGpuProcess() const { + return RenderViewHostDelegateHelper::gpu_enabled(); +} + +bool GpuProcessHost::LaunchGpuProcess() { + if (g_gpu_crash_count >= kGpuMaxCrashCount) + return false; + + const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess(); + + // If the single-process switch is present, just launch the GPU service in a + // new thread in the browser process. + if (browser_command_line.HasSwitch(switches::kSingleProcess)) { + GpuMainThread* thread = new GpuMainThread(channel_id()); + + base::Thread::Options options; +#if defined(OS_LINUX) + options.message_loop_type = MessageLoop::TYPE_IO; +#else + options.message_loop_type = MessageLoop::TYPE_UI; +#endif + + if (!thread->StartWithOptions(options)) + return false; + + return true; + } + + CommandLine::StringType gpu_launcher = + browser_command_line.GetSwitchValueNative(switches::kGpuLauncher); + + FilePath exe_path = ChildProcessHost::GetChildPath(gpu_launcher.empty()); + if (exe_path.empty()) + return false; + + CommandLine* cmd_line = new CommandLine(exe_path); + cmd_line->AppendSwitchASCII(switches::kProcessType, switches::kGpuProcess); + cmd_line->AppendSwitchASCII(switches::kProcessChannelID, channel_id()); + + SetCrashReporterCommandLine(cmd_line); + + // Propagate relevant command line switches. + static const char* const kSwitchNames[] = { + switches::kUseGL, + switches::kDisableGpuVsync, + switches::kDisableGpuWatchdog, + switches::kDisableLogging, + switches::kEnableAcceleratedDecoding, + switches::kEnableLogging, +#if defined(OS_MACOSX) + switches::kEnableSandboxLogging, +#endif + switches::kGpuStartupDialog, + switches::kLoggingLevel, + switches::kNoGpuSandbox, + switches::kNoSandbox, + }; + cmd_line->CopySwitchesFrom(browser_command_line, kSwitchNames, + arraysize(kSwitchNames)); + + // If specified, prepend a launcher program to the command line. + if (!gpu_launcher.empty()) + cmd_line->PrependWrapper(gpu_launcher); + + Launch( +#if defined(OS_WIN) + FilePath(), +#elif defined(OS_POSIX) + false, // Never use the zygote (GPU plugin can't be sandboxed). + base::environment_vector(), +#endif + cmd_line); + + UMA_HISTOGRAM_ENUMERATION("GPU.GPUProcessLifetimeEvents", + LAUNCHED, GPU_PROCESS_LIFETIME_EVENT_MAX); + return true; +} |