Bug 998863: Asynchronous Plugin Initialization, Part 3: ipc/glue changes; r=dvander
authorAaron Klotz <aklotz@mozilla.com>
Mon, 29 Dec 2014 16:12:47 -0700
changeset 247406 07f4a782ff639fc59b91fba1972107c4e9ab65f6
parent 247405 70509d064c207374383ac5c1cacac9a90239126d
child 247407 5de6c193d867397e2ac277664922907310301bd1
push id4489
push userraliiev@mozilla.com
push dateMon, 23 Feb 2015 15:17:55 +0000
treeherdermozilla-beta@fd7c3dc24146 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdvander
bugs998863
milestone37.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 998863: Asynchronous Plugin Initialization, Part 3: ipc/glue changes; r=dvander
ipc/glue/MessageChannel.cpp
ipc/glue/MessageChannel.h
ipc/glue/WindowsMessageLoop.cpp
--- a/ipc/glue/MessageChannel.cpp
+++ b/ipc/glue/MessageChannel.cpp
@@ -293,16 +293,17 @@ MessageChannel::MessageChannel(MessageLi
     mDispatchingSyncMessagePriority(0),
     mDispatchingAsyncMessage(false),
     mDispatchingAsyncMessagePriority(0),
     mCurrentTransaction(0),
     mTimedOutMessageSeqno(0),
     mRecvdErrors(0),
     mRemoteStackDepthGuess(false),
     mSawInterruptOutMsg(false),
+    mIsWaitingForIncoming(false),
     mAbortOnError(false),
     mBlockScripts(false),
     mFlags(REQUIRE_DEFAULT),
     mPeerPidSet(false),
     mPeerPid(-1)
 {
     MOZ_COUNT_CTOR(ipc::MessageChannel);
 
@@ -659,17 +660,18 @@ MessageChannel::OnMessageReceivedFromLin
             mPending.erase((++it).base());
         } else {
             // No other messages with the same type/destination exist.
             compress = false;
         }
     }
 
     bool shouldWakeUp = AwaitingInterruptReply() ||
-                        (AwaitingSyncReply() && !ShouldDeferMessage(aMsg));
+                        (AwaitingSyncReply() && !ShouldDeferMessage(aMsg)) ||
+                        AwaitingIncomingMessage();
 
     // There are three cases we're concerned about, relating to the state of the
     // main thread:
     //
     // (1) We are waiting on a sync reply - main thread is blocked on the
     //     IPC monitor.
     //   - If the message is high priority, we wake up the main thread to
     //     deliver the message depending on ShouldDeferMessage. Otherwise, we
@@ -983,16 +985,45 @@ MessageChannel::Call(Message* aMsg, Mess
             return false;
         }
     }
 
     return true;
 }
 
 bool
