| OLD | NEW |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/gpu_process_host.h" | 5 #include "chrome/browser/gpu_process_host.h" |
| 6 | 6 |
| 7 #include "base/command_line.h" | 7 #include "base/command_line.h" |
| 8 #include "base/singleton.h" | |
| 9 #include "base/thread.h" | 8 #include "base/thread.h" |
| 10 #include "chrome/browser/browser_process.h" | 9 #include "chrome/browser/browser_process.h" |
| 11 #include "chrome/browser/child_process_host.h" | 10 #include "chrome/browser/chrome_thread.h" |
| 12 #include "chrome/browser/child_process_launcher.h" | 11 #include "chrome/browser/gpu_process_host_ui_shim.h" |
| 13 #include "chrome/browser/io_thread.h" | |
| 14 #include "chrome/browser/renderer_host/render_process_host.h" | |
| 15 #include "chrome/common/child_process_info.h" | |
| 16 #include "chrome/common/chrome_switches.h" | 12 #include "chrome/common/chrome_switches.h" |
| 17 #include "chrome/common/gpu_messages.h" | 13 #include "chrome/common/gpu_messages.h" |
| 18 #include "chrome/common/render_messages.h" | 14 #include "chrome/common/render_messages.h" |
| 19 #include "ipc/ipc_switches.h" | 15 #include "ipc/ipc_switches.h" |
| 20 | 16 |
| 21 GpuProcessHost::GpuProcessHost() : last_routing_id_(1) { | 17 namespace { |
| 18 |
| 19 // Tasks used by this file |
| 20 class RouteOnUIThreadTask : public Task { |
| 21 public: |
| 22 explicit RouteOnUIThreadTask(const IPC::Message& msg) { |
| 23 msg_ = new IPC::Message(msg); |
| 24 } |
| 25 |
| 26 private: |
| 27 void Run() { |
| 28 GpuProcessHostUIShim::Get()->OnMessageReceived(*msg_); |
| 29 delete msg_; |
| 30 msg_ = NULL; |
| 31 } |
| 32 IPC::Message* msg_; |
| 33 }; |
| 34 |
| 35 // Global GpuProcessHost instance. |
| 36 // We can not use Singleton<GpuProcessHost> because that gets |
| 37 // terminated on the wrong thread (main thread). We need the |
| 38 // GpuProcessHost to be terminated on the same thread on which it is |
| 39 // initialized, the IO thread. |
| 40 static GpuProcessHost* sole_instance_; |
| 41 |
| 42 } // anonymous namespace |
| 43 |
| 44 GpuProcessHost::GpuProcessHost() |
| 45 : ChildProcessHost(GPU_PROCESS, NULL), |
| 46 initialized_(false), |
| 47 initialized_successfully_(false) { |
| 48 } |
| 49 |
| 50 GpuProcessHost::~GpuProcessHost() { |
| 51 while (!queued_synchronization_replies_.empty()) { |
| 52 delete queued_synchronization_replies_.front().reply; |
| 53 queued_synchronization_replies_.pop(); |
| 54 } |
| 55 } |
| 56 |
| 57 bool GpuProcessHost::EnsureInitialized() { |
| 58 if (!initialized_) { |
| 59 initialized_ = true; |
| 60 initialized_successfully_ = Init(); |
| 61 } |
| 62 return initialized_successfully_; |
| 63 } |
| 64 |
| 65 bool GpuProcessHost::Init() { |
| 66 if (!CreateChannel()) |
| 67 return false; |
| 68 |
| 22 const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess(); | 69 const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess(); |
| 23 std::wstring gpu_launcher = | 70 std::wstring gpu_launcher = |
| 24 browser_command_line.GetSwitchValue(switches::kGpuLauncher); | 71 browser_command_line.GetSwitchValue(switches::kGpuLauncher); |
| 25 | 72 |
| 26 FilePath exe_path = ChildProcessHost::GetChildPath(gpu_launcher.empty()); | 73 FilePath exe_path = ChildProcessHost::GetChildPath(gpu_launcher.empty()); |
| 27 if (exe_path.empty()) | 74 if (exe_path.empty()) |
| 28 return; | 75 return false; |
| 29 | |
| 30 std::string channel_id = ChildProcessInfo::GenerateRandomChannelID(this); | |
| 31 channel_.reset(new IPC::ChannelProxy( | |
| 32 channel_id, | |
| 33 IPC::Channel::MODE_SERVER, | |
| 34 this, | |
| 35 NULL, // No filter (for now). | |
| 36 g_browser_process->io_thread()->message_loop())); | |
| 37 | 76 |
| 38 CommandLine* cmd_line = new CommandLine(exe_path); | 77 CommandLine* cmd_line = new CommandLine(exe_path); |
| 39 cmd_line->AppendSwitchWithValue(switches::kProcessType, | 78 cmd_line->AppendSwitchWithValue(switches::kProcessType, |
| 40 switches::kGpuProcess); | 79 switches::kGpuProcess); |
| 41 cmd_line->AppendSwitchWithValue(switches::kProcessChannelID, | 80 cmd_line->AppendSwitchWithValue(switches::kProcessChannelID, |
| 42 ASCIIToWide(channel_id)); | 81 ASCIIToWide(channel_id())); |
| 43 | 82 |
| 44 const CommandLine& browser_cmd_line = *CommandLine::ForCurrentProcess(); | 83 const CommandLine& browser_cmd_line = *CommandLine::ForCurrentProcess(); |
| 45 PropagateBrowserCommandLineToGpu(browser_cmd_line, cmd_line); | 84 PropagateBrowserCommandLineToGpu(browser_cmd_line, cmd_line); |
| 46 | 85 |
| 47 // If specified, prepend a launcher program to the command line. | 86 // If specified, prepend a launcher program to the command line. |
| 48 if (!gpu_launcher.empty()) | 87 if (!gpu_launcher.empty()) |
| 49 cmd_line->PrependWrapper(gpu_launcher); | 88 cmd_line->PrependWrapper(gpu_launcher); |
| 50 | 89 |
| 51 // Spawn the child process asynchronously to avoid blocking the UI thread. | 90 Launch( |
| 52 child_process_.reset(new ChildProcessLauncher( | |
| 53 #if defined(OS_WIN) | 91 #if defined(OS_WIN) |
| 54 FilePath(), | 92 FilePath(), |
| 55 #elif defined(POSIX) | 93 #elif defined(POSIX) |
| 56 false, // Never use the zygote (GPU plugin can't be sandboxed). | 94 false, // Never use the zygote (GPU plugin can't be sandboxed). |
| 57 base::environment_vector(), | 95 base::environment_vector(), |
| 58 channel_->GetClientFileDescriptor(), | |
| 59 #endif | 96 #endif |
| 60 cmd_line, | 97 cmd_line); |
| 61 this)); | |
| 62 } | |
| 63 | 98 |
| 64 GpuProcessHost::~GpuProcessHost() { | 99 return true; |
| 65 while (!queued_synchronization_replies_.empty()) { | |
| 66 delete queued_synchronization_replies_.front(); | |
| 67 queued_synchronization_replies_.pop(); | |
| 68 } | |
| 69 } | 100 } |
| 70 | 101 |
| 71 // static | 102 // static |
| 72 GpuProcessHost* GpuProcessHost::Get() { | 103 GpuProcessHost* GpuProcessHost::Get() { |
| 73 GpuProcessHost* host = Singleton<GpuProcessHost>::get(); | 104 if (sole_instance_ == NULL) |
| 74 if (!host->child_process_.get()) | 105 sole_instance_ = new GpuProcessHost(); |
| 75 return NULL; // Failed to init. | 106 return sole_instance_; |
| 76 return host; | |
| 77 } | 107 } |
| 78 | 108 |
| 79 int32 GpuProcessHost::GetNextRoutingId() { | 109 // static |
| 80 return ++last_routing_id_; | 110 void GpuProcessHost::Shutdown() { |
| 81 } | 111 if (sole_instance_) { |
| 82 | 112 delete sole_instance_; |
| 83 int32 GpuProcessHost::NewRenderWidgetHostView(GpuNativeWindowHandle parent) { | 113 sole_instance_ = NULL; |
| 84 int32 routing_id = GetNextRoutingId(); | 114 } |
| 85 Send(new GpuMsg_NewRenderWidgetHostView(parent, routing_id)); | |
| 86 return routing_id; | |
| 87 } | 115 } |
| 88 | 116 |
| 89 bool GpuProcessHost::Send(IPC::Message* msg) { | 117 bool GpuProcessHost::Send(IPC::Message* msg) { |
| 90 if (!channel_.get()) { | 118 if (!EnsureInitialized()) |
| 91 delete msg; | |
| 92 return false; | 119 return false; |
| 93 } | |
| 94 | 120 |
| 95 if (child_process_.get() && child_process_->IsStarting()) { | 121 return ChildProcessHost::Send(msg); |
| 96 queued_messages_.push(msg); | |
| 97 return true; | |
| 98 } | |
| 99 | |
| 100 return channel_->Send(msg); | |
| 101 } | 122 } |
| 102 | 123 |
| 103 void GpuProcessHost::OnMessageReceived(const IPC::Message& message) { | 124 void GpuProcessHost::OnMessageReceived(const IPC::Message& message) { |
| 104 if (message.routing_id() == MSG_ROUTING_CONTROL) { | 125 if (message.routing_id() == MSG_ROUTING_CONTROL) { |
| 105 OnControlMessageReceived(message); | 126 OnControlMessageReceived(message); |
| 106 } else { | 127 } else { |
| 107 router_.OnMessageReceived(message); | 128 // Need to transfer this message to the UI thread and the |
| 129 // GpuProcessHostUIShim for dispatching via its message router. |
| 130 ChromeThread::PostTask(ChromeThread::UI, |
| 131 FROM_HERE, |
| 132 new RouteOnUIThreadTask(message)); |
| 108 } | 133 } |
| 109 } | 134 } |
| 110 | 135 |
| 111 void GpuProcessHost::OnChannelConnected(int32 peer_pid) { | 136 void GpuProcessHost::EstablishGpuChannel(int renderer_id, |
| 112 } | 137 ResourceMessageFilter* filter) { |
| 113 | 138 if (Send(new GpuMsg_EstablishChannel(renderer_id))) { |
| 114 void GpuProcessHost::OnChannelError() { | 139 sent_requests_.push(ChannelRequest(filter)); |
| 115 } | 140 } else { |
| 116 | 141 ReplyToRenderer(IPC::ChannelHandle(), filter); |
| 117 void GpuProcessHost::OnProcessLaunched() { | |
| 118 while (!queued_messages_.empty()) { | |
| 119 Send(queued_messages_.front()); | |
| 120 queued_messages_.pop(); | |
| 121 } | 142 } |
| 122 } | 143 } |
| 123 | 144 |
| 124 void GpuProcessHost::AddRoute(int32 routing_id, | 145 void GpuProcessHost::Synchronize(IPC::Message* reply, |
| 125 IPC::Channel::Listener* listener) { | 146 ResourceMessageFilter* filter) { |
| 126 router_.AddRoute(routing_id, listener); | 147 queued_synchronization_replies_.push(SynchronizationRequest(reply, filter)); |
| 127 } | 148 Send(new GpuMsg_Synchronize()); |
| 128 | |
| 129 void GpuProcessHost::RemoveRoute(int32 routing_id) { | |
| 130 router_.RemoveRoute(routing_id); | |
| 131 } | |
| 132 | |
| 133 void GpuProcessHost::EstablishGpuChannel(int renderer_id) { | |
| 134 if (Send(new GpuMsg_EstablishChannel(renderer_id))) | |
| 135 sent_requests_.push(ChannelRequest(renderer_id)); | |
| 136 else | |
| 137 ReplyToRenderer(renderer_id, IPC::ChannelHandle()); | |
| 138 } | |
| 139 | |
| 140 void GpuProcessHost::Synchronize(int renderer_id, IPC::Message* reply) { | |
| 141 // ************ | |
| 142 // TODO(kbr): the handling of this synchronous message (which is | |
| 143 // needed for proper initialization semantics of APIs like WebGL) is | |
| 144 // currently broken on Windows because the renderer is sending a | |
| 145 // synchronous message to the browser's UI thread. To fix this, the | |
| 146 // GpuProcessHost needs to move to the IO thread, and any backing | |
| 147 // store handling needs to remain on the UI thread in a new | |
| 148 // GpuProcessHostProxy, where work is sent from the IO thread to the | |
| 149 // UI thread via PostTask. | |
| 150 // ************ | |
| 151 queued_synchronization_replies_.push(reply); | |
| 152 CHECK(Send(new GpuMsg_Synchronize(renderer_id))); | |
| 153 } | 149 } |
| 154 | 150 |
| 155 void GpuProcessHost::OnControlMessageReceived(const IPC::Message& message) { | 151 void GpuProcessHost::OnControlMessageReceived(const IPC::Message& message) { |
| 156 IPC_BEGIN_MESSAGE_MAP(GpuProcessHost, message) | 152 IPC_BEGIN_MESSAGE_MAP(GpuProcessHost, message) |
| 157 IPC_MESSAGE_HANDLER(GpuHostMsg_ChannelEstablished, OnChannelEstablished) | 153 IPC_MESSAGE_HANDLER(GpuHostMsg_ChannelEstablished, OnChannelEstablished) |
| 158 IPC_MESSAGE_HANDLER(GpuHostMsg_SynchronizeReply, OnSynchronizeReply) | 154 IPC_MESSAGE_HANDLER(GpuHostMsg_SynchronizeReply, OnSynchronizeReply) |
| 159 IPC_MESSAGE_UNHANDLED_ERROR() | 155 IPC_MESSAGE_UNHANDLED_ERROR() |
| 160 IPC_END_MESSAGE_MAP() | 156 IPC_END_MESSAGE_MAP() |
| 161 } | 157 } |
| 162 | 158 |
| 163 void GpuProcessHost::OnChannelEstablished( | 159 void GpuProcessHost::OnChannelEstablished( |
| 164 const IPC::ChannelHandle& channel_handle) { | 160 const IPC::ChannelHandle& channel_handle) { |
| 165 const ChannelRequest& request = sent_requests_.front(); | 161 const ChannelRequest& request = sent_requests_.front(); |
| 166 | 162 ReplyToRenderer(channel_handle, request.filter); |
| 167 ReplyToRenderer(request.renderer_id, channel_handle); | |
| 168 sent_requests_.pop(); | 163 sent_requests_.pop(); |
| 169 } | 164 } |
| 170 | 165 |
| 171 void GpuProcessHost::OnSynchronizeReply(int renderer_id) { | 166 void GpuProcessHost::OnSynchronizeReply() { |
| 172 IPC::Message* reply = queued_synchronization_replies_.front(); | 167 const SynchronizationRequest& request = |
| 168 queued_synchronization_replies_.front(); |
| 169 request.filter->Send(request.reply); |
| 173 queued_synchronization_replies_.pop(); | 170 queued_synchronization_replies_.pop(); |
| 174 RenderProcessHost* process_host = RenderProcessHost::FromID(renderer_id); | |
| 175 if (!process_host) { | |
| 176 delete reply; | |
| 177 return; | |
| 178 } | |
| 179 CHECK(process_host->Send(reply)); | |
| 180 } | 171 } |
| 181 | 172 |
| 182 void GpuProcessHost::ReplyToRenderer( | 173 void GpuProcessHost::ReplyToRenderer( |
| 183 int renderer_id, | 174 const IPC::ChannelHandle& channel, |
| 184 const IPC::ChannelHandle& channel) { | 175 ResourceMessageFilter* filter) { |
| 185 // Check whether the renderer process is still around. | 176 ViewMsg_GpuChannelEstablished* message = |
| 186 RenderProcessHost* process_host = RenderProcessHost::FromID(renderer_id); | |
| 187 if (!process_host) | |
| 188 return; | |
| 189 | |
| 190 ViewMsg_GpuChannelEstablished* msg = | |
| 191 new ViewMsg_GpuChannelEstablished(channel); | 177 new ViewMsg_GpuChannelEstablished(channel); |
| 192 // If the renderer process is performing synchronous initialization, | 178 // If the renderer process is performing synchronous initialization, |
| 193 // it needs to handle this message before receiving the reply for | 179 // it needs to handle this message before receiving the reply for |
| 194 // the synchronous ViewHostMsg_SynchronizeGpu message. | 180 // the synchronous ViewHostMsg_SynchronizeGpu message. |
| 195 msg->set_unblock(true); | 181 message->set_unblock(true); |
| 196 CHECK(process_host->Send(msg)); | 182 filter->Send(message); |
| 197 } | 183 } |
| 198 | 184 |
| 199 void GpuProcessHost::PropagateBrowserCommandLineToGpu( | 185 void GpuProcessHost::PropagateBrowserCommandLineToGpu( |
| 200 const CommandLine& browser_cmd, | 186 const CommandLine& browser_cmd, |
| 201 CommandLine* gpu_cmd) const { | 187 CommandLine* gpu_cmd) const { |
| 202 // Propagate the following switches to the GPU process command line (along | 188 // Propagate the following switches to the GPU process command line (along |
| 203 // with any associated values) if present in the browser command line. | 189 // with any associated values) if present in the browser command line. |
| 204 static const char* const switch_names[] = { | 190 static const char* const switch_names[] = { |
| 205 switches::kDisableLogging, | 191 switches::kDisableLogging, |
| 206 switches::kEnableLogging, | 192 switches::kEnableLogging, |
| 207 switches::kGpuStartupDialog, | 193 switches::kGpuStartupDialog, |
| 208 switches::kLoggingLevel, | 194 switches::kLoggingLevel, |
| 209 }; | 195 }; |
| 210 | 196 |
| 211 for (size_t i = 0; i < arraysize(switch_names); ++i) { | 197 for (size_t i = 0; i < arraysize(switch_names); ++i) { |
| 212 if (browser_cmd.HasSwitch(switch_names[i])) { | 198 if (browser_cmd.HasSwitch(switch_names[i])) { |
| 213 gpu_cmd->AppendSwitchWithValue(switch_names[i], | 199 gpu_cmd->AppendSwitchWithValue(switch_names[i], |
| 214 browser_cmd.GetSwitchValueASCII(switch_names[i])); | 200 browser_cmd.GetSwitchValueASCII(switch_names[i])); |
| 215 } | 201 } |
| 216 } | 202 } |
| 217 } | 203 } |
| 204 |
| 205 URLRequestContext* GpuProcessHost::GetRequestContext( |
| 206 uint32 request_id, |
| 207 const ViewHostMsg_Resource_Request& request_data) { |
| 208 return NULL; |
| 209 } |
| 210 |
| 211 bool GpuProcessHost::CanShutdown() { |
| 212 return true; |
| 213 } |
| 214 |
| OLD | NEW |