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