Index: ipc/ipc_channel_proxy.cc |
diff --git a/ipc/ipc_channel_proxy.cc b/ipc/ipc_channel_proxy.cc |
index 50431d575604c43045d174fa0800613bba17d463..ca09146294d6a9fcf85e834a57c90f30707dc753 100644 |
--- a/ipc/ipc_channel_proxy.cc |
+++ b/ipc/ipc_channel_proxy.cc |
@@ -156,7 +156,7 @@ void ChannelProxy::Context::ClearIPCTaskRunner() { |
void ChannelProxy::Context::CreateChannel(const IPC::ChannelHandle& handle, |
const Channel::Mode& mode) { |
- DCHECK(channel_.get() == NULL); |
+ DCHECK(!channel_); |
channel_id_ = handle.name; |
channel_.reset(new Channel(handle, mode, this)); |
} |
@@ -196,17 +196,15 @@ bool ChannelProxy::Context::OnMessageReceivedNoFilter(const Message& message) { |
// Called on the IPC::Channel thread |
void ChannelProxy::Context::OnChannelConnected(int32 peer_pid) { |
+ // We cache off the peer_pid so it can be safely accessed from both threads. |
+ peer_pid_ = channel_->peer_pid(); |
+ |
// Add any pending filters. This avoids a race condition where someone |
// creates a ChannelProxy, calls AddFilter, and then right after starts the |
// peer process. The IO thread could receive a message before the task to add |
// the filter is run on the IO thread. |
OnAddFilter(); |
- // We cache off the peer_pid so it can be safely accessed from both threads. |
- peer_pid_ = channel_->peer_pid(); |
- for (size_t i = 0; i < filters_.size(); ++i) |
- filters_[i]->OnChannelConnected(peer_pid); |
- |
// See above comment about using listener_task_runner_ here. |
listener_task_runner_->PostTask( |
FROM_HERE, base::Bind(&Context::OnDispatchConnected, this)); |
@@ -243,7 +241,7 @@ void ChannelProxy::Context::OnChannelOpened() { |
void ChannelProxy::Context::OnChannelClosed() { |
// It's okay for IPC::ChannelProxy::Close to be called more than once, which |
// would result in this branch being taken. |
- if (!channel_.get()) |
+ if (!channel_) |
return; |
for (size_t i = 0; i < filters_.size(); ++i) { |
@@ -254,6 +252,9 @@ void ChannelProxy::Context::OnChannelClosed() { |
// We don't need the filters anymore. |
message_filter_router_->Clear(); |
filters_.clear(); |
+ // We don't need the lock, because at this point, the listener thread can't |
+ // access it any more. |
+ pending_filters_.clear(); |
channel_.reset(); |
@@ -268,16 +269,24 @@ void ChannelProxy::Context::Clear() { |
// Called on the IPC::Channel thread |
void ChannelProxy::Context::OnSendMessage(scoped_ptr<Message> message) { |
- if (!channel_.get()) { |
+ if (!channel_) { |
OnChannelClosed(); |
return; |
} |
+ |
if (!channel_->Send(message.release())) |
OnChannelError(); |
} |
// Called on the IPC::Channel thread |
void ChannelProxy::Context::OnAddFilter() { |
+ // Our OnChannelConnected method has not yet been called, so we can't be |
+ // sure that channel_ is valid yet. When OnChannelConnected *is* called, |
+ // it invokes OnAddFilter, so any pending filter(s) will be added at that |
+ // time. |
+ if (peer_pid_ == base::kNullProcessId) |
+ return; |
+ |
std::vector<scoped_refptr<MessageFilter> > new_filters; |
{ |
base::AutoLock auto_lock(pending_filters_lock_); |
@@ -289,19 +298,28 @@ void ChannelProxy::Context::OnAddFilter() { |
message_filter_router_->AddFilter(new_filters[i].get()); |
- // If the channel has already been created, then we need to send this |
- // message so that the filter gets access to the Channel. |
- if (channel_.get()) |
- new_filters[i]->OnFilterAdded(channel_.get()); |
- // Ditto for if the channel has been connected. |
- if (peer_pid_) |
- new_filters[i]->OnChannelConnected(peer_pid_); |
+ // The channel has already been created and connected, so we need to |
+ // inform the filters right now. |
+ new_filters[i]->OnFilterAdded(channel_.get()); |
+ new_filters[i]->OnChannelConnected(peer_pid_); |
} |
} |
// Called on the IPC::Channel thread |
void ChannelProxy::Context::OnRemoveFilter(MessageFilter* filter) { |
- if (!channel_.get()) |
+ if (peer_pid_ == base::kNullProcessId) { |
+ // The channel is not yet connected, so any filters are still pending. |
+ base::AutoLock auto_lock(pending_filters_lock_); |
+ for (size_t i = 0; i < pending_filters_.size(); ++i) { |
+ if (pending_filters_[i].get() == filter) { |
+ filter->OnFilterRemoved(); |
+ pending_filters_.erase(pending_filters_.begin() + i); |
+ return; |
+ } |
+ } |
+ return; |
+ } |
+ if (!channel_) |
return; // The filters have already been deleted. |
message_filter_router_->RemoveFilter(filter); |