Index: mojo/edk/system/raw_channel.cc |
diff --git a/mojo/edk/system/raw_channel.cc b/mojo/edk/system/raw_channel.cc |
index aff11103a9daba8c591742dd0dff41432c32759f..383a743175b55e53057766bf9f0fbef851ec685c 100644 |
--- a/mojo/edk/system/raw_channel.cc |
+++ b/mojo/edk/system/raw_channel.cc |
@@ -156,7 +156,7 @@ void RawChannel::WriteBuffer::GetBuffers(std::vector<Buffer>* buffers) const { |
RawChannel::RawChannel() |
: message_loop_for_io_(nullptr), |
delegate_(nullptr), |
- read_stopped_(false), |
+ set_on_shutdown_(nullptr), |
write_stopped_(false), |
weak_ptr_factory_(this) { |
} |
@@ -212,7 +212,10 @@ void RawChannel::Shutdown() { |
// Reset the delegate so that it won't receive further calls. |
delegate_ = nullptr; |
- read_stopped_ = true; |
+ if (set_on_shutdown_) { |
+ *set_on_shutdown_ = true; |
+ set_on_shutdown_ = nullptr; |
+ } |
write_stopped_ = true; |
weak_ptr_factory_.InvalidateWeakPtrs(); |
@@ -264,11 +267,6 @@ bool RawChannel::IsWriteBufferEmpty() { |
void RawChannel::OnReadCompleted(IOResult io_result, size_t bytes_read) { |
DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io_); |
- if (read_stopped_) { |
- NOTREACHED(); |
- return; |
- } |
- |
// Keep reading data in a loop, and dispatch messages if enough data is |
// received. Exit the loop if any of the following happens: |
// - one or more messages were dispatched; |
@@ -281,9 +279,8 @@ void RawChannel::OnReadCompleted(IOResult io_result, size_t bytes_read) { |
case IO_FAILED_SHUTDOWN: |
case IO_FAILED_BROKEN: |
case IO_FAILED_UNKNOWN: |
- read_stopped_ = true; |
CallOnError(ReadIOResultToError(io_result)); |
- return; |
+ return; // |this| may have been destroyed in |CallOnError()|. |
case IO_PENDING: |
NOTREACHED(); |
return; |
@@ -318,16 +315,14 @@ void RawChannel::OnReadCompleted(IOResult io_result, size_t bytes_read) { |
&error_message)) { |
DCHECK(error_message); |
LOG(ERROR) << "Received invalid message: " << error_message; |
- read_stopped_ = true; |
CallOnError(Delegate::ERROR_READ_BAD_MESSAGE); |
- return; |
+ return; // |this| may have been destroyed in |CallOnError()|. |
} |
if (message_view.type() == MessageInTransit::kTypeRawChannel) { |
if (!OnReadMessageForRawChannel(message_view)) { |
- read_stopped_ = true; |
CallOnError(Delegate::ERROR_READ_BAD_MESSAGE); |
- return; |
+ return; // |this| may have been destroyed in |CallOnError()|. |
} |
} else { |
embedder::ScopedPlatformHandleVectorPtr platform_handles; |
@@ -344,9 +339,8 @@ void RawChannel::OnReadCompleted(IOResult io_result, size_t bytes_read) { |
platform_handle_table).Pass(); |
if (!platform_handles) { |
LOG(ERROR) << "Invalid number of platform handles received"; |
- read_stopped_ = true; |
CallOnError(Delegate::ERROR_READ_BAD_MESSAGE); |
- return; |
+ return; // |this| may have been destroyed in |CallOnError()|. |
} |
} |
} |
@@ -355,13 +349,16 @@ void RawChannel::OnReadCompleted(IOResult io_result, size_t bytes_read) { |
// for the POSIX implementation, we should confirm that none are stored. |
// Dispatch the message. |
+ // Detect the case when |Shutdown()| is called; subsequent destruction |
+ // is also permitted then. |
+ bool shutdown_called = false; |
+ DCHECK(!set_on_shutdown_); |
+ set_on_shutdown_ = &shutdown_called; |
DCHECK(delegate_); |
delegate_->OnReadMessage(message_view, platform_handles.Pass()); |
- if (read_stopped_) { |
- // |Shutdown()| was called in |OnReadMessage()|. |
- // TODO(vtl): Add test for this case. |
+ if (shutdown_called) |
return; |
- } |
+ set_on_shutdown_ = nullptr; |
} |
did_dispatch_message = true; |
@@ -429,8 +426,10 @@ void RawChannel::OnWriteCompleted(IOResult io_result, |
bytes_written); |
} |
- if (did_fail) |
+ if (did_fail) { |
CallOnError(Delegate::ERROR_WRITE); |
+ return; // |this| may have been destroyed in |CallOnError()|. |
+ } |
} |
void RawChannel::EnqueueMessageNoLock(scoped_ptr<MessageInTransit> message) { |
@@ -467,8 +466,10 @@ RawChannel::Delegate::Error RawChannel::ReadIOResultToError( |
void RawChannel::CallOnError(Delegate::Error error) { |
DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io_); |
// TODO(vtl): Add a "write_lock_.AssertNotAcquired()"? |
- if (delegate_) |
+ if (delegate_) { |
delegate_->OnError(error); |
+ return; // |this| may have been destroyed in |OnError()|. |
+ } |
} |
bool RawChannel::OnWriteCompletedNoLock(IOResult io_result, |