Use Mojo pipes to signal sync IPC events

This transitions legacy sync IPC to use Mojo message pipe handles
for all of its sync event waiting. This is a necessary precursor to
mixing sync legacy IPC with sync Mojo IPC, and is also required to
support correct FIFO between ChannelProxy and Mojo Channel
associated interfaces.

Specifically:

 - Introduces a new IPC::MojoEvent type which is a WaitableEvent-like
   interface around a local message pipe.
 - Moves mojo::SyncHandleRegistry out of internal bindings API and
   exposes it publicly.
 - Replaces most uses of WaitableEvent with MojoEvent for sync IPC
 - Replaces all use of WaitableEvent::WaitMany for sync IPC
   with mojo::SyncHandleRegistry::WatchAllHandles.
 - Cleans up some unnecessary complexity in SyncMessage since
   pump_messages_event() was only being used with a single
   global event that's always signaled.

The system's behavior should be effectively unchanged by this CL,
but legacy sync IPC and mojo sync IPC can now be mixed freely.

BUG=612500
CQ_INCLUDE_TRYBOTS=tryserver.chromium.linux:linux_optional_gpu_tests_rel;tryserver.chromium.mac:mac_optional_gpu_tests_rel;tryserver.chromium.win:win_optional_gpu_tests_rel

Review-Url: https://codereview.chromium.org/2033243003
Cr-Commit-Position: refs/heads/master@{#399848}
diff --git a/ipc/ipc_sync_channel.cc b/ipc/ipc_sync_channel.cc
index 6485139..570647b 100644
--- a/ipc/ipc_sync_channel.cc
+++ b/ipc/ipc_sync_channel.cc
@@ -13,9 +13,10 @@
 #include "base/lazy_instance.h"
 #include "base/location.h"
 #include "base/logging.h"
+#include "base/macros.h"
 #include "base/memory/ptr_util.h"
+#include "base/run_loop.h"
 #include "base/synchronization/waitable_event.h"
-#include "base/synchronization/waitable_event_watcher.h"
 #include "base/threading/thread_local.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/trace_event.h"
@@ -23,12 +24,84 @@
 #include "ipc/ipc_logging.h"
 #include "ipc/ipc_message_macros.h"
 #include "ipc/ipc_sync_message.h"
+#include "ipc/mojo_event.h"
+#include "mojo/public/cpp/bindings/sync_handle_registry.h"
 
 using base::TimeDelta;
 using base::TimeTicks;
 using base::WaitableEvent;
 
 namespace IPC {
+
+namespace {
+
+// A lazy thread-local Mojo Event which is always signaled. Used to wake up the
+// sync waiter when a SyncMessage requires the MessageLoop to be pumped while
+// waiting for a reply. This object is created lazily and ref-counted so it can
+// be cleaned up when no longer in use.
+class PumpMessagesEvent {
+ public:
+  // Acquires the event for this thread. Creates a new instance if necessary.
+  static PumpMessagesEvent* Acquire() {
+    PumpMessagesEvent* pump_messages_event = g_event_.Pointer()->Get();
+    if (!pump_messages_event) {
+      pump_messages_event = new PumpMessagesEvent;
+      pump_messages_event->event_.Signal();
+      g_event_.Pointer()->Set(pump_messages_event);
+    }
+    pump_messages_event->ref_count_++;
+    return pump_messages_event;
+  }
+
+  // Releases a handle to this event. There must be a 1:1 correspondence between
+  // calls to Acquire() and calls to Release().
+  static void Release() {
+    PumpMessagesEvent* pump_messages_event = g_event_.Pointer()->Get();
+    DCHECK(pump_messages_event);
+    DCHECK_GT(pump_messages_event->ref_count_, 0);
+    pump_messages_event->ref_count_--;
+    if (!pump_messages_event->ref_count_) {
+      g_event_.Pointer()->Set(nullptr);
+      delete pump_messages_event;
+    }
+  }
+
+  const mojo::Handle& GetHandle() const { return event_.GetHandle(); }
+
+ private:
+  PumpMessagesEvent() {}
+  ~PumpMessagesEvent() {}
+
+  int ref_count_ = 0;
+  MojoEvent event_;
+
+  static base::LazyInstance<base::ThreadLocalPointer<PumpMessagesEvent>>
+      g_event_;
+
+  DISALLOW_COPY_AND_ASSIGN(PumpMessagesEvent);
+};
+
+// A generic callback used when watching handles synchronously. Sets |*signal|
+// to true. Also sets |*error| to true in case of an error.
+void OnSyncHandleReady(bool* signal, bool* error, MojoResult result) {
+  *signal = true;
+  *error = result != MOJO_RESULT_OK;
+}
+
+// A ReadyCallback for use with mojo::Watcher. Ignores the result (DCHECKs, but
+// is only used in cases where failure should be impossible) and runs
+// |callback|.
+void RunOnHandleReady(const base::Closure& callback, MojoResult result) {
+  DCHECK(result == MOJO_RESULT_OK || result == MOJO_RESULT_ABORTED);
+  if (result == MOJO_RESULT_OK)
+    callback.Run();
+}
+
+}  // namespace
+
+base::LazyInstance<base::ThreadLocalPointer<PumpMessagesEvent>>
+PumpMessagesEvent::g_event_ = LAZY_INSTANCE_INITIALIZER;
+
 // When we're blocked in a Send(), we need to process incoming synchronous
 // messages right away because it could be blocking our reply (either
 // directly from the same object we're calling, or indirectly through one or
@@ -155,7 +228,7 @@
     }
   }
 