+MessageChannel::WaitForIncomingMessage()
+{
+#ifdef OS_WIN
+    SyncStackFrame frame(this, true);
+#endif
+
+    { // Scope for lock
+        MonitorAutoLock lock(*mMonitor);
+        AutoEnterWaitForIncoming waitingForIncoming(*this);
+        if (mChannelState != ChannelConnected) {
+            return false;
+        }
+        if (!HasPendingEvents()) {
+            return WaitForInterruptNotify();
+        }
+    }
+
+    return OnMaybeDequeueOne();
+}
+
+bool
+MessageChannel::HasPendingEvents()
+{
+    AssertWorkerThread();
+    mMonitor->AssertCurrentThreadOwns();
+    return Connected() && !mPending.empty();
+}
+
+bool
 MessageChannel::InterruptEventOccurred()
 {
     AssertWorkerThread();
     mMonitor->AssertCurrentThreadOwns();
     IPC_ASSERT(InterruptStackDepth() > 0, "not in wait loop");
 
     return (!Connected() ||
             !mPending.empty() ||
@@ -1541,17 +1572,17 @@ void
 MessageChannel::OnChannelErrorFromLink()
 {
     AssertLinkThread();
     mMonitor->AssertCurrentThreadOwns();
 
     if (InterruptStackDepth() > 0)
         NotifyWorkerThread();
 
-    if (AwaitingSyncReply())
+    if (AwaitingSyncReply() || AwaitingIncomingMessage())
         NotifyWorkerThread();
 
     if (ChannelClosing != mChannelState) {
         if (mAbortOnError) {
             NS_RUNTIMEABORT("Aborting on channel error.");
         }
         mChannelState = ChannelError;
         mMonitor->Notify();
--- a/ipc/glue/MessageChannel.h
+++ b/ipc/glue/MessageChannel.h
@@ -119,16 +119,19 @@ class MessageChannel : HasResultCodes
     bool Echo(Message* aMsg);
 
     // Synchronously send |msg| (i.e., wait for |reply|)
     bool Send(Message* aMsg, Message* aReply);
 
     // Make an Interrupt call to the other side of the channel
     bool Call(Message* aMsg, Message* aReply);
 
+    // Wait until a message is received
+    bool WaitForIncomingMessage();
+
     bool CanSend() const;
 
     void SetReplyTimeoutMs(int32_t aTimeoutMs);
 
     bool IsOnCxxStack() const {
         return !mCxxStackFrames.empty();
     }
 
@@ -209,16 +212,17 @@ class MessageChannel : HasResultCodes
     bool MaybeHandleError(Result code, const Message& aMsg, const char* channelName);
 
     void Clear();
 
     // Send OnChannelConnected notification to listeners.
     void DispatchOnChannelConnected();
 
     bool InterruptEventOccurred();
+    bool HasPendingEvents();
 
     bool ProcessPendingRequest(const Message &aUrgent);
 
     void MaybeUndeferIncall();
     void EnqueuePendingMessages();
 
     // Executed on the worker thread. Dequeues one pending message.
     bool OnMaybeDequeueOne();
@@ -314,16 +318,40 @@ class MessageChannel : HasResultCodes
     int AwaitingSyncReplyPriority() const {
         mMonitor->AssertCurrentThreadOwns();
         return mAwaitingSyncReplyPriority;
     }
     bool AwaitingInterruptReply() const {
         mMonitor->AssertCurrentThreadOwns();
         return !mInterruptStack.empty();
     }
+    bool AwaitingIncomingMessage() const {
+        mMonitor->AssertCurrentThreadOwns();
+        return mIsWaitingForIncoming;
+    }
+
+    class MOZ_STACK_CLASS AutoEnterWaitForIncoming
+    {
+    public:
+        explicit AutoEnterWaitForIncoming(MessageChannel& aChannel)
+            : mChannel(aChannel)
+        {
+            aChannel.mMonitor->AssertCurrentThreadOwns();
+            aChannel.mIsWaitingForIncoming = true;
+        }
+
+        ~AutoEnterWaitForIncoming()
+        {
+            mChannel.mIsWaitingForIncoming = false;
+        }
+
+    private:
+        MessageChannel& mChannel;
+    };
+    friend class AutoEnterWaitForIncoming;
 
     // Returns true if we're dispatching a sync message's callback.
     bool DispatchingSyncMessage() const {
         AssertWorkerThread();
         return mDispatchingSyncMessage;
     }
 
     int DispatchingSyncMessagePriority() const {
@@ -634,16 +662,21 @@ class MessageChannel : HasResultCodes
     // protected by mMonitor.  It is managed exclusively by the helper
     // |class CxxStackFrame|.
     mozilla::Vector<InterruptFrame> mCxxStackFrames;
 
     // Did we process an Interrupt out-call during this stack?  Only meaningful in
     // ExitedCxxStack(), from which this variable is reset.
     bool mSawInterruptOutMsg;
 
+    // Are we waiting on this channel for an incoming message? This is used
+    // to implement WaitForIncomingMessage(). Must only be accessed while owning
+    // mMonitor.
+    bool mIsWaitingForIncoming;
+
     // Map of replies received "out of turn", because of Interrupt
     // in-calls racing with replies to outstanding in-calls.  See
     // https://bugzilla.mozilla.org/show_bug.cgi?id=521929.
     MessageMap mOutOfTurnReplies;
 
     // Stack of Interrupt in-calls that were deferred because of race
     // conditions.
     std::stack<Message> mDeferred;
--- a/ipc/glue/WindowsMessageLoop.cpp
+++ b/ipc/glue/WindowsMessageLoop.cpp
@@ -961,17 +961,17 @@ MessageChannel::WaitForInterruptNotify()
   MOZ_ASSERT(gUIThreadId, "InitUIThread was not called!");
 
   // Re-use sync notification wait code if this channel does not require
   // Windows message deferral behavior. 
   if (!(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION)) {
     return WaitForSyncNotify();
   }
 
-  if (!InterruptStackDepth()) {
+  if (!InterruptStackDepth() && !AwaitingIncomingMessage()) {
     // There is currently no way to recover from this condition.
     NS_RUNTIMEABORT("StackDepth() is 0 in call to MessageChannel::WaitForNotify!");
   }
 
   NS_ASSERTION(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION,
                "Shouldn't be here for channels that don't use message deferral!");
   NS_ASSERTION(mTopFrame && mTopFrame->mInterrupt,
                "Top frame is not a sync frame!");