1// Copyright (c) 2012 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/gpu/gpu_child_thread.h"
6
7#include "base/bind.h"
8#include "base/lazy_instance.h"
9#include "base/threading/worker_pool.h"
10#include "build/build_config.h"
11#include "content/child/child_process.h"
12#include "content/child/thread_safe_sender.h"
13#include "content/common/gpu/gpu_messages.h"
14#include "content/gpu/gpu_watchdog_thread.h"
15#include "content/public/common/content_client.h"
16#include "content/public/common/content_switches.h"
17#include "gpu/config/gpu_info_collector.h"
18#include "ipc/ipc_channel_handle.h"
19#include "ipc/ipc_sync_message_filter.h"
20#include "ui/gl/gl_implementation.h"
21
22namespace content {
23namespace {
24
25static base::LazyInstance<scoped_refptr<ThreadSafeSender> >
26    g_thread_safe_sender = LAZY_INSTANCE_INITIALIZER;
27
28bool GpuProcessLogMessageHandler(int severity,
29                                 const char* file, int line,
30                                 size_t message_start,
31                                 const std::string& str) {
32  std::string header = str.substr(0, message_start);
33  std::string message = str.substr(message_start);
34
35  g_thread_safe_sender.Get()->Send(new GpuHostMsg_OnLogMessage(
36      severity, header, message));
37
38  return false;
39}
40
41}  // namespace
42
43GpuChildThread::GpuChildThread(GpuWatchdogThread* watchdog_thread,
44                               bool dead_on_arrival,
45                               const gpu::GPUInfo& gpu_info,
46                               const DeferredMessages& deferred_messages)
47    : dead_on_arrival_(dead_on_arrival),
48      gpu_info_(gpu_info),
49      deferred_messages_(deferred_messages),
50      in_browser_process_(false) {
51  watchdog_thread_ = watchdog_thread;
52#if defined(OS_WIN)
53  target_services_ = NULL;
54#endif
55  g_thread_safe_sender.Get() = thread_safe_sender();
56}
57
58GpuChildThread::GpuChildThread(const std::string& channel_id)
59    : ChildThread(channel_id),
60      dead_on_arrival_(false),
61      in_browser_process_(true) {
62#if defined(OS_WIN)
63  target_services_ = NULL;
64#endif
65  DCHECK(
66      CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess) ||
67      CommandLine::ForCurrentProcess()->HasSwitch(switches::kInProcessGPU));
68  // For single process and in-process GPU mode, we need to load and
69  // initialize the GL implementation and locate the GL entry points here.
70  if (!gfx::GLSurface::InitializeOneOff()) {
71    VLOG(1) << "gfx::GLSurface::InitializeOneOff()";
72  }
73  g_thread_safe_sender.Get() = thread_safe_sender();
74}
75
76GpuChildThread::~GpuChildThread() {
77}
78
79void GpuChildThread::Shutdown() {
80  ChildThread::Shutdown();
81  logging::SetLogMessageHandler(NULL);
82}
83
84void GpuChildThread::Init(const base::Time& process_start_time) {
85  process_start_time_ = process_start_time;
86}
87
88bool GpuChildThread::Send(IPC::Message* msg) {
89  // The GPU process must never send a synchronous IPC message to the browser
90  // process. This could result in deadlock.
91  DCHECK(!msg->is_sync());
92
93  return ChildThread::Send(msg);
94}
95
96bool GpuChildThread::OnControlMessageReceived(const IPC::Message& msg) {
97  bool msg_is_ok = true;
98  bool handled = true;
99  IPC_BEGIN_MESSAGE_MAP_EX(GpuChildThread, msg, msg_is_ok)
100    IPC_MESSAGE_HANDLER(GpuMsg_Initialize, OnInitialize)
101    IPC_MESSAGE_HANDLER(GpuMsg_CollectGraphicsInfo, OnCollectGraphicsInfo)
102    IPC_MESSAGE_HANDLER(GpuMsg_GetVideoMemoryUsageStats,
103                        OnGetVideoMemoryUsageStats)
104    IPC_MESSAGE_HANDLER(GpuMsg_Clean, OnClean)
105    IPC_MESSAGE_HANDLER(GpuMsg_Crash, OnCrash)
106    IPC_MESSAGE_HANDLER(GpuMsg_Hang, OnHang)
107    IPC_MESSAGE_HANDLER(GpuMsg_DisableWatchdog, OnDisableWatchdog)
108    IPC_MESSAGE_UNHANDLED(handled = false)
109  IPC_END_MESSAGE_MAP_EX()
110
111  if (handled)
112    return true;
113
114  return gpu_channel_manager_.get() &&
115      gpu_channel_manager_->OnMessageReceived(msg);
116}
117
118void GpuChildThread::OnInitialize() {
119  Send(new GpuHostMsg_Initialized(!dead_on_arrival_, gpu_info_));
120  while (!deferred_messages_.empty()) {
121    Send(deferred_messages_.front());
122    deferred_messages_.pop();
123  }
124
125  if (dead_on_arrival_) {
126    VLOG(1) << "Exiting GPU process due to errors during initialization";
127    base::MessageLoop::current()->Quit();
128    return;
129  }
130
131#if defined(OS_ANDROID)
132  base::PlatformThread::SetThreadPriority(
133      base::PlatformThread::CurrentHandle(),
134      base::kThreadPriority_Display);
135#endif
136
137  // We don't need to pipe log messages if we are running the GPU thread in
138  // the browser process.
139  if (!in_browser_process_)
140    logging::SetLogMessageHandler(GpuProcessLogMessageHandler);
141
142  // Record initialization only after collecting the GPU info because that can
143  // take a significant amount of time.
144  gpu_info_.initialization_time = base::Time::Now() - process_start_time_;
145
146  // Defer creation of the render thread. This is to prevent it from handling
147  // IPC messages before the sandbox has been enabled and all other necessary
148  // initialization has succeeded.
149  gpu_channel_manager_.reset(
150      new GpuChannelManager(this,
151                            watchdog_thread_.get(),
152                            ChildProcess::current()->io_message_loop_proxy(),
153                            ChildProcess::current()->GetShutDownEvent()));
154
155  // Ensure the browser process receives the GPU info before a reply to any
156  // subsequent IPC it might send.
157  if (!in_browser_process_)
158    Send(new GpuHostMsg_GraphicsInfoCollected(gpu_info_));
159}
160
161void GpuChildThread::StopWatchdog() {
162  if (watchdog_thread_.get()) {
163    watchdog_thread_->Stop();
164  }
165}
166
167void GpuChildThread::OnCollectGraphicsInfo() {
168#if defined(OS_WIN)
169  // GPU full info collection should only happen on un-sandboxed GPU process
170  // or single process/in-process gpu mode on Windows.
171  CommandLine* command_line = CommandLine::ForCurrentProcess();
172  DCHECK(command_line->HasSwitch(switches::kDisableGpuSandbox) ||
173         in_browser_process_);
174#endif  // OS_WIN
175
176  if (!gpu::CollectContextGraphicsInfo(&gpu_info_))
177    VLOG(1) << "gpu::CollectGraphicsInfo failed";
178  GetContentClient()->SetGpuInfo(gpu_info_);
179
180#if defined(OS_WIN)
181  // This is slow, but it's the only thing the unsandboxed GPU process does,
182  // and GpuDataManager prevents us from sending multiple collecting requests,
183  // so it's OK to be blocking.
184  gpu::GetDxDiagnostics(&gpu_info_.dx_diagnostics);
185  gpu_info_.finalized = true;
186#endif  // OS_WIN
187
188  Send(new GpuHostMsg_GraphicsInfoCollected(gpu_info_));
189
190#if defined(OS_WIN)
191  if (!in_browser_process_) {
192    // The unsandboxed GPU process fulfilled its duty.  Rest in peace.
193    base::MessageLoop::current()->Quit();
194  }
195#endif  // OS_WIN
196}
197
198void GpuChildThread::OnGetVideoMemoryUsageStats() {
199  GPUVideoMemoryUsageStats video_memory_usage_stats;
200  if (gpu_channel_manager_)
201    gpu_channel_manager_->gpu_memory_manager()->GetVideoMemoryUsageStats(
202        &video_memory_usage_stats);
203  Send(new GpuHostMsg_VideoMemoryUsageStats(video_memory_usage_stats));
204}
205
206void GpuChildThread::OnClean() {
207  VLOG(1) << "GPU: Removing all contexts";
208  if (gpu_channel_manager_)
209    gpu_channel_manager_->LoseAllContexts();
210}
211
212void GpuChildThread::OnCrash() {
213  VLOG(1) << "GPU: Simulating GPU crash";
214  // Good bye, cruel world.
215  volatile int* it_s_the_end_of_the_world_as_we_know_it = NULL;
216  *it_s_the_end_of_the_world_as_we_know_it = 0xdead;
217}
218
219void GpuChildThread::OnHang() {
220  VLOG(1) << "GPU: Simulating GPU hang";
221  for (;;) {
222    // Do not sleep here. The GPU watchdog timer tracks the amount of user
223    // time this thread is using and it doesn't use much while calling Sleep.
224  }
225}
226
227void GpuChildThread::OnDisableWatchdog() {
228  VLOG(1) << "GPU: Disabling watchdog thread";
229  if (watchdog_thread_.get()) {
230    // Disarm the watchdog before shutting down the message loop. This prevents
231    // the future posting of tasks to the message loop.
232    if (watchdog_thread_->message_loop())
233      watchdog_thread_->PostAcknowledge();
234    // Prevent rearming.
235    watchdog_thread_->Stop();
236  }
237}
238
239}  // namespace content
240
241