-  WaitableEvent* dispatch_event() { return &dispatch_event_; }
+  MojoEvent* dispatch_event() { return &dispatch_event_; }
   base::SingleThreadTaskRunner* listener_task_runner() {
     return listener_task_runner_.get();
   }
@@ -177,11 +250,11 @@
     }
   }
 
-  base::WaitableEventWatcher* top_send_done_watcher() {
+  mojo::Watcher* top_send_done_watcher() {
     return top_send_done_watcher_;
   }
 
-  void set_top_send_done_watcher(base::WaitableEventWatcher* watcher) {
+  void set_top_send_done_watcher(mojo::Watcher* watcher) {
     top_send_done_watcher_ = watcher;
   }
 
@@ -192,8 +265,6 @@
   // as manual reset.
   ReceivedSyncMsgQueue()
       : message_queue_version_(0),
-        dispatch_event_(base::WaitableEvent::ResetPolicy::MANUAL,
-                        base::WaitableEvent::InitialState::NOT_SIGNALED),
         listener_task_runner_(base::ThreadTaskRunnerHandle::Get()),
         task_pending_(false),
         listener_count_(0),
@@ -214,19 +285,19 @@
 
   std::vector<QueuedMessage> received_replies_;
 
-  // Set when we got a synchronous message that we must respond to as the
+  // Signaled when we get a synchronous message that we must respond to, as the
   // sender needs its reply before it can reply to our original synchronous
   // message.
-  WaitableEvent dispatch_event_;
+  MojoEvent dispatch_event_;
   scoped_refptr<base::SingleThreadTaskRunner> listener_task_runner_;
   base::Lock message_lock_;
   bool task_pending_;
   int listener_count_;
 
-  // The current send done event watcher for this thread. Used to maintain
-  // a local global stack of send done watchers to ensure that nested sync
+  // The current send done handle watcher for this thread. Used to maintain
+  // a thread-local stack of send done watchers to ensure that nested sync
   // message loops complete correctly.
-  base::WaitableEventWatcher* top_send_done_watcher_;
+  mojo::Watcher* top_send_done_watcher_;
 };
 
 base::LazyInstance<base::ThreadLocalPointer<SyncChannel::ReceivedSyncMsgQueue> >
@@ -262,8 +333,7 @@
   // Send completes, so the event will need to remain set.
   PendingSyncMsg pending(
       SyncMessage::GetMessageId(*sync_msg), sync_msg->GetReplyDeserializer(),
-      new WaitableEvent(base::WaitableEvent::ResetPolicy::MANUAL,
-                        base::WaitableEvent::InitialState::NOT_SIGNALED));
+      new MojoEvent);
   base::AutoLock auto_lock(deserializers_lock_);
   deserializers_.push_back(pending);
 }
