Make it possible to write a message pipe endpoint's peer into it.

This is a nonsensical thing to do, but there's no real reason to
disallow it. (Similar nonsense can be achieve anyway.)

This requires a bit of unfortunate plumbing, but maybe it's less
unfortunate than the existing plumbing. (I also need to replace
"DispatcherTransport" with "HandleTransport", which will carry handle
rights.)

R=azani@chromium.org
BUG=fixes #767

Review URL: https://codereview.chromium.org/1943123002 .
diff --git a/mojo/edk/system/core_test_base.cc b/mojo/edk/system/core_test_base.cc
index d73706d..7df5fb7 100644
--- a/mojo/edk/system/core_test_base.cc
+++ b/mojo/edk/system/core_test_base.cc
@@ -191,7 +191,10 @@
     mutex().AssertHeld();
   }
 
-  RefPtr<Dispatcher> CreateEquivalentDispatcherAndCloseImplNoLock() override {
+  RefPtr<Dispatcher> CreateEquivalentDispatcherAndCloseImplNoLock(
+      MessagePipe* /*message_pipe*/,
+      unsigned /*port*/) override {
+    CancelAllAwakablesNoLock();
     return Create(info_);
   }
 
diff --git a/mojo/edk/system/core_unittest.cc b/mojo/edk/system/core_unittest.cc
index 9395286..1b3c1fa 100644
--- a/mojo/edk/system/core_unittest.cc
+++ b/mojo/edk/system/core_unittest.cc
@@ -870,16 +870,11 @@
   EXPECT_STREQ(kHello, buffer);
   EXPECT_EQ(0u, num_handles);
 
-  // Make sure that you can't pass either of the message pipe's handles over
-  // itself.
+  // Make sure that you can't pass a message pipe handle over itself.
   EXPECT_EQ(MOJO_RESULT_BUSY,
             core()->WriteMessage(h_passing[0], UserPointer<const void>(kHello),
                                  kHelloSize, MakeUserPointer(&h_passing[0]), 1,
                                  MOJO_WRITE_MESSAGE_FLAG_NONE));
-  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
-            core()->WriteMessage(h_passing[0], UserPointer<const void>(kHello),
-                                 kHelloSize, MakeUserPointer(&h_passing[1]), 1,
-                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
 
   MojoHandle h_passed[2];
   EXPECT_EQ(MOJO_RESULT_OK,
@@ -1603,6 +1598,57 @@
   EXPECT_EQ(MOJO_RESULT_OK, core()->Close(ch));
 }
 
+// Tests "faux leak" message pipe handle passing situations.
+TEST_F(CoreTest, MessagePipeBasicLocalHandlePassing3) {
+  {
+    MojoHandle h0 = MOJO_HANDLE_INVALID;
+    MojoHandle h1 = MOJO_HANDLE_INVALID;
+    EXPECT_EQ(MOJO_RESULT_OK,
+              core()->CreateMessagePipe(NullUserPointer(), MakeUserPointer(&h0),
+                                        MakeUserPointer(&h1)));
+
+    // You can send a message pipe's peer handle over itself (and nothing bad
+    // happens).
+    EXPECT_EQ(
+        MOJO_RESULT_OK,
+        core()->WriteMessage(h0, NullUserPointer(), 0, MakeUserPointer(&h1), 1,
+                             MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+    // Of course, there's nothing to do afterwards except close the handle you
+    // have left.
+    EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h0));
+  }
+
+  {
+    MojoHandle h0 = MOJO_HANDLE_INVALID;
+    MojoHandle h1 = MOJO_HANDLE_INVALID;
+    EXPECT_EQ(MOJO_RESULT_OK,
+              core()->CreateMessagePipe(NullUserPointer(), MakeUserPointer(&h0),
+                                        MakeUserPointer(&h1)));
+
+    MojoHandle h_passed[2] = {MOJO_HANDLE_INVALID, MOJO_HANDLE_INVALID};
+    EXPECT_EQ(MOJO_RESULT_OK,
+              core()->CreateMessagePipe(NullUserPointer(),
+                                        MakeUserPointer(&h_passed[0]),
+                                        MakeUserPointer(&h_passed[1])));
+
+    // You can also write |h1| into some other message pipe.
+    EXPECT_EQ(MOJO_RESULT_OK,
+              core()->WriteMessage(h_passed[0], NullUserPointer(), 0,
+                                   MakeUserPointer(&h1), 1,
+                                   MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+    // And then write both ends of that message pipe to |h0|.
+    EXPECT_EQ(MOJO_RESULT_OK,
+              core()->WriteMessage(h0, NullUserPointer(), 0,
+                                   MakeUserPointer(h_passed), 2,
+                                   MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+    // Again, nothing bad happens, but again you can only close |h0|.
+    EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h0));
+  }
+}
+
 struct TestAsyncWaiter {
   TestAsyncWaiter() : result(MOJO_RESULT_UNKNOWN) {}
 
diff --git a/mojo/edk/system/data_pipe_consumer_dispatcher.cc b/mojo/edk/system/data_pipe_consumer_dispatcher.cc
index 521456e..cdf553f 100644
--- a/mojo/edk/system/data_pipe_consumer_dispatcher.cc
+++ b/mojo/edk/system/data_pipe_consumer_dispatcher.cc
@@ -72,9 +72,13 @@
 }
 
 RefPtr<Dispatcher>
-DataPipeConsumerDispatcher::CreateEquivalentDispatcherAndCloseImplNoLock() {
+DataPipeConsumerDispatcher::CreateEquivalentDispatcherAndCloseImplNoLock(
+    MessagePipe* /*message_pipe*/,
+    unsigned /*port*/) {
   mutex().AssertHeld();
 
+  CancelAllAwakablesNoLock();
+
   auto dispatcher = DataPipeConsumerDispatcher::Create();
   dispatcher->Init(std::move(data_pipe_));
   return dispatcher;
diff --git a/mojo/edk/system/data_pipe_consumer_dispatcher.h b/mojo/edk/system/data_pipe_consumer_dispatcher.h
index ba69a35..aa4455a 100644
--- a/mojo/edk/system/data_pipe_consumer_dispatcher.h
+++ b/mojo/edk/system/data_pipe_consumer_dispatcher.h
@@ -46,8 +46,9 @@
   // |Dispatcher| protected methods:
   void CancelAllAwakablesNoLock() override;
   void CloseImplNoLock() override;
-  util::RefPtr<Dispatcher> CreateEquivalentDispatcherAndCloseImplNoLock()
-      override;
+  util::RefPtr<Dispatcher> CreateEquivalentDispatcherAndCloseImplNoLock(
+      MessagePipe* message_pipe,
+      unsigned port) override;
   MojoResult SetDataPipeConsumerOptionsImplNoLock(
       UserPointer<const MojoDataPipeConsumerOptions> options) override;
   MojoResult GetDataPipeConsumerOptionsImplNoLock(
diff --git a/mojo/edk/system/data_pipe_producer_dispatcher.cc b/mojo/edk/system/data_pipe_producer_dispatcher.cc
index bfa7646..e89f44f 100644
--- a/mojo/edk/system/data_pipe_producer_dispatcher.cc
+++ b/mojo/edk/system/data_pipe_producer_dispatcher.cc
@@ -72,9 +72,13 @@
 }
 
 RefPtr<Dispatcher>
-DataPipeProducerDispatcher::CreateEquivalentDispatcherAndCloseImplNoLock() {
+DataPipeProducerDispatcher::CreateEquivalentDispatcherAndCloseImplNoLock(
+    MessagePipe* /*message_pipe*/,
+    unsigned /*port*/) {
   mutex().AssertHeld();
 
+  CancelAllAwakablesNoLock();
+
   auto dispatcher = DataPipeProducerDispatcher::Create();
   dispatcher->Init(std::move(data_pipe_));
   return dispatcher;
diff --git a/mojo/edk/system/data_pipe_producer_dispatcher.h b/mojo/edk/system/data_pipe_producer_dispatcher.h
index 51675e7..c024c18 100644
--- a/mojo/edk/system/data_pipe_producer_dispatcher.h
+++ b/mojo/edk/system/data_pipe_producer_dispatcher.h
@@ -46,8 +46,9 @@
   // |Dispatcher| protected methods:
   void CancelAllAwakablesNoLock() override;
   void CloseImplNoLock() override;
-  util::RefPtr<Dispatcher> CreateEquivalentDispatcherAndCloseImplNoLock()
-      override;
+  util::RefPtr<Dispatcher> CreateEquivalentDispatcherAndCloseImplNoLock(
+      MessagePipe* message_pipe,
+      unsigned port) override;
   MojoResult SetDataPipeProducerOptionsImplNoLock(
       UserPointer<const MojoDataPipeProducerOptions> options) override;
   MojoResult GetDataPipeProducerOptionsImplNoLock(
diff --git a/mojo/edk/system/dispatcher.cc b/mojo/edk/system/dispatcher.cc
index 386fa0e..f3ef810 100644
--- a/mojo/edk/system/dispatcher.cc
+++ b/mojo/edk/system/dispatcher.cc
@@ -526,13 +526,14 @@
   CloseImplNoLock();
 }
 
-RefPtr<Dispatcher> Dispatcher::CreateEquivalentDispatcherAndCloseNoLock() {
+RefPtr<Dispatcher> Dispatcher::CreateEquivalentDispatcherAndCloseNoLock(
+    MessagePipe* message_pipe,
+    unsigned port) {
   mutex_.AssertHeld();
   DCHECK(!is_closed_);
 
   is_closed_ = true;
-  CancelAllAwakablesNoLock();
-  return CreateEquivalentDispatcherAndCloseImplNoLock();
+  return CreateEquivalentDispatcherAndCloseImplNoLock(message_pipe, port);
 }
 
 void Dispatcher::StartSerialize(Channel* channel,
diff --git a/mojo/edk/system/dispatcher.h b/mojo/edk/system/dispatcher.h
index f22765b..ff8ba87 100644
--- a/mojo/edk/system/dispatcher.h
+++ b/mojo/edk/system/dispatcher.h
@@ -35,15 +35,16 @@
 
 namespace system {
 
+class Awakable;
 class Channel;
 class Core;
 class Dispatcher;
 class DispatcherTransport;
 class HandleTable;
 class LocalMessagePipeEndpoint;
+class MessagePipe;
 class ProxyMessagePipeEndpoint;
 class TransportData;
-class Awakable;
 
 using DispatcherVector = std::vector<util::RefPtr<Dispatcher>>;
 
@@ -259,9 +260,15 @@
   virtual void CancelAllAwakablesNoLock() MOJO_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
   virtual void CloseImplNoLock() MOJO_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
 
-  virtual util::RefPtr<Dispatcher>
-  CreateEquivalentDispatcherAndCloseImplNoLock()
-      MOJO_EXCLUSIVE_LOCKS_REQUIRED(mutex_) = 0;
+  // This is called by |CreateEquivalentDispatcherAndCloseNoLock()|. It should
+  // produce "close" this dispatcher and return a new one equivalent to it.
+  // Note: Probably the first thing an implementation should do is call
+  // |CancelAllAwakablesNoLock()| (or equivalent); unlike |CloseNoLock()|,
+  // |CreateEquivalentDispatcherAndCloseNoLock()| does not do this
+  // automatically.
+  virtual util::RefPtr<Dispatcher> CreateEquivalentDispatcherAndCloseImplNoLock(
+      MessagePipe* message_pipe,
+      unsigned port) MOJO_EXCLUSIVE_LOCKS_REQUIRED(mutex_) = 0;
 
   // These are to be overridden by subclasses (if necessary). They are never
   // called after the dispatcher has been closed. See the descriptions of the
@@ -379,9 +386,12 @@
   // dispatcher -- and close (i.e., disable) this dispatcher. I.e., this
   // dispatcher will look as though it was closed, but the resource it
   // represents will be assigned to the new dispatcher. This must be called
-  // under the dispatcher's lock.
-  util::RefPtr<Dispatcher> CreateEquivalentDispatcherAndCloseNoLock()
-      MOJO_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+  // under the dispatcher's lock. If the resulting dispatcher will be put into a
+  // message on a message pipe, then |message_pipe| will be set appropriately
+  // (otherwise, it may be null) and |port| will be set to the destination port.
+  util::RefPtr<Dispatcher> CreateEquivalentDispatcherAndCloseNoLock(
+      MessagePipe* message_pipe,
+      unsigned port) MOJO_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
 
   // API to serialize dispatchers to a |Channel|, exposed to only
   // |TransportData| (via |TransportData|). They may only be called on a
@@ -440,7 +450,7 @@
 //
 // Note: This class is deliberately "thin" -- no more expensive than a
 // |Dispatcher*|.
-class DispatcherTransport {
+class DispatcherTransport final {
  public:
   DispatcherTransport() : dispatcher_(nullptr) {}
 
@@ -451,16 +461,15 @@
     return dispatcher_->IsBusyNoLock();
   }
   void Close() MOJO_NOT_THREAD_SAFE { dispatcher_->CloseNoLock(); }
-  util::RefPtr<Dispatcher> CreateEquivalentDispatcherAndClose()
-      MOJO_NOT_THREAD_SAFE {
-    return dispatcher_->CreateEquivalentDispatcherAndCloseNoLock();
+  util::RefPtr<Dispatcher> CreateEquivalentDispatcherAndClose(
+      MessagePipe* message_pipe,
+      unsigned port) MOJO_NOT_THREAD_SAFE {
+    return dispatcher_->CreateEquivalentDispatcherAndCloseNoLock(message_pipe,
+                                                                 port);
   }
 
   bool is_valid() const { return !!dispatcher_; }
 
- protected:
-  Dispatcher* dispatcher() { return dispatcher_; }
-
  private:
   friend class Dispatcher::HandleTableAccess;
 
diff --git a/mojo/edk/system/message_pipe.cc b/mojo/edk/system/message_pipe.cc
index 26f93cb..cb8fc23 100644
--- a/mojo/edk/system/message_pipe.cc
+++ b/mojo/edk/system/message_pipe.cc
@@ -124,11 +124,8 @@
 }
 
 void MessagePipe::CancelAllAwakables(unsigned port) {
-  DCHECK(port == 0 || port == 1);
-
   MutexLocker locker(&mutex_);
-  DCHECK(endpoints_[port]);
-  endpoints_[port]->CancelAllAwakables();
+  CancelAllAwakablesNoLock(port);
 }
 
 void MessagePipe::Close(unsigned port) {
@@ -286,6 +283,13 @@
   return true;
 }
 
+void MessagePipe::CancelAllAwakablesNoLock(unsigned port) {
+  DCHECK(port == 0 || port == 1);
+  mutex_.AssertHeld();
+  DCHECK(endpoints_[port]);
+  endpoints_[port]->CancelAllAwakables();
+}
+
 bool MessagePipe::OnReadMessage(unsigned port, MessageInTransit* message) {
   MutexLocker locker(&mutex_);
 
@@ -355,27 +359,6 @@
     std::vector<DispatcherTransport>* transports) {
   DCHECK(!message->has_dispatchers());
 
-  // You're not allowed to send either handle to a message pipe over the message
-  // pipe, so check for this. (The case of trying to write a handle to itself is
-  // taken care of by |Core|. That case kind of makes sense, but leads to
-  // complications if, e.g., both sides try to do the same thing with their
-  // respective handles simultaneously. The other case, of trying to write the
-  // peer handle to a handle, doesn't make sense -- since no handle will be
-  // available to read the message from.)
-  for (size_t i = 0; i < transports->size(); i++) {
-    if (!(*transports)[i].is_valid())
-      continue;
-    if ((*transports)[i].GetType() == Dispatcher::Type::MESSAGE_PIPE) {
-      MessagePipeDispatcherTransport mp_transport((*transports)[i]);
-      if (mp_transport.GetMessagePipe() == this) {
-        // The other case should have been disallowed by |Core|. (Note: |port|
-        // is the peer port of the handle given to |WriteMessage()|.)
-        DCHECK_EQ(mp_transport.GetPort(), port);
-        return MOJO_RESULT_INVALID_ARGUMENT;
-      }
-    }
-  }
-
   // Clone the dispatchers and attach them to the message. (This must be done as
   // a separate loop, since we want to leave the dispatchers alone on failure.)
   std::unique_ptr<DispatcherVector> dispatchers(new DispatcherVector());
@@ -383,7 +366,7 @@
   for (size_t i = 0; i < transports->size(); i++) {
     if ((*transports)[i].is_valid()) {
       dispatchers->push_back(
-          (*transports)[i].CreateEquivalentDispatcherAndClose());
+          (*transports)[i].CreateEquivalentDispatcherAndClose(this, port));
     } else {
       LOG(WARNING) << "Enqueueing null dispatcher";
       dispatchers->push_back(nullptr);
diff --git a/mojo/edk/system/message_pipe.h b/mojo/edk/system/message_pipe.h
index d54ba7c..4cffa35 100644
--- a/mojo/edk/system/message_pipe.h
+++ b/mojo/edk/system/message_pipe.h
@@ -117,6 +117,12 @@
       size_t* actual_size,
       std::vector<platform::ScopedPlatformHandle>* platform_handles);
 
+  // This "implements" |CancelAllAwakables()|, but assumes that |mutex_| is
+  // already held. This is for use by
+  // |MessagePipeDispatcher::CreateEquivalentDispatcherAndCloseImplNoLock()|.
+  void CancelAllAwakablesNoLock(unsigned port)
+      MOJO_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+
   // |ChannelEndpointClient| methods:
   bool OnReadMessage(unsigned port, MessageInTransit* message) override;
   void OnDetachFromChannel(unsigned port) override;
diff --git a/mojo/edk/system/message_pipe_dispatcher.cc b/mojo/edk/system/message_pipe_dispatcher.cc
index 005b094..a0d5d02 100644
--- a/mojo/edk/system/message_pipe_dispatcher.cc
+++ b/mojo/edk/system/message_pipe_dispatcher.cc
@@ -133,9 +133,22 @@
 }
 
 RefPtr<Dispatcher>
-MessagePipeDispatcher::CreateEquivalentDispatcherAndCloseImplNoLock() {
+MessagePipeDispatcher::CreateEquivalentDispatcherAndCloseImplNoLock(
+    MessagePipe* message_pipe,
+    unsigned port) {
   mutex().AssertHeld();
 
+  // "We" are being sent over our peer.
+  if (message_pipe == message_pipe_.get()) {
+    // A message pipe dispatcher can't be sent over itself (this should be
+    // disallowed by |Core|). Note that |port| is the destination port.
+    DCHECK_EQ(port, port_);
+    // In this case, |message_pipe_|'s mutex should already be held!
+    message_pipe_->CancelAllAwakablesNoLock(port_);
+  } else {
+    CancelAllAwakablesNoLock();
+  }
+
   // TODO(vtl): Currently, there are no options, so we just use
   // |kDefaultCreateOptions|. Eventually, we'll have to duplicate the options
   // too.
@@ -220,14 +233,5 @@
   return rv;
 }
 
-// MessagePipeDispatcherTransport ----------------------------------------------
-
-MessagePipeDispatcherTransport::MessagePipeDispatcherTransport(
-    DispatcherTransport transport)
-    : DispatcherTransport(transport) {
-  DCHECK_EQ(message_pipe_dispatcher()->GetType(),
-            Dispatcher::Type::MESSAGE_PIPE);
-}
-
 }  // namespace system
 }  // namespace mojo
diff --git a/mojo/edk/system/message_pipe_dispatcher.h b/mojo/edk/system/message_pipe_dispatcher.h
index be86377..8eb9d0c 100644
--- a/mojo/edk/system/message_pipe_dispatcher.h
+++ b/mojo/edk/system/message_pipe_dispatcher.h
@@ -16,7 +16,6 @@
 
 class ChannelEndpoint;
 class MessagePipe;
-class MessagePipeDispatcherTransport;
 
 // This is the |Dispatcher| implementation for message pipes (created by the
 // Mojo primitive |MojoCreateMessagePipe()|). This class is thread-safe.
@@ -64,8 +63,6 @@
                                                          size_t size);
 
  private:
-  friend class MessagePipeDispatcherTransport;
-
   MessagePipeDispatcher();
   ~MessagePipeDispatcher() override;
 
@@ -80,8 +77,9 @@
   // |Dispatcher| protected methods:
   void CancelAllAwakablesNoLock() override;
   void CloseImplNoLock() override;
-  util::RefPtr<Dispatcher> CreateEquivalentDispatcherAndCloseImplNoLock()
-      override;
+  util::RefPtr<Dispatcher> CreateEquivalentDispatcherAndCloseImplNoLock(
+      MessagePipe* message_pipe,
+      unsigned port) override;
   MojoResult WriteMessageImplNoLock(
       UserPointer<const void> bytes,
       uint32_t num_bytes,
@@ -117,23 +115,6 @@
   MOJO_DISALLOW_COPY_AND_ASSIGN(MessagePipeDispatcher);
 };
 
-class MessagePipeDispatcherTransport : public DispatcherTransport {
- public:
-  explicit MessagePipeDispatcherTransport(DispatcherTransport transport);
-
-  MessagePipe* GetMessagePipe() {
-    return message_pipe_dispatcher()->GetMessagePipeNoLock();
-  }
-  unsigned GetPort() { return message_pipe_dispatcher()->GetPortNoLock(); }
-
- private:
-  MessagePipeDispatcher* message_pipe_dispatcher() {
-    return static_cast<MessagePipeDispatcher*>(dispatcher());
-  }
-
-  // Copy and assign allowed.
-};
-
 }  // namespace system
 }  // namespace mojo
 
diff --git a/mojo/edk/system/mock_simple_dispatcher.cc b/mojo/edk/system/mock_simple_dispatcher.cc
index bd8b283..a1f080f 100644
--- a/mojo/edk/system/mock_simple_dispatcher.cc
+++ b/mojo/edk/system/mock_simple_dispatcher.cc
@@ -66,8 +66,10 @@
 MockSimpleDispatcher::~MockSimpleDispatcher() {}
 
 RefPtr<Dispatcher>
-MockSimpleDispatcher::CreateEquivalentDispatcherAndCloseImplNoLock()
-    MOJO_NO_THREAD_SAFETY_ANALYSIS {
+MockSimpleDispatcher::CreateEquivalentDispatcherAndCloseImplNoLock(
+    MessagePipe* /*message_pipe*/,
+    unsigned /*port*/) MOJO_NO_THREAD_SAFETY_ANALYSIS {
+  CancelAllAwakablesNoLock();
   return MakeRefCounted<MockSimpleDispatcher>(state_);
 }
 
diff --git a/mojo/edk/system/mock_simple_dispatcher.h b/mojo/edk/system/mock_simple_dispatcher.h
index 9776bd6..a578248 100644
--- a/mojo/edk/system/mock_simple_dispatcher.h
+++ b/mojo/edk/system/mock_simple_dispatcher.h
@@ -37,8 +37,9 @@
   explicit MockSimpleDispatcher(const HandleSignalsState& state);
   ~MockSimpleDispatcher() override;
 
-  util::RefPtr<Dispatcher> CreateEquivalentDispatcherAndCloseImplNoLock()
-      override;
+  util::RefPtr<Dispatcher> CreateEquivalentDispatcherAndCloseImplNoLock(
+      MessagePipe* message_pipe,
+      unsigned port) override;
 
   // |Dispatcher| override:
   HandleSignalsState GetHandleSignalsStateImplNoLock() const override;
diff --git a/mojo/edk/system/platform_handle_dispatcher.cc b/mojo/edk/system/platform_handle_dispatcher.cc
index f2c6e72..93d2b5c 100644
--- a/mojo/edk/system/platform_handle_dispatcher.cc
+++ b/mojo/edk/system/platform_handle_dispatcher.cc
@@ -86,8 +86,11 @@
 }
 
 RefPtr<Dispatcher>
-PlatformHandleDispatcher::CreateEquivalentDispatcherAndCloseImplNoLock() {
+PlatformHandleDispatcher::CreateEquivalentDispatcherAndCloseImplNoLock(
+    MessagePipe* /*message_pipe*/,
+    unsigned /*port*/) {
   mutex().AssertHeld();
+  CancelAllAwakablesNoLock();
   return Create(platform_handle_.Pass());
 }
 
diff --git a/mojo/edk/system/platform_handle_dispatcher.h b/mojo/edk/system/platform_handle_dispatcher.h
index 5190927..577323b 100644
--- a/mojo/edk/system/platform_handle_dispatcher.h
+++ b/mojo/edk/system/platform_handle_dispatcher.h
@@ -46,8 +46,9 @@
 
   // |Dispatcher| protected methods:
   void CloseImplNoLock() override;
-  util::RefPtr<Dispatcher> CreateEquivalentDispatcherAndCloseImplNoLock()
-      override;
+  util::RefPtr<Dispatcher> CreateEquivalentDispatcherAndCloseImplNoLock(
+      MessagePipe* message_pipe,
+      unsigned port) override;
   void StartSerializeImplNoLock(Channel* channel,
                                 size_t* max_size,
                                 size_t* max_platform_handles) override
diff --git a/mojo/edk/system/platform_handle_dispatcher_unittest.cc b/mojo/edk/system/platform_handle_dispatcher_unittest.cc
index 232c7df..938c220 100644
--- a/mojo/edk/system/platform_handle_dispatcher_unittest.cc
+++ b/mojo/edk/system/platform_handle_dispatcher_unittest.cc
@@ -102,7 +102,8 @@
   EXPECT_EQ(Dispatcher::Type::PLATFORM_HANDLE, transport.GetType());
   EXPECT_FALSE(transport.IsBusy());
 
-  auto generic_dispatcher = transport.CreateEquivalentDispatcherAndClose();
+  auto generic_dispatcher =
+      transport.CreateEquivalentDispatcherAndClose(nullptr, 0u);
   ASSERT_TRUE(generic_dispatcher);
 
   transport.End();
diff --git a/mojo/edk/system/shared_buffer_dispatcher.cc b/mojo/edk/system/shared_buffer_dispatcher.cc
index 3ea46d80..ee428cd 100644
--- a/mojo/edk/system/shared_buffer_dispatcher.cc
+++ b/mojo/edk/system/shared_buffer_dispatcher.cc
@@ -216,8 +216,13 @@
 }
 
 RefPtr<Dispatcher>
-SharedBufferDispatcher::CreateEquivalentDispatcherAndCloseImplNoLock() {
+SharedBufferDispatcher::CreateEquivalentDispatcherAndCloseImplNoLock(
+    MessagePipe* /*message_pipe*/,
+    unsigned /*port*/) {
   mutex().AssertHeld();
+
+  CancelAllAwakablesNoLock();
+
   DCHECK(shared_buffer_);
   return CreateInternal(std::move(shared_buffer_));
 }
diff --git a/mojo/edk/system/shared_buffer_dispatcher.h b/mojo/edk/system/shared_buffer_dispatcher.h
index 3f2f6e7..0286520 100644
--- a/mojo/edk/system/shared_buffer_dispatcher.h
+++ b/mojo/edk/system/shared_buffer_dispatcher.h
@@ -87,8 +87,9 @@
 
   // |Dispatcher| protected methods:
   void CloseImplNoLock() override;
-  util::RefPtr<Dispatcher> CreateEquivalentDispatcherAndCloseImplNoLock()
-      override;
+  util::RefPtr<Dispatcher> CreateEquivalentDispatcherAndCloseImplNoLock(
+      MessagePipe* message_pipe,
+      unsigned port) override;
   MojoResult DuplicateBufferHandleImplNoLock(
       UserPointer<const MojoDuplicateBufferHandleOptions> options,
       util::RefPtr<Dispatcher>* new_dispatcher) override;