Index: content/renderer/gpu/gpu_channel_host.cc |
diff --git a/content/renderer/gpu/gpu_channel_host.cc b/content/renderer/gpu/gpu_channel_host.cc |
index 9229183ab11c97249421be6fe25e88767fca7f12..2850d951d13bb909037b509153e57a4d3518d990 100644 |
--- a/content/renderer/gpu/gpu_channel_host.cc |
+++ b/content/renderer/gpu/gpu_channel_host.cc |
@@ -4,13 +4,103 @@ |
#include "content/renderer/gpu/gpu_channel_host.h" |
+#include "base/message_loop_proxy.h" |
#include "content/common/child_process.h" |
#include "content/common/gpu/gpu_messages.h" |
#include "content/renderer/gpu/command_buffer_proxy.h" |
#include "content/renderer/gpu/gpu_surface_proxy.h" |
#include "content/renderer/gpu/transport_texture_service.h" |
+#include "content/renderer/render_process.h" |
#include "content/renderer/render_thread.h" |
#include "googleurl/src/gurl.h" |
+#include "ipc/ipc_sync_message_filter.h" |
+ |
+using base::AutoLock; |
+using base::MessageLoopProxy; |
+ |
+GpuChannelListener::GpuChannelListener() |
+ : orphaned_(false), |
+ loop_(MessageLoopProxy::current()) { |
+ |
+} |
+ |
+GpuChannelListener::~GpuChannelListener() { |
+ |
+} |
+ |
+GpuChannelHost::MessageFilter::MessageFilter(GpuChannelHost* parent) |
+ : parent_(parent) { |
+ DetachFromThread(); |
+} |
+ |
+GpuChannelHost::MessageFilter::~MessageFilter() { |
+ |
+} |
+ |
+void GpuChannelHost::MessageFilter::AddRoute( |
+ int route_id, |
+ scoped_refptr<GpuChannelListener> listener) { |
+ DCHECK(CalledOnValidThread()); |
+ DCHECK(listeners_.find(route_id) == listeners_.end()); |
+ listeners_[route_id] = listener; |
+} |
+ |
+void GpuChannelHost::MessageFilter::RemoveRoute(int route_id) { |
+ DCHECK(CalledOnValidThread()); |
+ ListenerMap::iterator it = listeners_.find(route_id); |
+ if (it != listeners_.end()) |
+ listeners_.erase(it); |
+} |
+ |
+bool GpuChannelHost::MessageFilter::OnMessageReceived( |
+ const IPC::Message& message) { |
+ DCHECK(CalledOnValidThread()); |
+ |
+ // Never handle sync message replies or we will deadlock here. |
+ if (message.is_reply()) |
+ return false; |
+ |
+ DCHECK(message.routing_id() != MSG_ROUTING_CONTROL); |
+ |
+ ListenerMap::iterator it = listeners_.find(message.routing_id()); |
+ |
+ if (it != listeners_.end()) { |
+ const scoped_refptr<GpuChannelListener>& listener = it->second; |
+ listener->loop()->PostTask( |
+ FROM_HERE, |
+ NewRunnableMethod( |
+ listener.get(), |
+ &GpuChannelListener::OnMessageReceived, |
nduca
2011/08/15 22:28:36
i.e. post to DispatchMessage and check this->orpha
|
+ message)); |
+ } |
+ |
+ return true; |
+} |
+ |
+void GpuChannelHost::MessageFilter::OnChannelError() { |
+ DCHECK(CalledOnValidThread()); |
+ |
+ // Inform all the proxies that an error has occurred. This will be reported |
+ // via OpenGL as a lost context. |
+ for (ListenerMap::iterator it = listeners_.begin(); |
+ it != listeners_.end(); |
+ it++) { |
+ const scoped_refptr<GpuChannelListener>& listener = it->second; |
+ listener->loop()->PostTask( |
+ FROM_HERE, |
+ NewRunnableMethod( |
+ listener.get(), |
+ &GpuChannelListener::OnChannelError)); |
+ } |
+ |
+ listeners_.clear(); |
+ |
+ ChildThread* main_thread = RenderProcess::current()->main_thread(); |
+ MessageLoop* main_loop = main_thread->message_loop(); |
+ main_loop->PostTask(FROM_HERE, |
+ NewRunnableMethod(parent_, |
+ &GpuChannelHost::OnChannelError)); |
+} |
GpuChannelHost::GpuChannelHost() |
: state_(kUnconnected), |
@@ -23,12 +113,27 @@ GpuChannelHost::~GpuChannelHost() { |
void GpuChannelHost::Connect( |
const IPC::ChannelHandle& channel_handle, |
base::ProcessHandle renderer_process_for_gpu) { |
- // Open a channel to the GPU process. |
+ DCHECK(RenderThread::current()); |
+ // Open a channel to the GPU process. We pass NULL as the main listener here |
+ // since we need to filter everything to route it to the right thread. |
channel_.reset(new IPC::SyncChannel( |
- channel_handle, IPC::Channel::MODE_CLIENT, this, |
+ channel_handle, IPC::Channel::MODE_CLIENT, NULL, |
ChildProcess::current()->io_message_loop_proxy(), true, |
ChildProcess::current()->GetShutDownEvent())); |
+ sync_filter_.reset(new IPC::SyncMessageFilter( |
+ ChildProcess::current()->GetShutDownEvent())); |
+ |
+ channel_->AddFilter(sync_filter_.get()); |
+ |
+ channel_->AddFilter(transport_texture_service_.get()); |
+ |
+ channel_filter_ = new MessageFilter(this); |
+ |
+ // Install the filter last, because we intercept all leftover |
+ // messages. |
+ channel_->AddFilter(channel_filter_.get()); |
+ |
// It is safe to send IPC messages before the channel completes the connection |
// and receives the hello message from the GPU process. The messages get |
// cached. |
@@ -51,43 +156,25 @@ void GpuChannelHost::SetStateLost() { |
state_ = kLost; |
} |
-bool GpuChannelHost::OnMessageReceived(const IPC::Message& message) { |
- DCHECK(message.routing_id() != MSG_ROUTING_CONTROL); |
- |
- return router_.RouteMessage(message); |
-} |
- |
-void GpuChannelHost::OnChannelConnected(int32 peer_pid) { |
- channel_->AddFilter(transport_texture_service_.get()); |
-} |
- |
void GpuChannelHost::OnChannelError() { |
state_ = kLost; |
// Channel is invalid and will be reinitialized if this host is requested |
// again. |
channel_.reset(); |
- |
- // Inform all the proxies that an error has occured. This will be reported via |
- // OpenGL as a lost context. |
- for (ProxyMap::iterator iter = proxies_.begin(); |
- iter != proxies_.end(); iter++) { |
- router_.RemoveRoute(iter->first); |
- iter->second->OnChannelError(); |
- } |
- |
- // The proxies are reference counted so this will not result in their |
- // destruction if the client still holds a reference. The proxy will report |
- // a lost context, indicating to the client that it needs to be recreated. |
- proxies_.clear(); |
} |
bool GpuChannelHost::Send(IPC::Message* message) { |
// The GPU process never sends synchronous IPCs so clear the unblock flag to |
// preserve order. |
message->set_unblock(false); |
- if (channel_.get()) |
+ |
+ // Unfortunately a sync filter cannot be used on the main (listener) thread. |
+ // TODO: Is that true even when we don't install a listener? |
+ if (channel_.get() && RenderThread::current()) |
return channel_->Send(message); |
+ else |
+ return sync_filter_->Send(message); |
// Callee takes ownership of message, regardless of whether Send is |
// successful. See IPC::Message::Sender. |
@@ -95,13 +182,14 @@ bool GpuChannelHost::Send(IPC::Message* message) { |
return false; |
} |
-CommandBufferProxy* GpuChannelHost::CreateViewCommandBuffer( |
+scoped_refptr<CommandBufferProxy> GpuChannelHost::CreateViewCommandBuffer( |
int render_view_id, |
CommandBufferProxy* share_group, |
const std::string& allowed_extensions, |
const std::vector<int32>& attribs, |
const GURL& active_url) { |
#if defined(ENABLE_GPU) |
+ AutoLock lock(context_lock_); |
// An error occurred. Need to get the host again to reinitialize it. |
if (!channel_.get()) |
return NULL; |
@@ -124,8 +212,9 @@ CommandBufferProxy* GpuChannelHost::CreateViewCommandBuffer( |
if (route_id == MSG_ROUTING_NONE) |
return NULL; |
- CommandBufferProxy* command_buffer = new CommandBufferProxy(this, route_id); |
- router_.AddRoute(route_id, command_buffer); |
+ scoped_refptr<CommandBufferProxy> command_buffer = |
+ new CommandBufferProxy(this, route_id); |
+ AddRoute(route_id, command_buffer); |
proxies_[route_id] = command_buffer; |
return command_buffer; |
#else |
@@ -137,19 +226,21 @@ GpuVideoDecodeAcceleratorHost* GpuChannelHost::CreateVideoDecoder( |
int command_buffer_route_id, |
const std::vector<int32>& configs, |
media::VideoDecodeAccelerator::Client* client) { |
+ AutoLock lock(context_lock_); |
ProxyMap::iterator it = proxies_.find(command_buffer_route_id); |
DCHECK(it != proxies_.end()); |
CommandBufferProxy* proxy = it->second; |
return proxy->CreateVideoDecoder(configs, client); |
} |
-CommandBufferProxy* GpuChannelHost::CreateOffscreenCommandBuffer( |
+scoped_refptr<CommandBufferProxy> GpuChannelHost::CreateOffscreenCommandBuffer( |
const gfx::Size& size, |
CommandBufferProxy* share_group, |
const std::string& allowed_extensions, |
const std::vector<int32>& attribs, |
const GURL& active_url) { |
#if defined(ENABLE_GPU) |
+ AutoLock lock(context_lock_); |
// An error occurred. Need to get the host again to reinitialize it. |
if (!channel_.get()) |
return NULL; |
@@ -170,8 +261,9 @@ CommandBufferProxy* GpuChannelHost::CreateOffscreenCommandBuffer( |
if (route_id == MSG_ROUTING_NONE) |
return NULL; |
- CommandBufferProxy* command_buffer = new CommandBufferProxy(this, route_id); |
- router_.AddRoute(route_id, command_buffer); |
+ scoped_refptr<CommandBufferProxy> command_buffer = |
+ new CommandBufferProxy(this, route_id); |
+ AddRoute(route_id, command_buffer); |
proxies_[route_id] = command_buffer; |
return command_buffer; |
#else |
@@ -181,38 +273,60 @@ CommandBufferProxy* GpuChannelHost::CreateOffscreenCommandBuffer( |
void GpuChannelHost::DestroyCommandBuffer(CommandBufferProxy* command_buffer) { |
#if defined(ENABLE_GPU) |
+ AutoLock lock(context_lock_); |
Send(new GpuChannelMsg_DestroyCommandBuffer(command_buffer->route_id())); |
// Check the proxy has not already been removed after a channel error. |
int route_id = command_buffer->route_id(); |
- if (proxies_.find(command_buffer->route_id()) != proxies_.end()) { |
+ if (proxies_.find(command_buffer->route_id()) != proxies_.end()) |
proxies_.erase(route_id); |
- router_.RemoveRoute(route_id); |
- } |
- |
- delete command_buffer; |
+ RemoveRoute(route_id); |
+ command_buffer->Orphan(); |
#endif |
} |
-GpuSurfaceProxy* GpuChannelHost::CreateOffscreenSurface(const gfx::Size& size) { |
+scoped_refptr<GpuSurfaceProxy> GpuChannelHost::CreateOffscreenSurface( |
+ const gfx::Size& size) { |
#if defined(ENABLE_GPU) |
+ AutoLock lock(context_lock_); |
int route_id; |
if (!Send(new GpuChannelMsg_CreateOffscreenSurface(size, &route_id))) |
return NULL; |
- scoped_ptr<GpuSurfaceProxy> surface(new GpuSurfaceProxy(this, route_id)); |
- router_.AddRoute(route_id, surface.get()); |
+ scoped_refptr<GpuSurfaceProxy> surface(new GpuSurfaceProxy(this, route_id)); |
+ AddRoute(route_id, surface.get()); |
- return surface.release(); |
+ return surface; |
#endif |
} |
void GpuChannelHost::DestroySurface(GpuSurfaceProxy* surface) { |
#if defined(ENABLE_GPU) |
+ AutoLock lock(context_lock_); |
Send(new GpuChannelMsg_DestroySurface(surface->route_id())); |
- if (router_.ResolveRoute(surface->route_id())) |
- router_.RemoveRoute(surface->route_id()); |
- delete surface; |
+ RemoveRoute(surface->route_id()); |
+ surface->Orphan(); |
#endif |
} |
+ |
+void GpuChannelHost::AddRoute( |
+ int route_id, scoped_refptr<GpuChannelListener> listener) { |
+ DCHECK(listener->loop() && listener->loop() == MessageLoopProxy::current()); |
+ |
+ MessageLoopProxy* io_loop = RenderProcess::current()->io_message_loop_proxy(); |
+ io_loop->PostTask(FROM_HERE, |
+ NewRunnableMethod( |
+ channel_filter_.get(), |
+ &GpuChannelHost::MessageFilter::AddRoute, |
+ route_id, listener)); |
+} |
+ |
+void GpuChannelHost::RemoveRoute(int route_id) { |
+ MessageLoopProxy* io_loop = RenderProcess::current()->io_message_loop_proxy(); |
+ io_loop->PostTask(FROM_HERE, |
+ NewRunnableMethod( |
+ channel_filter_.get(), |
+ &GpuChannelHost::MessageFilter::RemoveRoute, |
+ route_id)); |
+} |