@@ -275,7 +345,7 @@
     PendingSyncMsg msg = deserializers_.back();
     delete msg.deserializer;
     delete msg.done_event;
-    msg.done_event = NULL;
+    msg.done_event = nullptr;
     deserializers_.pop_back();
     result = msg.send_result;
   }
@@ -292,12 +362,12 @@
   return result;
 }
 
-WaitableEvent* SyncChannel::SyncContext::GetSendDoneEvent() {
+MojoEvent* SyncChannel::SyncContext::GetSendDoneEvent() {
   base::AutoLock auto_lock(deserializers_lock_);
   return deserializers_.back().done_event;
 }
 
-WaitableEvent* SyncChannel::SyncContext::GetDispatchEvent() {
+MojoEvent* SyncChannel::SyncContext::GetDispatchEvent() {
   return received_sync_msgs_->dispatch_event();
 }
 
@@ -321,7 +391,7 @@
     DVLOG(1) << "Received error reply";
   }
 
-  base::WaitableEvent* done_event = deserializers_.back().done_event;
+  MojoEvent* done_event = deserializers_.back().done_event;
   TRACE_EVENT_FLOW_BEGIN0(
       TRACE_DISABLED_BY_DEFAULT("ipc.flow"),
       "SyncChannel::SyncContext::TryToUnblockListener", done_event);
@@ -367,7 +437,7 @@
 void SyncChannel::SyncContext::OnChannelOpened() {
   shutdown_watcher_.StartWatching(
       shutdown_event_,
-      base::Bind(&SyncChannel::SyncContext::OnWaitableEventSignaled,
+      base::Bind(&SyncChannel::SyncContext::OnShutdownEventSignaled,
                  base::Unretained(this)));
   Context::OnChannelOpened();
 }
@@ -390,21 +460,12 @@
   }
 }
 
-void SyncChannel::SyncContext::OnWaitableEventSignaled(WaitableEvent* event) {
-  if (event == shutdown_event_) {
-    // Process shut down before we can get a reply to a synchronous message.
-    // Cancel pending Send calls, which will end up setting the send done event.
-    CancelPendingSends();
-  } else {
-    // We got the reply, timed out or the process shutdown.
-    DCHECK_EQ(GetSendDoneEvent(), event);
-    base::MessageLoop::current()->QuitNow();
-  }
-}
+void SyncChannel::SyncContext::OnShutdownEventSignaled(WaitableEvent* event) {
+  DCHECK_EQ(event, shutdown_event_);
 
-base::WaitableEventWatcher::EventCallback
-    SyncChannel::SyncContext::MakeWaitableEventCallback() {
-  return base::Bind(&SyncChannel::SyncContext::OnWaitableEventSignaled, this);
+  // Process shut down before we can get a reply to a synchronous message.
+  // Cancel pending Send calls, which will end up setting the send done event.
+  CancelPendingSends();
 }
 
 // static
@@ -448,6 +509,10 @@
     const scoped_refptr<base::SingleThreadTaskRunner>& ipc_task_runner,
     WaitableEvent* shutdown_event)
     : ChannelProxy(new SyncContext(listener, ipc_task_runner, shutdown_event)) {
+  // Keep a thread-local PumpMessagesEvent alive at least as long as any
+  // SyncChannel exists. This is balanced in the SyncChannel destructor below.
+  PumpMessagesEvent::Acquire();
+
   // The current (listener) thread must be distinct from the IPC thread, or else
   // sending synchronous messages will deadlock.
   DCHECK_NE(ipc_task_runner.get(), base::ThreadTaskRunnerHandle::Get().get());
@@ -455,6 +520,7 @@
 }
 
 SyncChannel::~SyncChannel() {
+  PumpMessagesEvent::Release();
 }
 
 void SyncChannel::SetRestrictDispatchChannelGroup(int group) {
@@ -495,14 +561,14 @@
   }
 
   SyncMessage* sync_msg = static_cast<SyncMessage*>(message);
