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 |