+  bool pump_messages = sync_msg->ShouldPumpMessages();
   context->Push(sync_msg);
-  WaitableEvent* pump_messages_event = sync_msg->pump_messages_event();
 
   ChannelProxy::Send(message);
 
   // Wait for reply, or for any other incoming synchronous messages.
   // *this* might get deleted, so only call static functions at this point.
-  WaitForReply(context.get(), pump_messages_event);
+  WaitForReply(context.get(), pump_messages);
 
   TRACE_EVENT_FLOW_END0(TRACE_DISABLED_BY_DEFAULT("ipc.flow"),
                         "SyncChannel::Send", context->GetSendDoneEvent());
@@ -510,19 +576,45 @@
   return context->Pop();
 }
 
-void SyncChannel::WaitForReply(
-    SyncContext* context, WaitableEvent* pump_messages_event) {
+void SyncChannel::WaitForReply(SyncContext* context, bool pump_messages) {
   context->DispatchMessages();
-  while (true) {
-    WaitableEvent* objects[] = {
-      context->GetDispatchEvent(),
-      context->GetSendDoneEvent(),
-      pump_messages_event
-    };
 
-    unsigned count = pump_messages_event ? 3: 2;
-    size_t result = WaitableEvent::WaitMany(objects, count);
-    if (result == 0 /* dispatch event */) {
+  PumpMessagesEvent* pump_messages_event = nullptr;
+  if (pump_messages)
+    pump_messages_event = PumpMessagesEvent::Acquire();
+
+  scoped_refptr<mojo::SyncHandleRegistry> registry =
+      mojo::SyncHandleRegistry::current();
+
+  while (true) {
+    bool dispatch = false;
+    bool send_done = false;
+    bool should_pump_messages = false;
+    bool error = false;
+    registry->RegisterHandle(context->GetDispatchEvent()->GetHandle(),
+                             MOJO_HANDLE_SIGNAL_READABLE,
+                             base::Bind(&OnSyncHandleReady, &dispatch, &error));
+    registry->RegisterHandle(
+        context->GetSendDoneEvent()->GetHandle(),
+        MOJO_HANDLE_SIGNAL_READABLE,
+        base::Bind(&OnSyncHandleReady, &send_done, &error));
+    if (pump_messages_event) {
+      registry->RegisterHandle(
+          pump_messages_event->GetHandle(), MOJO_HANDLE_SIGNAL_READABLE,
+          base::Bind(&OnSyncHandleReady, &should_pump_messages, &error));
+    }
+
+    const bool* stop_flags[] = { &dispatch, &send_done, &should_pump_messages };
+    bool result = registry->WatchAllHandles(stop_flags, 3);
+    DCHECK(result);
+    DCHECK(!error);
+
+    registry->UnregisterHandle(context->GetDispatchEvent()->GetHandle());
+    registry->UnregisterHandle(context->GetSendDoneEvent()->GetHandle());
+    if (pump_messages_event)
+      registry->UnregisterHandle(pump_messages_event->GetHandle());
+
+    if (dispatch) {
       // We're waiting for a reply, but we received a blocking synchronous
       // call.  We must process it or otherwise a deadlock might occur.
       context->GetDispatchEvent()->Reset();
@@ -530,72 +622,72 @@
       continue;
     }
 
-    if (result == 2 /* pump_messages_event */)
+    DCHECK(send_done || should_pump_messages);
+
+    if (should_pump_messages)
       WaitForReplyWithNestedMessageLoop(context);  // Run a nested message loop.
 
     break;
   }
+
+  if (pump_messages_event)
+    PumpMessagesEvent::Release();
 }
 
 void SyncChannel::WaitForReplyWithNestedMessageLoop(SyncContext* context) {
-  base::WaitableEventWatcher send_done_watcher;
+  mojo::Watcher send_done_watcher;
 
   ReceivedSyncMsgQueue* sync_msg_queue = context->received_sync_msgs();
-  DCHECK(sync_msg_queue != NULL);
+  DCHECK_NE(sync_msg_queue, nullptr);
 
-  base::WaitableEventWatcher* old_send_done_event_watcher =
-      sync_msg_queue->top_send_done_watcher();
+  mojo::Watcher* old_watcher = sync_msg_queue->top_send_done_watcher();
+  mojo::Handle old_handle(mojo::kInvalidHandleValue);
+  mojo::Watcher::ReadyCallback old_callback;
 
-  base::WaitableEventWatcher::EventCallback old_callback;
-  base::WaitableEvent* old_event = NULL;
-
-  // Maintain a local global stack of send done delegates to ensure that
-  // nested sync calls complete in the correct sequence, i.e. the
-  // outermost call completes first, etc.
-  if (old_send_done_event_watcher) {
-    old_callback = old_send_done_event_watcher->callback();
-    old_event = old_send_done_event_watcher->GetWatchedEvent();
-    old_send_done_event_watcher->StopWatching();
+  // Maintain a thread-local stack of watchers to ensure nested calls complete
+  // in the correct sequence, i.e. the outermost call completes first, etc.
+  if (old_watcher) {
+    old_callback = old_watcher->ready_callback();
+    old_handle = old_watcher->handle();
+    old_watcher->Cancel();
   }
 
   sync_msg_queue->set_top_send_done_watcher(&send_done_watcher);
 
-  send_done_watcher.StartWatching(context->GetSendDoneEvent(),
-                                  context->MakeWaitableEventCallback());
-
   {
+    base::RunLoop nested_loop;
+    send_done_watcher.Start(
+        context->GetSendDoneEvent()->GetHandle(), MOJO_HANDLE_SIGNAL_READABLE,
+        base::Bind(&RunOnHandleReady, nested_loop.QuitClosure()));
+
     base::MessageLoop::ScopedNestableTaskAllower allow(
         base::MessageLoop::current());
-    base::MessageLoop::current()->Run();
+    nested_loop.Run();
+    send_done_watcher.Cancel();
   }
 
-  sync_msg_queue->set_top_send_done_watcher(old_send_done_event_watcher);
-  if (old_send_done_event_watcher && old_event) {
-    old_send_done_event_watcher->StartWatching(old_event, old_callback);
-  }
+  sync_msg_queue->set_top_send_done_watcher(old_watcher);
+  if (old_watcher)
+    old_watcher->Start(old_handle, MOJO_HANDLE_SIGNAL_READABLE, old_callback);
 }
 
-void SyncChannel::OnWaitableEventSignaled(WaitableEvent* event) {
-  DCHECK(event == sync_context()->GetDispatchEvent());
-  // The call to DispatchMessages might delete this object, so reregister
-  // the object watcher first.
-  event->Reset();
-  dispatch_watcher_.StartWatching(event, dispatch_watcher_callback_);
-  sync_context()->DispatchMessages();
+void SyncChannel::OnDispatchHandleReady(MojoResult result) {
+  DCHECK(result == MOJO_RESULT_OK || result == MOJO_RESULT_ABORTED);
+  if (result == MOJO_RESULT_OK) {
+    sync_context()->GetDispatchEvent()->Reset();
+    sync_context()->DispatchMessages();
+  }
 }
 
 void SyncChannel::StartWatching() {
   // Ideally we only want to watch this object when running a nested message
   // loop.  However, we don't know when it exits if there's another nested
   // message loop running under it or not, so we wouldn't know whether to
-  // stop or keep watching.  So we always watch it, and create the event as
-  // manual reset since the object watcher might otherwise reset the event
-  // when we're doing a WaitMany.
-  dispatch_watcher_callback_ =
-      base::Bind(&SyncChannel::OnWaitableEventSignaled,
-                  base::Unretained(this));
-  dispatch_watcher_.StartWatching(sync_context()->GetDispatchEvent(),
-                                  dispatch_watcher_callback_);
+  // stop or keep watching.  So we always watch it.
+  dispatch_watcher_.Start(sync_context()->GetDispatchEvent()->GetHandle(),
+                          MOJO_HANDLE_SIGNAL_READABLE,
+                          base::Bind(&SyncChannel::OnDispatchHandleReady,
+                                     base::Unretained(this)));
 }
 
 void SyncChannel::OnChannelInit() {