LCOV - code coverage report
Current view: top level - ipc/glue - MessageChannel.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 538 1281 42.0 %
Date: 2017-07-14 16:53:18 Functions: 75 136 55.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  * vim: sw=4 ts=4 et :
       3             :  */
       4             : /* This Source Code Form is subject to the terms of the Mozilla Public
       5             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       6             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       7             : 
       8             : #include "mozilla/ipc/MessageChannel.h"
       9             : 
      10             : #include "mozilla/Assertions.h"
      11             : #include "mozilla/DebugOnly.h"
      12             : #include "mozilla/dom/ScriptSettings.h"
      13             : #include "mozilla/ipc/ProtocolUtils.h"
      14             : #include "mozilla/Logging.h"
      15             : #include "mozilla/Move.h"
      16             : #include "mozilla/SizePrintfMacros.h"
      17             : #include "mozilla/Sprintf.h"
      18             : #include "mozilla/Telemetry.h"
      19             : #include "mozilla/TimeStamp.h"
      20             : #include "nsAppRunner.h"
      21             : #include "nsAutoPtr.h"
      22             : #include "nsDebug.h"
      23             : #include "nsISupportsImpl.h"
      24             : #include "nsContentUtils.h"
      25             : #include <math.h>
      26             : 
      27             : #ifdef MOZ_TASK_TRACER
      28             : #include "GeckoTaskTracer.h"
      29             : using namespace mozilla::tasktracer;
      30             : #endif
      31             : 
      32             : using mozilla::Move;
      33             : 
      34             : // Undo the damage done by mozzconf.h
      35             : #undef compress
      36             : 
      37             : // Logging seems to be somewhat broken on b2g.
      38             : #ifdef MOZ_B2G
      39             : #define IPC_LOG(...)
      40             : #else
      41             : static mozilla::LazyLogModule sLogModule("ipc");
      42             : #define IPC_LOG(...) MOZ_LOG(sLogModule, LogLevel::Debug, (__VA_ARGS__))
      43             : #endif
      44             : 
      45             : /*
      46             :  * IPC design:
      47             :  *
      48             :  * There are three kinds of messages: async, sync, and intr. Sync and intr
      49             :  * messages are blocking.
      50             :  *
      51             :  * Terminology: To dispatch a message Foo is to run the RecvFoo code for
      52             :  * it. This is also called "handling" the message.
      53             :  *
      54             :  * Sync and async messages can sometimes "nest" inside other sync messages
      55             :  * (i.e., while waiting for the sync reply, we can dispatch the inner
      56             :  * message). Intr messages cannot nest.  The three possible nesting levels are
      57             :  * NOT_NESTED, NESTED_INSIDE_SYNC, and NESTED_INSIDE_CPOW.  The intended uses
      58             :  * are:
      59             :  *   NOT_NESTED - most messages.
      60             :  *   NESTED_INSIDE_SYNC - CPOW-related messages, which are always sync
      61             :  *                        and can go in either direction.
      62             :  *   NESTED_INSIDE_CPOW - messages where we don't want to dispatch
      63             :  *                        incoming CPOWs while waiting for the response.
      64             :  * These nesting levels are ordered: NOT_NESTED, NESTED_INSIDE_SYNC,
      65             :  * NESTED_INSIDE_CPOW.  Async messages cannot be NESTED_INSIDE_SYNC but they can
      66             :  * be NESTED_INSIDE_CPOW.
      67             :  *
      68             :  * To avoid jank, the parent process is not allowed to send NOT_NESTED sync messages.
      69             :  * When a process is waiting for a response to a sync message
      70             :  * M0, it will dispatch an incoming message M if:
      71             :  *   1. M has a higher nesting level than M0, or
      72             :  *   2. if M has the same nesting level as M0 and we're in the child, or
      73             :  *   3. if M has the same nesting level as M0 and it was sent by the other side
      74             :  *      while dispatching M0.
      75             :  * The idea is that messages with higher nesting should take precendence. The
      76             :  * purpose of rule 2 is to handle a race where both processes send to each other
      77             :  * simultaneously. In this case, we resolve the race in favor of the parent (so
      78             :  * the child dispatches first).
      79             :  *
      80             :  * Messages satisfy the following properties:
      81             :  *   A. When waiting for a response to a sync message, we won't dispatch any
      82             :  *      messages of nesting level.
      83             :  *   B. Messages of the same nesting level will be dispatched roughly in the
      84             :  *      order they were sent. The exception is when the parent and child send
      85             :  *      sync messages to each other simulataneously. In this case, the parent's
      86             :  *      message is dispatched first. While it is dispatched, the child may send
      87             :  *      further nested messages, and these messages may be dispatched before the
      88             :  *      child's original message. We can consider ordering to be preserved here
      89             :  *      because we pretend that the child's original message wasn't sent until
      90             :  *      after the parent's message is finished being dispatched.
      91             :  *
      92             :  * When waiting for a sync message reply, we dispatch an async message only if
      93             :  * it is NESTED_INSIDE_CPOW. Normally NESTED_INSIDE_CPOW async
      94             :  * messages are sent only from the child. However, the parent can send
      95             :  * NESTED_INSIDE_CPOW async messages when it is creating a bridged protocol.
      96             :  *
      97             :  * Intr messages are blocking and can nest, but they don't participate in the
      98             :  * nesting levels. While waiting for an intr response, all incoming messages are
      99             :  * dispatched until a response is received. When two intr messages race with
     100             :  * each other, a similar scheme is used to ensure that one side wins. The
     101             :  * winning side is chosen based on the message type.
     102             :  *
     103             :  * Intr messages differ from sync messages in that, while sending an intr
     104             :  * message, we may dispatch an async message. This causes some additional
     105             :  * complexity. One issue is that replies can be received out of order. It's also
     106             :  * more difficult to determine whether one message is nested inside
     107             :  * another. Consequently, intr handling uses mOutOfTurnReplies and
     108             :  * mRemoteStackDepthGuess, which are not needed for sync messages.
     109             :  */
     110             : 
     111             : using namespace mozilla;
     112             : using namespace mozilla::ipc;
     113             : using namespace std;
     114             : 
     115             : using mozilla::dom::AutoNoJSAPI;
     116             : using mozilla::dom::ScriptSettingsInitialized;
     117             : using mozilla::MonitorAutoLock;
     118             : using mozilla::MonitorAutoUnlock;
     119             : 
     120             : #define IPC_ASSERT(_cond, ...)                                      \
     121             :     do {                                                            \
     122             :         if (!(_cond))                                               \
     123             :             DebugAbort(__FILE__, __LINE__, #_cond,## __VA_ARGS__);  \
     124             :     } while (0)
     125             : 
     126             : static MessageChannel* gParentProcessBlocker;
     127             : 
     128             : namespace mozilla {
     129             : namespace ipc {
     130             : 
     131             : static const uint32_t kMinTelemetryMessageSize = 4096;
     132             : 
     133             : // Note: we round the time we spend to the nearest millisecond. So a min value
     134             : // of 1 ms actually captures from 500us and above.
     135             : static const uint32_t kMinTelemetryIPCWriteLatencyMs = 1;
     136             : 
     137             : // Note: we round the time we spend waiting for a response to the nearest
     138             : // millisecond. So a min value of 1 ms actually captures from 500us and above.
     139             : // This is used for both the sending and receiving side telemetry for sync IPC,
     140             : // (IPC_SYNC_MAIN_LATENCY_MS and IPC_SYNC_RECEIVE_MS).
     141             : static const uint32_t kMinTelemetrySyncIPCLatencyMs = 1;
     142             : 
     143             : const int32_t MessageChannel::kNoTimeout = INT32_MIN;
     144             : 
     145             : // static
     146             : bool MessageChannel::sIsPumpingMessages = false;
     147             : 
     148             : enum Direction
     149             : {
     150             :     IN_MESSAGE,
     151             :     OUT_MESSAGE
     152             : };
     153             : 
     154             : class MessageChannel::InterruptFrame
     155             : {
     156             : private:
     157             :     enum Semantics
     158             :     {
     159             :         INTR_SEMS,
     160             :         SYNC_SEMS,
     161             :         ASYNC_SEMS
     162             :     };
     163             : 
     164             : public:
     165         976 :     InterruptFrame(Direction direction, const Message* msg)
     166         976 :       : mMessageName(msg->name()),
     167         976 :         mMessageRoutingId(msg->routing_id()),
     168        1952 :         mMesageSemantics(msg->is_interrupt() ? INTR_SEMS :
     169         976 :                           msg->is_sync() ? SYNC_SEMS :
     170             :                           ASYNC_SEMS),
     171             :         mDirection(direction),
     172        2928 :         mMoved(false)
     173             :     {
     174         976 :         MOZ_RELEASE_ASSERT(mMessageName);
     175         976 :     }
     176             : 
     177         983 :     InterruptFrame(InterruptFrame&& aOther)
     178         983 :     {
     179         983 :         MOZ_RELEASE_ASSERT(aOther.mMessageName);
     180         983 :         mMessageName = aOther.mMessageName;
     181         983 :         aOther.mMessageName = nullptr;
     182         983 :         mMoved = aOther.mMoved;
     183         983 :         aOther.mMoved = true;
     184             : 
     185         983 :         mMessageRoutingId = aOther.mMessageRoutingId;
     186         983 :         mMesageSemantics = aOther.mMesageSemantics;
     187         983 :         mDirection = aOther.mDirection;
     188         983 :     }
     189             : 
     190        1956 :     ~InterruptFrame()
     191        1956 :     {
     192        1956 :         MOZ_RELEASE_ASSERT(mMessageName || mMoved);
     193        1956 :     }
     194             : 
     195             :     InterruptFrame& operator=(InterruptFrame&& aOther)
     196             :     {
     197             :         MOZ_RELEASE_ASSERT(&aOther != this);
     198             :         this->~InterruptFrame();
     199             :         new (this) InterruptFrame(Move(aOther));
     200             :         return *this;
     201             :     }
     202             : 
     203        1948 :     bool IsInterruptIncall() const
     204             :     {
     205        1948 :         return INTR_SEMS == mMesageSemantics && IN_MESSAGE == mDirection;
     206             :     }
     207             : 
     208         976 :     bool IsInterruptOutcall() const
     209             :     {
     210         976 :         return INTR_SEMS == mMesageSemantics && OUT_MESSAGE == mDirection;
     211             :     }
     212             : 
     213        1948 :     bool IsOutgoingSync() const {
     214        1960 :         return (mMesageSemantics == INTR_SEMS || mMesageSemantics == SYNC_SEMS) &&
     215        1960 :                mDirection == OUT_MESSAGE;
     216             :     }
     217             : 
     218           0 :     void Describe(int32_t* id, const char** dir, const char** sems,
     219             :                   const char** name) const
     220             :     {
     221           0 :         *id = mMessageRoutingId;
     222           0 :         *dir = (IN_MESSAGE == mDirection) ? "in" : "out";
     223           0 :         *sems = (INTR_SEMS == mMesageSemantics) ? "intr" :
     224           0 :                 (SYNC_SEMS == mMesageSemantics) ? "sync" :
     225             :                 "async";
     226           0 :         *name = mMessageName;
     227           0 :     }
     228             : 
     229           0 :     int32_t GetRoutingId() const
     230             :     {
     231           0 :         return mMessageRoutingId;
     232             :     }
     233             : 
     234             : private:
     235             :     const char* mMessageName;
     236             :     int32_t mMessageRoutingId;
     237             :     Semantics mMesageSemantics;
     238             :     Direction mDirection;
     239             :     bool mMoved;
     240             : 
     241             :     // Disable harmful methods.
     242             :     InterruptFrame(const InterruptFrame& aOther) = delete;
     243             :     InterruptFrame& operator=(const InterruptFrame&) = delete;
     244             : };
     245             : 
     246             : class MOZ_STACK_CLASS MessageChannel::CxxStackFrame
     247             : {
     248             : public:
     249         976 :     CxxStackFrame(MessageChannel& that, Direction direction, const Message* msg)
     250         976 :       : mThat(that)
     251             :     {
     252         976 :         mThat.AssertWorkerThread();
     253             : 
     254         976 :         if (mThat.mCxxStackFrames.empty())
     255         845 :             mThat.EnteredCxxStack();
     256             : 
     257         976 :         if (!mThat.mCxxStackFrames.append(InterruptFrame(direction, msg)))
     258           0 :             MOZ_CRASH();
     259             : 
     260         976 :         const InterruptFrame& frame = mThat.mCxxStackFrames.back();
     261             : 
     262         976 :         if (frame.IsInterruptIncall())
     263           0 :             mThat.EnteredCall();
     264             : 
     265         976 :         if (frame.IsOutgoingSync())
     266           3 :             mThat.EnteredSyncSend();
     267             : 
     268         976 :         mThat.mSawInterruptOutMsg |= frame.IsInterruptOutcall();
     269         976 :     }
     270             : 
     271        1946 :     ~CxxStackFrame() {
     272         973 :         mThat.AssertWorkerThread();
     273             : 
     274         973 :         MOZ_RELEASE_ASSERT(!mThat.mCxxStackFrames.empty());
     275             : 
     276         973 :         const InterruptFrame& frame = mThat.mCxxStackFrames.back();
     277         973 :         bool exitingSync = frame.IsOutgoingSync();
     278         973 :         bool exitingCall = frame.IsInterruptIncall();
     279         973 :         mThat.mCxxStackFrames.shrinkBy(1);
     280             : 
     281         973 :         bool exitingStack = mThat.mCxxStackFrames.empty();
     282             : 
     283             :         // According how lifetime is declared, mListener on MessageChannel
     284             :         // lives longer than MessageChannel itself.  Hence is expected to
     285             :         // be alive.  There is nothing to even assert here, there is no place
     286             :         // we would be nullifying mListener on MessageChannel.
     287             : 
     288         973 :         if (exitingCall)
     289           0 :             mThat.ExitedCall();
     290             : 
     291         973 :         if (exitingSync)
     292           3 :             mThat.ExitedSyncSend();
     293             : 
     294         973 :         if (exitingStack)
     295         842 :             mThat.ExitedCxxStack();
     296         973 :     }
     297             : private:
     298             :     MessageChannel& mThat;
     299             : 
     300             :     // Disable harmful methods.
     301             :     CxxStackFrame() = delete;
     302             :     CxxStackFrame(const CxxStackFrame&) = delete;
     303             :     CxxStackFrame& operator=(const CxxStackFrame&) = delete;
     304             : };
     305             : 
     306             : class AutoEnterTransaction
     307             : {
     308             : public:
     309           3 :     explicit AutoEnterTransaction(MessageChannel *aChan,
     310             :                                   int32_t aMsgSeqno,
     311             :                                   int32_t aTransactionID,
     312             :                                   int aNestedLevel)
     313           3 :       : mChan(aChan),
     314             :         mActive(true),
     315             :         mOutgoing(true),
     316             :         mNestedLevel(aNestedLevel),
     317             :         mSeqno(aMsgSeqno),
     318             :         mTransaction(aTransactionID),
     319           3 :         mNext(mChan->mTransactionStack)
     320             :     {
     321           3 :         mChan->mMonitor->AssertCurrentThreadOwns();
     322           3 :         mChan->mTransactionStack = this;
     323           3 :     }
     324             : 
     325         455 :     explicit AutoEnterTransaction(MessageChannel *aChan, const IPC::Message &aMessage)
     326         455 :       : mChan(aChan),
     327             :         mActive(true),
     328             :         mOutgoing(false),
     329         455 :         mNestedLevel(aMessage.nested_level()),
     330         455 :         mSeqno(aMessage.seqno()),
     331         455 :         mTransaction(aMessage.transaction_id()),
     332        1820 :         mNext(mChan->mTransactionStack)
     333             :     {
     334         455 :         mChan->mMonitor->AssertCurrentThreadOwns();
     335             : 
     336         455 :         if (!aMessage.is_sync()) {
     337         452 :             mActive = false;
     338         452 :             return;
     339             :         }
     340             : 
     341           3 :         mChan->mTransactionStack = this;
     342             :     }
     343             : 
     344         910 :     ~AutoEnterTransaction() {
     345         455 :         mChan->mMonitor->AssertCurrentThreadOwns();
     346         455 :         if (mActive) {
     347           6 :             mChan->mTransactionStack = mNext;
     348             :         }
     349         455 :     }
     350             : 
     351           0 :     void Cancel() {
     352           0 :         AutoEnterTransaction *cur = mChan->mTransactionStack;
     353           0 :         MOZ_RELEASE_ASSERT(cur == this);
     354           0 :         while (cur && cur->mNestedLevel != IPC::Message::NOT_NESTED) {
     355             :             // Note that, in the following situation, we will cancel multiple
     356             :             // transactions:
     357             :             // 1. Parent sends NESTED_INSIDE_SYNC message P1 to child.
     358             :             // 2. Child sends NESTED_INSIDE_SYNC message C1 to child.
     359             :             // 3. Child dispatches P1, parent blocks.
     360             :             // 4. Child cancels.
     361             :             // In this case, both P1 and C1 are cancelled. The parent will
     362             :             // remove C1 from its queue when it gets the cancellation message.
     363           0 :             MOZ_RELEASE_ASSERT(cur->mActive);
     364           0 :             cur->mActive = false;
     365           0 :             cur = cur->mNext;
     366             :         }
     367             : 
     368           0 :         mChan->mTransactionStack = cur;
     369             : 
     370           0 :         MOZ_RELEASE_ASSERT(IsComplete());
     371           0 :     }
     372             : 
     373           6 :     bool AwaitingSyncReply() const {
     374           6 :         MOZ_RELEASE_ASSERT(mActive);
     375           6 :         if (mOutgoing) {
     376           6 :             return true;
     377             :         }
     378           0 :         return mNext ? mNext->AwaitingSyncReply() : false;
     379             :     }
     380             : 
     381           0 :     int AwaitingSyncReplyNestedLevel() const {
     382           0 :         MOZ_RELEASE_ASSERT(mActive);
     383           0 :         if (mOutgoing) {
     384           0 :             return mNestedLevel;
     385             :         }
     386           0 :         return mNext ? mNext->AwaitingSyncReplyNestedLevel() : 0;
     387             :     }
     388             : 
     389           0 :     bool DispatchingSyncMessage() const {
     390           0 :         MOZ_RELEASE_ASSERT(mActive);
     391           0 :         if (!mOutgoing) {
     392           0 :             return true;
     393             :         }
     394           0 :         return mNext ? mNext->DispatchingSyncMessage() : false;
     395             :     }
     396             : 
     397           0 :     int DispatchingSyncMessageNestedLevel() const {
     398           0 :         MOZ_RELEASE_ASSERT(mActive);
     399           0 :         if (!mOutgoing) {
     400           0 :             return mNestedLevel;
     401             :         }
     402           0 :         return mNext ? mNext->DispatchingSyncMessageNestedLevel() : 0;
     403             :     }
     404             : 
     405           0 :     int NestedLevel() const {
     406           0 :         MOZ_RELEASE_ASSERT(mActive);
     407           0 :         return mNestedLevel;
     408             :     }
     409             : 
     410           0 :     int32_t SequenceNumber() const {
     411           0 :         MOZ_RELEASE_ASSERT(mActive);
     412           0 :         return mSeqno;
     413             :     }
     414             : 
     415           3 :     int32_t TransactionID() const {
     416           3 :         MOZ_RELEASE_ASSERT(mActive);
     417           3 :         return mTransaction;
     418             :     }
     419             : 
     420           3 :     void ReceivedReply(IPC::Message&& aMessage) {
     421           3 :         MOZ_RELEASE_ASSERT(aMessage.seqno() == mSeqno);
     422           3 :         MOZ_RELEASE_ASSERT(aMessage.transaction_id() == mTransaction);
     423           3 :         MOZ_RELEASE_ASSERT(!mReply);
     424           3 :         IPC_LOG("Reply received on worker thread: seqno=%d", mSeqno);
     425           6 :         mReply = new IPC::Message(Move(aMessage));
     426           3 :         MOZ_RELEASE_ASSERT(IsComplete());
     427           3 :     }
     428             : 
     429           3 :     void HandleReply(IPC::Message&& aMessage) {
     430           3 :         AutoEnterTransaction *cur = mChan->mTransactionStack;
     431           3 :         MOZ_RELEASE_ASSERT(cur == this);
     432           3 :         while (cur) {
     433           3 :             MOZ_RELEASE_ASSERT(cur->mActive);
     434           3 :             if (aMessage.seqno() == cur->mSeqno) {
     435           3 :                 cur->ReceivedReply(Move(aMessage));
     436           3 :                 break;
     437             :             }
     438           0 :             cur = cur->mNext;
     439           0 :             MOZ_RELEASE_ASSERT(cur);
     440             :         }
     441           3 :     }
     442             : 
     443          12 :     bool IsComplete() {
     444          12 :         return !mActive || mReply;
     445             :     }
     446             : 
     447             :     bool IsOutgoing() {
     448             :         return mOutgoing;
     449             :     }
     450             : 
     451          18 :     bool IsCanceled() {
     452          18 :         return !mActive;
     453             :     }
     454             : 
     455           3 :     bool IsBottom() const {
     456           3 :         return !mNext;
     457             :     }
     458             : 
     459           3 :     bool IsError() {
     460           3 :         MOZ_RELEASE_ASSERT(mReply);
     461           3 :         return mReply->is_reply_error();
     462             :     }
     463             : 
     464           3 :     nsAutoPtr<IPC::Message> GetReply() {
     465           3 :         return Move(mReply);
     466             :     }
     467             : 
     468             : private:
     469             :     MessageChannel *mChan;
     470             : 
     471             :     // Active is true if this transaction is on the mChan->mTransactionStack
     472             :     // stack. Generally we're not on the stack if the transaction was canceled
     473             :     // or if it was for a message that doesn't require transactions (an async
     474             :     // message).
     475             :     bool mActive;
     476             : 
     477             :     // Is this stack frame for an outgoing message?
     478             :     bool mOutgoing;
     479             : 
     480             :     // Properties of the message being sent/received.
     481             :     int mNestedLevel;
     482             :     int32_t mSeqno;
     483             :     int32_t mTransaction;
     484             : 
     485             :     // Next item in mChan->mTransactionStack.
     486             :     AutoEnterTransaction *mNext;
     487             : 
     488             :     // Pointer the a reply received for this message, if one was received.
     489             :     nsAutoPtr<IPC::Message> mReply;
     490             : };
     491             : 
     492           3 : class PromiseReporter final : public nsIMemoryReporter
     493             : {
     494           2 :     ~PromiseReporter() {}
     495             : public:
     496             :     NS_DECL_THREADSAFE_ISUPPORTS
     497             : 
     498             :     NS_IMETHOD
     499           0 :     CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData,
     500             :                    bool aAnonymize) override
     501             :     {
     502           0 :         MOZ_COLLECT_REPORT(
     503             :             "unresolved-ipc-promises", KIND_OTHER, UNITS_COUNT, MessageChannel::gUnresolvedPromises,
     504           0 :             "Outstanding IPC async message promises that is still not resolved.");
     505           0 :         return NS_OK;
     506             :     }
     507             : };
     508             : 
     509          25 : NS_IMPL_ISUPPORTS(PromiseReporter, nsIMemoryReporter)
     510             : 
     511             : Atomic<size_t> MessageChannel::gUnresolvedPromises;
     512             : 
     513          40 : MessageChannel::MessageChannel(const char* aName,
     514          40 :                                IToplevelProtocol *aListener)
     515             :   : mName(aName),
     516             :     mListener(aListener),
     517             :     mChannelState(ChannelClosed),
     518             :     mSide(UnknownSide),
     519             :     mLink(nullptr),
     520             :     mWorkerLoop(nullptr),
     521             :     mChannelErrorTask(nullptr),
     522             :     mWorkerThread(nullptr),
     523             :     mTimeoutMs(kNoTimeout),
     524             :     mInTimeoutSecondHalf(false),
     525             :     mNextSeqno(0),
     526             :     mLastSendError(SyncSendError::SendSuccess),
     527             :     mDispatchingAsyncMessage(false),
     528             :     mDispatchingAsyncMessageNestedLevel(0),
     529             :     mTransactionStack(nullptr),
     530             :     mTimedOutMessageSeqno(0),
     531             :     mTimedOutMessageNestedLevel(0),
     532             :     mMaybeDeferredPendingCount(0),
     533             :     mRemoteStackDepthGuess(0),
     534             :     mSawInterruptOutMsg(false),
     535             :     mIsWaitingForIncoming(false),
     536             :     mAbortOnError(false),
     537             :     mNotifiedChannelDone(false),
     538             :     mFlags(REQUIRE_DEFAULT),
     539             :     mPeerPidSet(false),
     540             :     mPeerPid(-1),
     541          40 :     mIsPostponingSends(false)
     542             : {
     543          40 :     MOZ_COUNT_CTOR(ipc::MessageChannel);
     544             : 
     545             : #ifdef OS_WIN
     546             :     mTopFrame = nullptr;
     547             :     mIsSyncWaitingOnNonMainThread = false;
     548             : #endif
     549             : 
     550          80 :     mOnChannelConnectedTask = NewNonOwningCancelableRunnableMethod(
     551             :       "ipc::MessageChannel::DispatchOnChannelConnected",
     552             :       this,
     553          40 :       &MessageChannel::DispatchOnChannelConnected);
     554             : 
     555             : #ifdef OS_WIN
     556             :     mEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr);
     557             :     MOZ_RELEASE_ASSERT(mEvent, "CreateEvent failed! Nothing is going to work!");
     558             : #endif
     559             : 
     560             :     static Atomic<bool> registered;
     561          40 :     if (registered.compareExchange(false, true)) {
     562           3 :         RegisterStrongMemoryReporter(new PromiseReporter());
     563             :     }
     564          40 : }
     565             : 
     566           0 : MessageChannel::~MessageChannel()
     567             : {
     568           0 :     MOZ_COUNT_DTOR(ipc::MessageChannel);
     569           0 :     IPC_ASSERT(mCxxStackFrames.empty(), "mismatched CxxStackFrame ctor/dtors");
     570             : #ifdef OS_WIN
     571             :     if (mEvent) {
     572             :         BOOL ok = CloseHandle(mEvent);
     573             :         mEvent = nullptr;
     574             : 
     575             :         if (!ok) {
     576             :             gfxDevCrash(mozilla::gfx::LogReason::MessageChannelCloseFailure) <<
     577             :                 "MessageChannel failed to close. GetLastError: " <<
     578             :                 GetLastError();
     579             :         }
     580             :         MOZ_RELEASE_ASSERT(ok);
     581             :     } else {
     582             :         gfxDevCrash(mozilla::gfx::LogReason::MessageChannelCloseFailure) <<
     583             :                 "MessageChannel destructor ran without an mEvent Handle";
     584             :     }
     585             : #endif
     586           0 :     Clear();
     587           0 : }
     588             : 
     589             : #ifdef DEBUG
     590             : void
     591           6 : MessageChannel::AssertMaybeDeferredCountCorrect()
     592             : {
     593           6 :     size_t count = 0;
     594          19 :     for (MessageTask* task : mPending) {
     595          13 :         if (!IsAlwaysDeferred(task->Msg())) {
     596           0 :             count++;
     597             :         }
     598             :     }
     599             : 
     600           6 :     MOZ_ASSERT(count == mMaybeDeferredPendingCount);
     601           6 : }
     602             : #endif
     603             : 
     604             : // This function returns the current transaction ID. Since the notion of a
     605             : // "current transaction" can be hard to define when messages race with each
     606             : // other and one gets canceled and the other doesn't, we require that this
     607             : // function is only called when the current transaction is known to be for a
     608             : // NESTED_INSIDE_SYNC message. In that case, we know for sure what the caller is
     609             : // looking for.
     610             : int32_t
     611           0 : MessageChannel::CurrentNestedInsideSyncTransaction() const
     612             : {
     613           0 :     mMonitor->AssertCurrentThreadOwns();
     614           0 :     if (!mTransactionStack) {
     615           0 :         return 0;
     616             :     }
     617           0 :     MOZ_RELEASE_ASSERT(mTransactionStack->NestedLevel() == IPC::Message::NESTED_INSIDE_SYNC);
     618           0 :     return mTransactionStack->TransactionID();
     619             : }
     620             : 
     621             : bool
     622         529 : MessageChannel::AwaitingSyncReply() const
     623             : {
     624         529 :     mMonitor->AssertCurrentThreadOwns();
     625         529 :     return mTransactionStack ? mTransactionStack->AwaitingSyncReply() : false;
     626             : }
     627             : 
     628             : int
     629           6 : MessageChannel::AwaitingSyncReplyNestedLevel() const
     630             : {
     631           6 :     mMonitor->AssertCurrentThreadOwns();
     632           6 :     return mTransactionStack ? mTransactionStack->AwaitingSyncReplyNestedLevel() : 0;
     633             : }
     634             : 
     635             : bool
     636           0 : MessageChannel::DispatchingSyncMessage() const
     637             : {
     638           0 :     mMonitor->AssertCurrentThreadOwns();
     639           0 :     return mTransactionStack ? mTransactionStack->DispatchingSyncMessage() : false;
     640             : }
     641             : 
     642             : int
     643          15 : MessageChannel::DispatchingSyncMessageNestedLevel() const
     644             : {
     645          15 :     mMonitor->AssertCurrentThreadOwns();
     646          15 :     return mTransactionStack ? mTransactionStack->DispatchingSyncMessageNestedLevel() : 0;
     647             : }
     648             : 
     649             : static void
     650           0 : PrintErrorMessage(Side side, const char* channelName, const char* msg)
     651             : {
     652             :     const char *from = (side == ChildSide)
     653           0 :                        ? "Child"
     654           0 :                        : ((side == ParentSide) ? "Parent" : "Unknown");
     655           0 :     printf_stderr("\n###!!! [%s][%s] Error: %s\n\n", from, channelName, msg);
     656           0 : }
     657             : 
     658             : bool
     659        1040 : MessageChannel::Connected() const
     660             : {
     661        1040 :     mMonitor->AssertCurrentThreadOwns();
     662             : 
     663             :     // The transport layer allows us to send messages before
     664             :     // receiving the "connected" ack from the remote side.
     665        1040 :     return (ChannelOpening == mChannelState || ChannelConnected == mChannelState);
     666             : }
     667             : 
     668             : bool
     669           0 : MessageChannel::CanSend() const
     670             : {
     671           0 :     if (!mMonitor) {
     672           0 :         return false;
     673             :     }
     674           0 :     MonitorAutoLock lock(*mMonitor);
     675           0 :     return Connected();
     676             : }
     677             : 
     678             : void
     679           0 : MessageChannel::WillDestroyCurrentMessageLoop()
     680             : {
     681             : #if defined(DEBUG) && !defined(ANDROID)
     682             : #if defined(MOZ_CRASHREPORTER)
     683           0 :     CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("ProtocolName"),
     684           0 :                                        nsDependentCString(mName));
     685             : #endif
     686           0 :     MOZ_CRASH("MessageLoop destroyed before MessageChannel that's bound to it");
     687             : #endif
     688             : }
     689             : 
     690             : void
     691           0 : MessageChannel::Clear()
     692             : {
     693             :     // Don't clear mWorkerThread; we use it in AssertLinkThread() and
     694             :     // AssertWorkerThread().
     695             :     //
     696             :     // Also don't clear mListener.  If we clear it, then sending a message
     697             :     // through this channel after it's Clear()'ed can cause this process to
     698             :     // crash.
     699             :     //
     700             :     // In practice, mListener owns the channel, so the channel gets deleted
     701             :     // before mListener.  But just to be safe, mListener is a weak pointer.
     702             : 
     703             : #if !defined(ANDROID)
     704           0 :     if (!Unsound_IsClosed()) {
     705             : #if defined(MOZ_CRASHREPORTER)
     706           0 :         CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("ProtocolName"),
     707           0 :                                            nsDependentCString(mName));
     708             : #endif
     709           0 :         MOZ_CRASH("MessageChannel destroyed without being closed");
     710             :     }
     711             : #endif
     712             : 
     713           0 :     if (gParentProcessBlocker == this) {
     714           0 :         gParentProcessBlocker = nullptr;
     715             :     }
     716             : 
     717           0 :     if (mWorkerLoop) {
     718           0 :         mWorkerLoop->RemoveDestructionObserver(this);
     719             :     }
     720             : 
     721           0 :     gUnresolvedPromises -= mPendingPromises.size();
     722           0 :     for (auto& pair : mPendingPromises) {
     723           0 :         pair.second.mRejectFunction(pair.second.mPromise,
     724             :                                     PromiseRejectReason::ChannelClosed,
     725           0 :                                     __func__);
     726             :     }
     727           0 :     mPendingPromises.clear();
     728             : 
     729           0 :     mWorkerLoop = nullptr;
     730           0 :     delete mLink;
     731           0 :     mLink = nullptr;
     732             : 
     733           0 :     mOnChannelConnectedTask->Cancel();
     734             : 
     735           0 :     if (mChannelErrorTask) {
     736           0 :         mChannelErrorTask->Cancel();
     737           0 :         mChannelErrorTask = nullptr;
     738             :     }
     739             : 
     740             :     // Free up any memory used by pending messages.
     741           0 :     for (MessageTask* task : mPending) {
     742           0 :         task->Clear();
     743             :     }
     744           0 :     mPending.clear();
     745             : 
     746           0 :     mMaybeDeferredPendingCount = 0;
     747             : 
     748           0 :     mOutOfTurnReplies.clear();
     749           0 :     while (!mDeferred.empty()) {
     750           0 :         mDeferred.pop();
     751             :     }
     752           0 : }
     753             : 
     754             : bool
     755          28 : MessageChannel::Open(Transport* aTransport, MessageLoop* aIOLoop, Side aSide)
     756             : {
     757          28 :     NS_PRECONDITION(!mLink, "Open() called > once");
     758             : 
     759          28 :     mMonitor = new RefCountedMonitor();
     760          28 :     mWorkerLoop = MessageLoop::current();
     761          28 :     mWorkerThread = GetCurrentVirtualThread();
     762          28 :     mWorkerLoop->AddDestructionObserver(this);
     763          28 :     mListener->SetIsMainThreadProtocol();
     764             : 
     765          28 :     ProcessLink *link = new ProcessLink(this);
     766          28 :     link->Open(aTransport, aIOLoop, aSide); // :TODO: n.b.: sets mChild
     767          28 :     mLink = link;
     768          28 :     return true;
     769             : }
     770             : 
     771             : bool
     772           5 : MessageChannel::Open(MessageChannel *aTargetChan, MessageLoop *aTargetLoop, Side aSide)
     773             : {
     774             :     // Opens a connection to another thread in the same process.
     775             : 
     776             :     //  This handshake proceeds as follows:
     777             :     //  - Let A be the thread initiating the process (either child or parent)
     778             :     //    and B be the other thread.
     779             :     //  - A spawns thread for B, obtaining B's message loop
     780             :     //  - A creates ProtocolChild and ProtocolParent instances.
     781             :     //    Let PA be the one appropriate to A and PB the side for B.
     782             :     //  - A invokes PA->Open(PB, ...):
     783             :     //    - set state to mChannelOpening
     784             :     //    - this will place a work item in B's worker loop (see next bullet)
     785             :     //      and then spins until PB->mChannelState becomes mChannelConnected
     786             :     //    - meanwhile, on PB's worker loop, the work item is removed and:
     787             :     //      - invokes PB->SlaveOpen(PA, ...):
     788             :     //        - sets its state and that of PA to Connected
     789           5 :     NS_PRECONDITION(aTargetChan, "Need a target channel");
     790           5 :     NS_PRECONDITION(ChannelClosed == mChannelState, "Not currently closed");
     791             : 
     792           5 :     CommonThreadOpenInit(aTargetChan, aSide);
     793             : 
     794           5 :     Side oppSide = UnknownSide;
     795           5 :     switch(aSide) {
     796           5 :       case ChildSide: oppSide = ParentSide; break;
     797           0 :       case ParentSide: oppSide = ChildSide; break;
     798           0 :       case UnknownSide: break;
     799             :     }
     800             : 
     801           5 :     mMonitor = new RefCountedMonitor();
     802             : 
     803          10 :     MonitorAutoLock lock(*mMonitor);
     804           5 :     mChannelState = ChannelOpening;
     805          10 :     aTargetLoop->PostTask(NewNonOwningRunnableMethod<MessageChannel*, Side>(
     806             :       "ipc::MessageChannel::OnOpenAsSlave",
     807             :       aTargetChan,
     808             :       &MessageChannel::OnOpenAsSlave,
     809             :       this,
     810           5 :       oppSide));
     811             : 
     812          15 :     while (ChannelOpening == mChannelState)
     813           5 :         mMonitor->Wait();
     814           5 :     MOZ_RELEASE_ASSERT(ChannelConnected == mChannelState, "not connected when awoken");
     815          10 :     return (ChannelConnected == mChannelState);
     816             : }
     817             : 
     818             : void
     819           5 : MessageChannel::OnOpenAsSlave(MessageChannel *aTargetChan, Side aSide)
     820             : {
     821             :     // Invoked when the other side has begun the open.
     822           5 :     NS_PRECONDITION(ChannelClosed == mChannelState,
     823             :                     "Not currently closed");
     824           5 :     NS_PRECONDITION(ChannelOpening == aTargetChan->mChannelState,
     825             :                     "Target channel not in the process of opening");
     826             : 
     827           5 :     CommonThreadOpenInit(aTargetChan, aSide);
     828           5 :     mMonitor = aTargetChan->mMonitor;
     829             : 
     830          10 :     MonitorAutoLock lock(*mMonitor);
     831           5 :     MOZ_RELEASE_ASSERT(ChannelOpening == aTargetChan->mChannelState,
     832             :                           "Target channel not in the process of opening");
     833           5 :     mChannelState = ChannelConnected;
     834           5 :     aTargetChan->mChannelState = ChannelConnected;
     835           5 :     aTargetChan->mMonitor->Notify();
     836           5 : }
     837             : 
     838             : void
     839          10 : MessageChannel::CommonThreadOpenInit(MessageChannel *aTargetChan, Side aSide)
     840             : {
     841          10 :     mWorkerLoop = MessageLoop::current();
     842          10 :     mWorkerThread = GetCurrentVirtualThread();
     843          10 :     mWorkerLoop->AddDestructionObserver(this);
     844          10 :     mListener->SetIsMainThreadProtocol();
     845             : 
     846          10 :     mLink = new ThreadLink(this, aTargetChan);
     847          10 :     mSide = aSide;
     848          10 : }
     849             : 
     850             : bool
     851           0 : MessageChannel::Echo(Message* aMsg)
     852             : {
     853           0 :     nsAutoPtr<Message> msg(aMsg);
     854           0 :     AssertWorkerThread();
     855           0 :     mMonitor->AssertNotCurrentThreadOwns();
     856           0 :     if (MSG_ROUTING_NONE == msg->routing_id()) {
     857           0 :         ReportMessageRouteError("MessageChannel::Echo");
     858           0 :         return false;
     859             :     }
     860             : 
     861           0 :     MonitorAutoLock lock(*mMonitor);
     862             : 
     863           0 :     if (!Connected()) {
     864           0 :         ReportConnectionError("MessageChannel", msg);
     865           0 :         return false;
     866             :     }
     867             : 
     868           0 :     mLink->EchoMessage(msg.forget());
     869           0 :     return true;
     870             : }
     871             : 
     872             : bool
     873         518 : MessageChannel::Send(Message* aMsg)
     874             : {
     875         518 :     if (aMsg->size() >= kMinTelemetryMessageSize) {
     876           9 :         Telemetry::Accumulate(Telemetry::IPC_MESSAGE_SIZE2, aMsg->size());
     877             :     }
     878             : 
     879             :     // If the message was created by the IPC bindings, the create time will be
     880             :     // recorded. Use this information to report the IPC_WRITE_MAIN_THREAD_LATENCY_MS (time
     881             :     // from message creation to it being sent).
     882         518 :     if (NS_IsMainThread() && aMsg->create_time()) {
     883         376 :         uint32_t latencyMs = round((mozilla::TimeStamp::Now() - aMsg->create_time()).ToMilliseconds());
     884         376 :         if (latencyMs >= kMinTelemetryIPCWriteLatencyMs) {
     885             :             mozilla::Telemetry::Accumulate(mozilla::Telemetry::IPC_WRITE_MAIN_THREAD_LATENCY_MS,
     886           4 :                                            nsDependentCString(aMsg->name()),
     887           2 :                                            latencyMs);
     888             :         }
     889             :     }
     890             : 
     891         518 :     MOZ_RELEASE_ASSERT(!aMsg->is_sync());
     892         518 :     MOZ_RELEASE_ASSERT(aMsg->nested_level() != IPC::Message::NESTED_INSIDE_SYNC);
     893             : 
     894        1036 :     CxxStackFrame frame(*this, OUT_MESSAGE, aMsg);
     895             : 
     896        1036 :     nsAutoPtr<Message> msg(aMsg);
     897         518 :     AssertWorkerThread();
     898         518 :     mMonitor->AssertNotCurrentThreadOwns();
     899         518 :     if (MSG_ROUTING_NONE == msg->routing_id()) {
     900           0 :         ReportMessageRouteError("MessageChannel::Send");
     901           0 :         return false;
     902             :     }
     903             : 
     904        1036 :     MonitorAutoLock lock(*mMonitor);
     905         518 :     if (!Connected()) {
     906           0 :         ReportConnectionError("MessageChannel", msg);
     907           0 :         return false;
     908             :     }
     909             : 
     910         518 :     SendMessageToLink(msg.forget());
     911         518 :     return true;
     912             : }
     913             : 
     914             : void
     915         521 : MessageChannel::SendMessageToLink(Message* aMsg)
     916             : {
     917         521 :     if (mIsPostponingSends) {
     918           0 :         UniquePtr<Message> msg(aMsg);
     919           0 :         mPostponedSends.push_back(Move(msg));
     920           0 :         return;
     921             :     }
     922         521 :     mLink->SendMessage(aMsg);
     923             : }
     924             : 
     925             : void
     926           0 : MessageChannel::BeginPostponingSends()
     927             : {
     928           0 :     AssertWorkerThread();
     929           0 :     mMonitor->AssertNotCurrentThreadOwns();
     930             : 
     931           0 :     MonitorAutoLock lock(*mMonitor);
     932             :     {
     933           0 :         MOZ_ASSERT(!mIsPostponingSends);
     934           0 :         mIsPostponingSends = true;
     935             :     }
     936           0 : }
     937             : 
     938             : void
     939           0 : MessageChannel::StopPostponingSends()
     940             : {
     941             :     // Note: this can be called from any thread.
     942           0 :     MonitorAutoLock lock(*mMonitor);
     943             : 
     944           0 :     MOZ_ASSERT(mIsPostponingSends);
     945             : 
     946           0 :     for (UniquePtr<Message>& iter : mPostponedSends) {
     947           0 :       mLink->SendMessage(iter.release());
     948             :     }
     949             : 
     950             :     // We unset this after SendMessage so we can make correct thread
     951             :     // assertions in MessageLink.
     952           0 :     mIsPostponingSends = false;
     953           0 :     mPostponedSends.clear();
     954           0 : }
     955             : 
     956             : already_AddRefed<MozPromiseRefcountable>
     957           0 : MessageChannel::PopPromise(const Message& aMsg)
     958             : {
     959           0 :     auto iter = mPendingPromises.find(aMsg.seqno());
     960           0 :     if (iter != mPendingPromises.end()) {
     961           0 :         PromiseHolder ret = iter->second;
     962           0 :         mPendingPromises.erase(iter);
     963           0 :         gUnresolvedPromises--;
     964           0 :         return ret.mPromise.forget();
     965             :     }
     966           0 :     return nullptr;
     967             : }
     968             : 
     969             : void
     970          25 : MessageChannel::RejectPendingPromisesForActor(ActorIdType aActorId)
     971             : {
     972          25 :   auto itr = mPendingPromises.begin();
     973          25 :   while (itr != mPendingPromises.end()) {
     974           0 :     if (itr->second.mActorId != aActorId) {
     975           0 :       ++itr;
     976           0 :       continue;
     977             :     }
     978           0 :     auto& promise = itr->second.mPromise;
     979           0 :     itr->second.mRejectFunction(promise,
     980             :                                 PromiseRejectReason::ActorDestroyed,
     981           0 :                                 __func__);
     982             :     // Take special care of advancing the iterator since we are
     983             :     // removing it while iterating.
     984           0 :     itr = mPendingPromises.erase(itr);
     985           0 :     gUnresolvedPromises--;
     986             :   }
     987          25 : }
     988             : 
     989           6 : class BuildIDMessage : public IPC::Message
     990             : {
     991             : public:
     992           2 :     BuildIDMessage()
     993           2 :         : IPC::Message(MSG_ROUTING_NONE, BUILD_ID_MESSAGE_TYPE)
     994             :     {
     995           2 :     }
     996             :     void Log(const std::string& aPrefix, FILE* aOutf) const
     997             :     {
     998             :         fputs("(special `Build ID' message)", aOutf);
     999             :     }
    1000             : };
    1001             : 
    1002             : // Send the parent a special async message to allow it to detect if
    1003             : // this process is running a different build. This is a minor
    1004             : // variation on MessageChannel::Send(Message* aMsg).
    1005             : void
    1006           2 : MessageChannel::SendBuildID()
    1007             : {
    1008           2 :     MOZ_ASSERT(!XRE_IsParentProcess());
    1009           4 :     nsAutoPtr<BuildIDMessage> msg(new BuildIDMessage());
    1010           4 :     nsCString buildID(mozilla::PlatformBuildID());
    1011           2 :     IPC::WriteParam(msg, buildID);
    1012             : 
    1013           2 :     MOZ_RELEASE_ASSERT(!msg->is_sync());
    1014           2 :     MOZ_RELEASE_ASSERT(msg->nested_level() != IPC::Message::NESTED_INSIDE_SYNC);
    1015             : 
    1016           2 :     AssertWorkerThread();
    1017           2 :     mMonitor->AssertNotCurrentThreadOwns();
    1018             :     // Don't check for MSG_ROUTING_NONE.
    1019             : 
    1020           4 :     MonitorAutoLock lock(*mMonitor);
    1021           2 :     if (!Connected()) {
    1022           0 :         ReportConnectionError("MessageChannel", msg);
    1023           0 :         MOZ_CRASH();
    1024             :     }
    1025           2 :     mLink->SendMessage(msg.forget());
    1026           2 : }
    1027             : 
    1028           0 : class CancelMessage : public IPC::Message
    1029             : {
    1030             : public:
    1031           0 :     explicit CancelMessage(int transaction) :
    1032           0 :         IPC::Message(MSG_ROUTING_NONE, CANCEL_MESSAGE_TYPE)
    1033             :     {
    1034           0 :         set_transaction_id(transaction);
    1035           0 :     }
    1036             :     static bool Read(const Message* msg) {
    1037             :         return true;
    1038             :     }
    1039             :     void Log(const std::string& aPrefix, FILE* aOutf) const {
    1040             :         fputs("(special `Cancel' message)", aOutf);
    1041             :     }
    1042             : };
    1043             : 
    1044             : MOZ_NEVER_INLINE static void
    1045           2 : CheckChildProcessBuildID(const IPC::Message& aMsg)
    1046             : {
    1047           2 :     MOZ_ASSERT(XRE_IsParentProcess());
    1048           4 :     nsCString childBuildID;
    1049           2 :     PickleIterator msgIter(aMsg);
    1050           2 :     MOZ_ALWAYS_TRUE(IPC::ReadParam(&aMsg, &msgIter, &childBuildID));
    1051           2 :     aMsg.EndRead(msgIter);
    1052             : 
    1053           4 :     nsCString parentBuildID(mozilla::PlatformBuildID());
    1054             : 
    1055             :     // This assert can fail if the child process has been updated
    1056             :     // to a newer version while the parent process was running.
    1057           2 :     MOZ_RELEASE_ASSERT(parentBuildID == childBuildID);
    1058           2 : }
    1059             : 
    1060             : bool
    1061         531 : MessageChannel::MaybeInterceptSpecialIOMessage(const Message& aMsg)
    1062             : {
    1063         531 :     AssertLinkThread();
    1064         531 :     mMonitor->AssertCurrentThreadOwns();
    1065             : 
    1066         531 :     if (MSG_ROUTING_NONE == aMsg.routing_id()) {
    1067           2 :         if (GOODBYE_MESSAGE_TYPE == aMsg.type()) {
    1068             :             // :TODO: Sort out Close() on this side racing with Close() on the
    1069             :             // other side
    1070           0 :             mChannelState = ChannelClosing;
    1071           0 :             if (LoggingEnabled()) {
    1072           0 :                 printf("NOTE: %s process received `Goodbye', closing down\n",
    1073           0 :                        (mSide == ChildSide) ? "child" : "parent");
    1074             :             }
    1075           0 :             return true;
    1076           2 :         } else if (CANCEL_MESSAGE_TYPE == aMsg.type()) {
    1077           0 :             IPC_LOG("Cancel from message");
    1078           0 :             CancelTransaction(aMsg.transaction_id());
    1079           0 :             NotifyWorkerThread();
    1080           0 :             return true;
    1081           2 :         } else if (BUILD_ID_MESSAGE_TYPE == aMsg.type()) {
    1082           2 :             IPC_LOG("Build ID message");
    1083           2 :             CheckChildProcessBuildID(aMsg);
    1084           2 :             return true;
    1085             :         }
    1086             :     }
    1087         529 :     return false;
    1088             : }
    1089             : 
    1090             : /* static */ bool
    1091         997 : MessageChannel::IsAlwaysDeferred(const Message& aMsg)
    1092             : {
    1093             :     // If a message is not NESTED_INSIDE_CPOW and not sync, then we always defer
    1094             :     // it.
    1095        1978 :     return aMsg.nested_level() != IPC::Message::NESTED_INSIDE_CPOW &&
    1096        1979 :            !aMsg.is_sync();
    1097             : }
    1098             : 
    1099             : bool
    1100           3 : MessageChannel::ShouldDeferMessage(const Message& aMsg)
    1101             : {
    1102             :     // Never defer messages that have the highest nested level, even async
    1103             :     // ones. This is safe because only the child can send these messages, so
    1104             :     // they can never nest.
    1105           3 :     if (aMsg.nested_level() == IPC::Message::NESTED_INSIDE_CPOW) {
    1106           0 :         MOZ_ASSERT(!IsAlwaysDeferred(aMsg));
    1107           0 :         return false;
    1108             :     }
    1109             : 
    1110             :     // Unless they're NESTED_INSIDE_CPOW, we always defer async messages.
    1111             :     // Note that we never send an async NESTED_INSIDE_SYNC message.
    1112           3 :     if (!aMsg.is_sync()) {
    1113           3 :         MOZ_RELEASE_ASSERT(aMsg.nested_level() == IPC::Message::NOT_NESTED);
    1114           3 :         MOZ_ASSERT(IsAlwaysDeferred(aMsg));
    1115           3 :         return true;
    1116             :     }
    1117             : 
    1118           0 :     MOZ_ASSERT(!IsAlwaysDeferred(aMsg));
    1119             : 
    1120           0 :     int msgNestedLevel = aMsg.nested_level();
    1121           0 :     int waitingNestedLevel = AwaitingSyncReplyNestedLevel();
    1122             : 
    1123             :     // Always defer if the nested level of the incoming message is less than the
    1124             :     // nested level of the message we're awaiting.
    1125           0 :     if (msgNestedLevel < waitingNestedLevel)
    1126           0 :         return true;
    1127             : 
    1128             :     // Never defer if the message has strictly greater nested level.
    1129           0 :     if (msgNestedLevel > waitingNestedLevel)
    1130           0 :         return false;
    1131             : 
    1132             :     // When both sides send sync messages of the same nested level, we resolve the
    1133             :     // race by dispatching in the child and deferring the incoming message in
    1134             :     // the parent. However, the parent still needs to dispatch nested sync
    1135             :     // messages.
    1136             :     //
    1137             :     // Deferring in the parent only sort of breaks message ordering. When the
    1138             :     // child's message comes in, we can pretend the child hasn't quite
    1139             :     // finished sending it yet. Since the message is sync, we know that the
    1140             :     // child hasn't moved on yet.
    1141           0 :     return mSide == ParentSide && aMsg.transaction_id() != CurrentNestedInsideSyncTransaction();
    1142             : }
    1143             : 
    1144             : void
    1145         531 : MessageChannel::OnMessageReceivedFromLink(Message&& aMsg)
    1146             : {
    1147         531 :     AssertLinkThread();
    1148         531 :     mMonitor->AssertCurrentThreadOwns();
    1149             : 
    1150         531 :     if (MaybeInterceptSpecialIOMessage(aMsg))
    1151          60 :         return;
    1152             : 
    1153         529 :     mListener->OnChannelReceivedMessage(aMsg);
    1154             : 
    1155             :     // Regardless of the Interrupt stack, if we're awaiting a sync reply,
    1156             :     // we know that it needs to be immediately handled to unblock us.
    1157         529 :     if (aMsg.is_sync() && aMsg.is_reply()) {
    1158           3 :         IPC_LOG("Received reply seqno=%d xid=%d", aMsg.seqno(), aMsg.transaction_id());
    1159             : 
    1160           3 :         if (aMsg.seqno() == mTimedOutMessageSeqno) {
    1161             :             // Drop the message, but allow future sync messages to be sent.
    1162           0 :             IPC_LOG("Received reply to timedout message; igoring; xid=%d", mTimedOutMessageSeqno);
    1163           0 :             EndTimeout();
    1164           0 :             return;
    1165             :         }
    1166             : 
    1167           3 :         MOZ_RELEASE_ASSERT(AwaitingSyncReply());
    1168           3 :         MOZ_RELEASE_ASSERT(!mTimedOutMessageSeqno);
    1169             : 
    1170           3 :         mTransactionStack->HandleReply(Move(aMsg));
    1171           3 :         NotifyWorkerThread();
    1172           3 :         return;
    1173             :     }
    1174             : 
    1175             :     // Nested messages cannot be compressed.
    1176         526 :     MOZ_RELEASE_ASSERT(aMsg.compress_type() == IPC::Message::COMPRESSION_NONE ||
    1177             :                        aMsg.nested_level() == IPC::Message::NOT_NESTED);
    1178             : 
    1179         526 :     bool reuseTask = false;
    1180         526 :     if (aMsg.compress_type() == IPC::Message::COMPRESSION_ENABLED) {
    1181         114 :         bool compress = (!mPending.isEmpty() &&
    1182         114 :                          mPending.getLast()->Msg().type() == aMsg.type() &&
    1183         114 :                          mPending.getLast()->Msg().routing_id() == aMsg.routing_id());
    1184          61 :         if (compress) {
    1185             :             // This message type has compression enabled, and the back of the
    1186             :             // queue was the same message type and routed to the same destination.
    1187             :             // Replace it with the newer message.
    1188          53 :             MOZ_RELEASE_ASSERT(mPending.getLast()->Msg().compress_type() ==
    1189             :                                IPC::Message::COMPRESSION_ENABLED);
    1190          53 :             mPending.getLast()->Msg() = Move(aMsg);
    1191             : 
    1192          53 :             reuseTask = true;
    1193             :         }
    1194         465 :     } else if (aMsg.compress_type() == IPC::Message::COMPRESSION_ALL && !mPending.isEmpty()) {
    1195          45 :         for (MessageTask* p = mPending.getLast(); p; p = p->getPrevious()) {
    1196          45 :             if (p->Msg().type() == aMsg.type() &&
    1197           1 :                 p->Msg().routing_id() == aMsg.routing_id())
    1198             :             {
    1199             :                 // This message type has compression enabled, and the queue
    1200             :                 // holds a message with the same message type and routed to the
    1201             :                 // same destination. Erase it. Note that, since we always
    1202             :                 // compress these redundancies, There Can Be Only One.
    1203           1 :                 MOZ_RELEASE_ASSERT(p->Msg().compress_type() == IPC::Message::COMPRESSION_ALL);
    1204           1 :                 MOZ_RELEASE_ASSERT(IsAlwaysDeferred(p->Msg()));
    1205           1 :                 p->remove();
    1206           1 :                 break;
    1207             :             }
    1208             :         }
    1209             :     }
    1210             : 
    1211         526 :     bool alwaysDeferred = IsAlwaysDeferred(aMsg);
    1212             : 
    1213         526 :     bool wakeUpSyncSend = AwaitingSyncReply() && !ShouldDeferMessage(aMsg);
    1214             : 
    1215        1052 :     bool shouldWakeUp = AwaitingInterruptReply() ||
    1216        1052 :                         wakeUpSyncSend ||
    1217        1052 :                         AwaitingIncomingMessage();
    1218             : 
    1219             :     // Although we usually don't need to post a message task if
    1220             :     // shouldWakeUp is true, it's easier to post anyway than to have to
    1221             :     // guarantee that every Send call processes everything it's supposed to
    1222             :     // before returning.
    1223         526 :     bool shouldPostTask = !shouldWakeUp || wakeUpSyncSend;
    1224             : 
    1225         526 :     IPC_LOG("Receive on link thread; seqno=%d, xid=%d, shouldWakeUp=%d",
    1226             :             aMsg.seqno(), aMsg.transaction_id(), shouldWakeUp);
    1227             : 
    1228         526 :     if (reuseTask) {
    1229          53 :         return;
    1230             :     }
    1231             : 
    1232             :     // There are three cases we're concerned about, relating to the state of the
    1233             :     // main thread:
    1234             :     //
    1235             :     // (1) We are waiting on a sync reply - main thread is blocked on the
    1236             :     //     IPC monitor.
    1237             :     //   - If the message is NESTED_INSIDE_SYNC, we wake up the main thread to
    1238             :     //     deliver the message depending on ShouldDeferMessage. Otherwise, we
    1239             :     //     leave it in the mPending queue, posting a task to the main event
    1240             :     //     loop, where it will be processed once the synchronous reply has been
    1241             :     //     received.
    1242             :     //
    1243             :     // (2) We are waiting on an Interrupt reply - main thread is blocked on the
    1244             :     //     IPC monitor.
    1245             :     //   - Always notify and wake up the main thread.
    1246             :     //
    1247             :     // (3) We are not waiting on a reply.
    1248             :     //   - We post a task to the main event loop.
    1249             :     //
    1250             :     // Note that, we may notify the main thread even though the monitor is not
    1251             :     // blocked. This is okay, since we always check for pending events before
    1252             :     // blocking again.
    1253             : 
    1254             : #ifdef MOZ_TASK_TRACER
    1255             :     aMsg.TaskTracerDispatch();
    1256             : #endif
    1257        1419 :     RefPtr<MessageTask> task = new MessageTask(this, Move(aMsg));
    1258         473 :     mPending.insertBack(task);
    1259             : 
    1260         473 :     if (!alwaysDeferred) {
    1261          12 :         mMaybeDeferredPendingCount++;
    1262             :     }
    1263             : 
    1264         473 :     if (shouldWakeUp) {
    1265           0 :         NotifyWorkerThread();
    1266             :     }
    1267             : 
    1268         473 :     if (shouldPostTask) {
    1269         473 :         task->Post();
    1270             :     }
    1271             : }
    1272             : 
    1273             : void
    1274           2 : MessageChannel::PeekMessages(const std::function<bool(const Message& aMsg)>& aInvoke)
    1275             : {
    1276             :     // FIXME: We shouldn't be holding the lock for aInvoke!
    1277           4 :     MonitorAutoLock lock(*mMonitor);
    1278             : 
    1279           5 :     for (MessageTask* it : mPending) {
    1280           3 :         const Message &msg = it->Msg();
    1281           3 :         if (!aInvoke(msg)) {
    1282           0 :             break;
    1283             :         }
    1284             :     }
    1285           2 : }
    1286             : 
    1287             : void
    1288           6 : MessageChannel::ProcessPendingRequests(AutoEnterTransaction& aTransaction)
    1289             : {
    1290           6 :     mMonitor->AssertCurrentThreadOwns();
    1291             : 
    1292           6 :     AssertMaybeDeferredCountCorrect();
    1293           6 :     if (mMaybeDeferredPendingCount == 0) {
    1294           6 :         return;
    1295             :     }
    1296             : 
    1297           0 :     IPC_LOG("ProcessPendingRequests for seqno=%d, xid=%d",
    1298             :             aTransaction.SequenceNumber(), aTransaction.TransactionID());
    1299             : 
    1300             :     // Loop until there aren't any more nested messages to process.
    1301             :     for (;;) {
    1302             :         // If we canceled during ProcessPendingRequest, then we need to leave
    1303             :         // immediately because the results of ShouldDeferMessage will be
    1304             :         // operating with weird state (as if no Send is in progress). That could
    1305             :         // cause even NOT_NESTED sync messages to be processed (but not
    1306             :         // NOT_NESTED async messages), which would break message ordering.
    1307           0 :         if (aTransaction.IsCanceled()) {
    1308           0 :             return;
    1309             :         }
    1310             : 
    1311           0 :         mozilla::Vector<Message> toProcess;
    1312             : 
    1313           0 :         for (MessageTask* p = mPending.getFirst(); p; ) {
    1314           0 :             Message &msg = p->Msg();
    1315             : 
    1316           0 :             MOZ_RELEASE_ASSERT(!aTransaction.IsCanceled(),
    1317             :                                "Calling ShouldDeferMessage when cancelled");
    1318           0 :             bool defer = ShouldDeferMessage(msg);
    1319             : 
    1320             :             // Only log the interesting messages.
    1321           0 :             if (msg.is_sync() || msg.nested_level() == IPC::Message::NESTED_INSIDE_CPOW) {
    1322           0 :                 IPC_LOG("ShouldDeferMessage(seqno=%d) = %d", msg.seqno(), defer);
    1323             :             }
    1324             : 
    1325           0 :             if (!defer) {
    1326           0 :                 MOZ_ASSERT(!IsAlwaysDeferred(msg));
    1327             : 
    1328           0 :                 if (!toProcess.append(Move(msg)))
    1329           0 :                     MOZ_CRASH();
    1330             : 
    1331           0 :                 mMaybeDeferredPendingCount--;
    1332             : 
    1333           0 :                 p = p->removeAndGetNext();
    1334           0 :                 continue;
    1335             :             }
    1336           0 :             p = p->getNext();
    1337             :         }
    1338             : 
    1339           0 :         if (toProcess.empty()) {
    1340           0 :             break;
    1341             :         }
    1342             : 
    1343             :         // Processing these messages could result in more messages, so we
    1344             :         // loop around to check for more afterwards.
    1345             : 
    1346           0 :         for (auto it = toProcess.begin(); it != toProcess.end(); it++) {
    1347           0 :             ProcessPendingRequest(Move(*it));
    1348             :         }
    1349           0 :     }
    1350             : 
    1351           0 :     AssertMaybeDeferredCountCorrect();
    1352             : }
    1353             : 
    1354             : bool
    1355           3 : MessageChannel::Send(Message* aMsg, Message* aReply)
    1356             : {
    1357           3 :     mozilla::TimeStamp start = TimeStamp::Now();
    1358           3 :     if (aMsg->size() >= kMinTelemetryMessageSize) {
    1359           0 :         Telemetry::Accumulate(Telemetry::IPC_MESSAGE_SIZE2, aMsg->size());
    1360             :     }
    1361             : 
    1362           6 :     nsAutoPtr<Message> msg(aMsg);
    1363             : 
    1364             :     // Sanity checks.
    1365           3 :     AssertWorkerThread();
    1366           3 :     mMonitor->AssertNotCurrentThreadOwns();
    1367             : 
    1368             : #ifdef OS_WIN
    1369             :     SyncStackFrame frame(this, false);
    1370             :     NeuteredWindowRegion neuteredRgn(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION);
    1371             : #endif
    1372             : #ifdef MOZ_TASK_TRACER
    1373             :     AutoScopedLabel autolabel("sync message %s", aMsg->name());
    1374             : #endif
    1375             : 
    1376           6 :     CxxStackFrame f(*this, OUT_MESSAGE, msg);
    1377             : 
    1378           6 :     MonitorAutoLock lock(*mMonitor);
    1379             : 
    1380           3 :     if (mTimedOutMessageSeqno) {
    1381             :         // Don't bother sending another sync message if a previous one timed out
    1382             :         // and we haven't received a reply for it. Once the original timed-out
    1383             :         // message receives a reply, we'll be able to send more sync messages
    1384             :         // again.
    1385           0 :         IPC_LOG("Send() failed due to previous timeout");
    1386           0 :         mLastSendError = SyncSendError::PreviousTimeout;
    1387           0 :         return false;
    1388             :     }
    1389             : 
    1390           3 :     if (DispatchingSyncMessageNestedLevel() == IPC::Message::NOT_NESTED &&
    1391           0 :         msg->nested_level() > IPC::Message::NOT_NESTED)
    1392             :     {
    1393             :         // Don't allow sending CPOWs while we're dispatching a sync message.
    1394             :         // If you want to do that, use sendRpcMessage instead.
    1395           0 :         IPC_LOG("Nested level forbids send");
    1396           0 :         mLastSendError = SyncSendError::SendingCPOWWhileDispatchingSync;
    1397           0 :         return false;
    1398             :     }
    1399             : 
    1400           6 :     if (DispatchingSyncMessageNestedLevel() == IPC::Message::NESTED_INSIDE_CPOW ||
    1401           3 :         DispatchingAsyncMessageNestedLevel() == IPC::Message::NESTED_INSIDE_CPOW)
    1402             :     {
    1403             :         // Generally only the parent dispatches urgent messages. And the only
    1404             :         // sync messages it can send are NESTED_INSIDE_SYNC. Mainly we want to ensure
    1405             :         // here that we don't return false for non-CPOW messages.
    1406           0 :         MOZ_RELEASE_ASSERT(msg->nested_level() == IPC::Message::NESTED_INSIDE_SYNC);
    1407           0 :         IPC_LOG("Sending while dispatching urgent message");
    1408           0 :         mLastSendError = SyncSendError::SendingCPOWWhileDispatchingUrgent;
    1409           0 :         return false;
    1410             :     }
    1411             : 
    1412           6 :     if (msg->nested_level() < DispatchingSyncMessageNestedLevel() ||
    1413           3 :         msg->nested_level() < AwaitingSyncReplyNestedLevel())
    1414             :     {
    1415           0 :         MOZ_RELEASE_ASSERT(DispatchingSyncMessage() || DispatchingAsyncMessage());
    1416           0 :         MOZ_RELEASE_ASSERT(!mIsPostponingSends);
    1417           0 :         IPC_LOG("Cancel from Send");
    1418           0 :         CancelMessage *cancel = new CancelMessage(CurrentNestedInsideSyncTransaction());
    1419           0 :         CancelTransaction(CurrentNestedInsideSyncTransaction());
    1420           0 :         mLink->SendMessage(cancel);
    1421             :     }
    1422             : 
    1423           3 :     IPC_ASSERT(msg->is_sync(), "can only Send() sync messages here");
    1424             : 
    1425           3 :     IPC_ASSERT(msg->nested_level() >= DispatchingSyncMessageNestedLevel(),
    1426             :                "can't send sync message of a lesser nested level than what's being dispatched");
    1427           3 :     IPC_ASSERT(AwaitingSyncReplyNestedLevel() <= msg->nested_level(),
    1428             :                "nested sync message sends must be of increasing nested level");
    1429           3 :     IPC_ASSERT(DispatchingSyncMessageNestedLevel() != IPC::Message::NESTED_INSIDE_CPOW,
    1430             :                "not allowed to send messages while dispatching urgent messages");
    1431             : 
    1432           3 :     IPC_ASSERT(DispatchingAsyncMessageNestedLevel() != IPC::Message::NESTED_INSIDE_CPOW,
    1433             :                "not allowed to send messages while dispatching urgent messages");
    1434             : 
    1435           3 :     if (!Connected()) {
    1436           0 :         ReportConnectionError("MessageChannel::SendAndWait", msg);
    1437           0 :         mLastSendError = SyncSendError::NotConnectedBeforeSend;
    1438           0 :         return false;
    1439             :     }
    1440             : 
    1441           3 :     msg->set_seqno(NextSeqno());
    1442             : 
    1443           3 :     int32_t seqno = msg->seqno();
    1444           3 :     int nestedLevel = msg->nested_level();
    1445           3 :     msgid_t replyType = msg->type() + 1;
    1446             : 
    1447           3 :     AutoEnterTransaction *stackTop = mTransactionStack;
    1448             : 
    1449             :     // If the most recent message on the stack is NESTED_INSIDE_SYNC, then our
    1450             :     // message should nest inside that and we use the same transaction
    1451             :     // ID. Otherwise we need a new transaction ID (so we use the seqno of the
    1452             :     // message we're sending).
    1453           3 :     bool nest = stackTop && stackTop->NestedLevel() == IPC::Message::NESTED_INSIDE_SYNC;
    1454           3 :     int32_t transaction = nest ? stackTop->TransactionID() : seqno;
    1455           3 :     msg->set_transaction_id(transaction);
    1456             : 
    1457           3 :     bool handleWindowsMessages = mListener->HandleWindowsMessages(*aMsg);
    1458           6 :     AutoEnterTransaction transact(this, seqno, transaction, nestedLevel);
    1459             : 
    1460           3 :     IPC_LOG("Send seqno=%d, xid=%d", seqno, transaction);
    1461             : 
    1462             :     // msg will be destroyed soon, but name() is not owned by msg.
    1463           3 :     const char* msgName = msg->name();
    1464             : 
    1465           3 :     SendMessageToLink(msg.forget());
    1466             : 
    1467             :     while (true) {
    1468           6 :         MOZ_RELEASE_ASSERT(!transact.IsCanceled());
    1469           6 :         ProcessPendingRequests(transact);
    1470           6 :         if (transact.IsComplete()) {
    1471           3 :             break;
    1472             :         }
    1473           3 :         if (!Connected()) {
    1474           0 :             ReportConnectionError("MessageChannel::Send");
    1475           0 :             mLastSendError = SyncSendError::DisconnectedDuringSend;
    1476           0 :             return false;
    1477             :         }
    1478             : 
    1479           3 :         MOZ_RELEASE_ASSERT(!mTimedOutMessageSeqno);
    1480           3 :         MOZ_RELEASE_ASSERT(!transact.IsComplete());
    1481           3 :         MOZ_RELEASE_ASSERT(mTransactionStack == &transact);
    1482             : 
    1483           3 :         bool maybeTimedOut = !WaitForSyncNotify(handleWindowsMessages);
    1484             : 
    1485           3 :         if (mListener->NeedArtificialSleep()) {
    1486           0 :             MonitorAutoUnlock unlock(*mMonitor);
    1487           0 :             mListener->ArtificialSleep();
    1488             :         }
    1489             : 
    1490           3 :         if (!Connected()) {
    1491           0 :             ReportConnectionError("MessageChannel::SendAndWait");
    1492           0 :             mLastSendError = SyncSendError::DisconnectedDuringSend;
    1493           0 :             return false;
    1494             :         }
    1495             : 
    1496           3 :         if (transact.IsCanceled()) {
    1497           0 :             break;
    1498             :         }
    1499             : 
    1500           3 :         MOZ_RELEASE_ASSERT(mTransactionStack == &transact);
    1501             : 
    1502             :         // We only time out a message if it initiated a new transaction (i.e.,
    1503             :         // if neither side has any other message Sends on the stack).
    1504           3 :         bool canTimeOut = transact.IsBottom();
    1505           3 :         if (maybeTimedOut && canTimeOut && !ShouldContinueFromTimeout()) {
    1506             :             // Since ShouldContinueFromTimeout drops the lock, we need to
    1507             :             // re-check all our conditions here. We shouldn't time out if any of
    1508             :             // these things happen because there won't be a reply to the timed
    1509             :             // out message in these cases.
    1510           0 :             if (transact.IsComplete()) {
    1511           0 :                 break;
    1512             :             }
    1513             : 
    1514           0 :             IPC_LOG("Timing out Send: xid=%d", transaction);
    1515             : 
    1516           0 :             mTimedOutMessageSeqno = seqno;
    1517           0 :             mTimedOutMessageNestedLevel = nestedLevel;
    1518           0 :             mLastSendError = SyncSendError::TimedOut;
    1519           0 :             return false;
    1520             :         }
    1521             : 
    1522           3 :         if (transact.IsCanceled()) {
    1523           0 :             break;
    1524             :         }
    1525           3 :     }
    1526             : 
    1527           3 :     if (transact.IsCanceled()) {
    1528           0 :         IPC_LOG("Other side canceled seqno=%d, xid=%d", seqno, transaction);
    1529           0 :         mLastSendError = SyncSendError::CancelledAfterSend;
    1530           0 :         return false;
    1531             :     }
    1532             : 
    1533           3 :     if (transact.IsError()) {
    1534           0 :         IPC_LOG("Error: seqno=%d, xid=%d", seqno, transaction);
    1535           0 :         mLastSendError = SyncSendError::ReplyError;
    1536           0 :         return false;
    1537             :     }
    1538             : 
    1539           3 :     uint32_t latencyMs = round((TimeStamp::Now() - start).ToMilliseconds());
    1540           3 :     IPC_LOG("Got reply: seqno=%d, xid=%d, msgName=%s, latency=%ums",
    1541             :             seqno, transaction, msgName, latencyMs);
    1542             : 
    1543           6 :     nsAutoPtr<Message> reply = transact.GetReply();
    1544             : 
    1545           3 :     MOZ_RELEASE_ASSERT(reply);
    1546           3 :     MOZ_RELEASE_ASSERT(reply->is_reply(), "expected reply");
    1547           3 :     MOZ_RELEASE_ASSERT(!reply->is_reply_error());
    1548           3 :     MOZ_RELEASE_ASSERT(reply->seqno() == seqno);
    1549           3 :     MOZ_RELEASE_ASSERT(reply->type() == replyType, "wrong reply type");
    1550           3 :     MOZ_RELEASE_ASSERT(reply->is_sync());
    1551             : 
    1552           3 :     *aReply = Move(*reply);
    1553           3 :     if (aReply->size() >= kMinTelemetryMessageSize) {
    1554           0 :         Telemetry::Accumulate(Telemetry::IPC_REPLY_SIZE,
    1555           0 :                               nsDependentCString(msgName), aReply->size());
    1556             :     }
    1557             : 
    1558             :     // NOTE: Only collect IPC_SYNC_MAIN_LATENCY_MS on the main thread (bug 1343729)
    1559           3 :     if (NS_IsMainThread() && latencyMs >= kMinTelemetrySyncIPCLatencyMs) {
    1560             :       Telemetry::Accumulate(Telemetry::IPC_SYNC_MAIN_LATENCY_MS,
    1561           1 :                             nsDependentCString(msgName), latencyMs);
    1562             :     }
    1563           3 :     return true;
    1564             : }
    1565             : 
    1566             : bool
    1567           0 : MessageChannel::Call(Message* aMsg, Message* aReply)
    1568             : {
    1569           0 :     nsAutoPtr<Message> msg(aMsg);
    1570           0 :     AssertWorkerThread();
    1571           0 :     mMonitor->AssertNotCurrentThreadOwns();
    1572             : 
    1573             : #ifdef OS_WIN
    1574             :     SyncStackFrame frame(this, true);
    1575             : #endif
    1576             : #ifdef MOZ_TASK_TRACER
    1577             :     AutoScopedLabel autolabel("sync message %s", aMsg->name());
    1578             : #endif
    1579             : 
    1580             :     // This must come before MonitorAutoLock, as its destructor acquires the
    1581             :     // monitor lock.
    1582           0 :     CxxStackFrame cxxframe(*this, OUT_MESSAGE, msg);
    1583             : 
    1584           0 :     MonitorAutoLock lock(*mMonitor);
    1585           0 :     if (!Connected()) {
    1586           0 :         ReportConnectionError("MessageChannel::Call", msg);
    1587           0 :         return false;
    1588             :     }
    1589             : 
    1590             :     // Sanity checks.
    1591           0 :     IPC_ASSERT(!AwaitingSyncReply(),
    1592             :                "cannot issue Interrupt call while blocked on sync request");
    1593           0 :     IPC_ASSERT(!DispatchingSyncMessage(),
    1594             :                "violation of sync handler invariant");
    1595           0 :     IPC_ASSERT(msg->is_interrupt(), "can only Call() Interrupt messages here");
    1596           0 :     IPC_ASSERT(!mIsPostponingSends, "not postponing sends");
    1597             : 
    1598           0 :     msg->set_seqno(NextSeqno());
    1599           0 :     msg->set_interrupt_remote_stack_depth_guess(mRemoteStackDepthGuess);
    1600           0 :     msg->set_interrupt_local_stack_depth(1 + InterruptStackDepth());
    1601           0 :     mInterruptStack.push(MessageInfo(*msg));
    1602           0 :     mLink->SendMessage(msg.forget());
    1603             : 
    1604             :     while (true) {
    1605             :         // if a handler invoked by *Dispatch*() spun a nested event
    1606             :         // loop, and the connection was broken during that loop, we
    1607             :         // might have already processed the OnError event. if so,
    1608             :         // trying another loop iteration will be futile because
    1609             :         // channel state will have been cleared
    1610           0 :         if (!Connected()) {
    1611           0 :             ReportConnectionError("MessageChannel::Call");
    1612           0 :             return false;
    1613             :         }
    1614             : 
    1615             : #ifdef OS_WIN
    1616             :         // We need to limit the scoped of neuteredRgn to this spot in the code.
    1617             :         // Window neutering can't be enabled during some plugin calls because
    1618             :         // we then risk the neutered window procedure being subclassed by a
    1619             :         // plugin.
    1620             :         {
    1621             :             NeuteredWindowRegion neuteredRgn(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION);
    1622             :             /* We should pump messages at this point to ensure that the IPC peer
    1623             :                does not become deadlocked on a pending inter-thread SendMessage() */
    1624             :             neuteredRgn.PumpOnce();
    1625             :         }
    1626             : #endif
    1627             : 
    1628             :         // Now might be the time to process a message deferred because of race
    1629             :         // resolution.
    1630           0 :         MaybeUndeferIncall();
    1631             : 
    1632             :         // Wait for an event to occur.
    1633           0 :         while (!InterruptEventOccurred()) {
    1634           0 :             bool maybeTimedOut = !WaitForInterruptNotify();
    1635             : 
    1636             :             // We might have received a "subtly deferred" message in a nested
    1637             :             // loop that it's now time to process.
    1638           0 :             if (InterruptEventOccurred() ||
    1639           0 :                 (!maybeTimedOut && (!mDeferred.empty() || !mOutOfTurnReplies.empty())))
    1640             :             {
    1641           0 :                 break;
    1642             :             }
    1643             : 
    1644           0 :             if (maybeTimedOut && !ShouldContinueFromTimeout())
    1645           0 :                 return false;
    1646             :         }
    1647             : 
    1648           0 :         Message recvd;
    1649           0 :         MessageMap::iterator it;
    1650             : 
    1651           0 :         if ((it = mOutOfTurnReplies.find(mInterruptStack.top().seqno()))
    1652           0 :             != mOutOfTurnReplies.end())
    1653             :         {
    1654           0 :             recvd = Move(it->second);
    1655           0 :             mOutOfTurnReplies.erase(it);
    1656           0 :         } else if (!mPending.isEmpty()) {
    1657           0 :             RefPtr<MessageTask> task = mPending.popFirst();
    1658           0 :             recvd = Move(task->Msg());
    1659           0 :             if (!IsAlwaysDeferred(recvd)) {
    1660           0 :                 mMaybeDeferredPendingCount--;
    1661             :             }
    1662             :         } else {
    1663             :             // because of subtleties with nested event loops, it's possible
    1664             :             // that we got here and nothing happened.  or, we might have a
    1665             :             // deferred in-call that needs to be processed.  either way, we
    1666             :             // won't break the inner while loop again until something new
    1667             :             // happens.
    1668           0 :             continue;
    1669             :         }
    1670             : 
    1671             :         // If the message is not Interrupt, we can dispatch it as normal.
    1672           0 :         if (!recvd.is_interrupt()) {
    1673           0 :             DispatchMessage(Move(recvd));
    1674           0 :             if (!Connected()) {
    1675           0 :                 ReportConnectionError("MessageChannel::DispatchMessage");
    1676           0 :                 return false;
    1677             :             }
    1678           0 :             continue;
    1679             :         }
    1680             : 
    1681             :         // If the message is an Interrupt reply, either process it as a reply to our
    1682             :         // call, or add it to the list of out-of-turn replies we've received.
    1683           0 :         if (recvd.is_reply()) {
    1684           0 :             IPC_ASSERT(!mInterruptStack.empty(), "invalid Interrupt stack");
    1685             : 
    1686             :             // If this is not a reply the call we've initiated, add it to our
    1687             :             // out-of-turn replies and keep polling for events.
    1688             :             {
    1689           0 :                 const MessageInfo &outcall = mInterruptStack.top();
    1690             : 
    1691             :                 // Note, In the parent, sequence numbers increase from 0, and
    1692             :                 // in the child, they decrease from 0.
    1693           0 :                 if ((mSide == ChildSide && recvd.seqno() > outcall.seqno()) ||
    1694           0 :                     (mSide != ChildSide && recvd.seqno() < outcall.seqno()))
    1695             :                 {
    1696           0 :                     mOutOfTurnReplies[recvd.seqno()] = Move(recvd);
    1697           0 :                     continue;
    1698             :                 }
    1699             : 
    1700           0 :                 IPC_ASSERT(recvd.is_reply_error() ||
    1701             :                            (recvd.type() == (outcall.type() + 1) &&
    1702             :                             recvd.seqno() == outcall.seqno()),
    1703             :                            "somebody's misbehavin'", true);
    1704             :             }
    1705             : 
    1706             :             // We received a reply to our most recent outstanding call. Pop
    1707             :             // this frame and return the reply.
    1708           0 :             mInterruptStack.pop();
    1709             : 
    1710           0 :             bool is_reply_error = recvd.is_reply_error();
    1711           0 :             if (!is_reply_error) {
    1712           0 :                 *aReply = Move(recvd);
    1713             :             }
    1714             : 
    1715             :             // If we have no more pending out calls waiting on replies, then
    1716             :             // the reply queue should be empty.
    1717           0 :             IPC_ASSERT(!mInterruptStack.empty() || mOutOfTurnReplies.empty(),
    1718             :                        "still have pending replies with no pending out-calls",
    1719             :                        true);
    1720             : 
    1721           0 :             return !is_reply_error;
    1722             :         }
    1723             : 
    1724             :         // Dispatch an Interrupt in-call. Snapshot the current stack depth while we
    1725             :         // own the monitor.
    1726           0 :         size_t stackDepth = InterruptStackDepth();
    1727             :         {
    1728             : #ifdef MOZ_TASK_TRACER
    1729             :             Message::AutoTaskTracerRun tasktracerRun(recvd);
    1730             : #endif
    1731           0 :             MonitorAutoUnlock unlock(*mMonitor);
    1732             : 
    1733           0 :             CxxStackFrame frame(*this, IN_MESSAGE, &recvd);
    1734           0 :             DispatchInterruptMessage(Move(recvd), stackDepth);
    1735             :         }
    1736           0 :         if (!Connected()) {
    1737           0 :             ReportConnectionError("MessageChannel::DispatchInterruptMessage");
    1738           0 :             return false;
    1739             :         }
    1740           0 :     }
    1741             : 
    1742             :     return true;
    1743             : }
    1744             : 
    1745             : bool
    1746           0 : MessageChannel::WaitForIncomingMessage()
    1747             : {
    1748             : #ifdef OS_WIN
    1749             :     SyncStackFrame frame(this, true);
    1750             :     NeuteredWindowRegion neuteredRgn(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION);
    1751             : #endif
    1752             : 
    1753           0 :     MonitorAutoLock lock(*mMonitor);
    1754           0 :     AutoEnterWaitForIncoming waitingForIncoming(*this);
    1755           0 :     if (mChannelState != ChannelConnected) {
    1756           0 :         return false;
    1757             :     }
    1758           0 :     if (!HasPendingEvents()) {
    1759           0 :         return WaitForInterruptNotify();
    1760             :     }
    1761             : 
    1762           0 :     MOZ_RELEASE_ASSERT(!mPending.isEmpty());
    1763           0 :     RefPtr<MessageTask> task = mPending.getFirst();
    1764           0 :     RunMessage(*task);
    1765           0 :     return true;
    1766             : }
    1767             : 
    1768             : bool
    1769           0 : MessageChannel::HasPendingEvents()
    1770             : {
    1771           0 :     AssertWorkerThread();
    1772           0 :     mMonitor->AssertCurrentThreadOwns();
    1773           0 :     return Connected() && !mPending.isEmpty();
    1774             : }
    1775             : 
    1776             : bool
    1777           0 : MessageChannel::InterruptEventOccurred()
    1778             : {
    1779           0 :     AssertWorkerThread();
    1780           0 :     mMonitor->AssertCurrentThreadOwns();
    1781           0 :     IPC_ASSERT(InterruptStackDepth() > 0, "not in wait loop");
    1782             : 
    1783           0 :     return (!Connected() ||
    1784           0 :             !mPending.isEmpty() ||
    1785           0 :             (!mOutOfTurnReplies.empty() &&
    1786           0 :              mOutOfTurnReplies.find(mInterruptStack.top().seqno()) !=
    1787           0 :              mOutOfTurnReplies.end()));
    1788             : }
    1789             : 
    1790             : bool
    1791           0 : MessageChannel::ProcessPendingRequest(Message &&aUrgent)
    1792             : {
    1793           0 :     AssertWorkerThread();
    1794           0 :     mMonitor->AssertCurrentThreadOwns();
    1795             : 
    1796           0 :     IPC_LOG("Process pending: seqno=%d, xid=%d", aUrgent.seqno(), aUrgent.transaction_id());
    1797             : 
    1798           0 :     DispatchMessage(Move(aUrgent));
    1799           0 :     if (!Connected()) {
    1800           0 :         ReportConnectionError("MessageChannel::ProcessPendingRequest");
    1801           0 :         return false;
    1802             :     }
    1803             : 
    1804           0 :     return true;
    1805             : }
    1806             : 
    1807             : bool
    1808         483 : MessageChannel::ShouldRunMessage(const Message& aMsg)
    1809             : {
    1810         483 :     if (!mTimedOutMessageSeqno) {
    1811         483 :         return true;
    1812             :     }
    1813             : 
    1814             :     // If we've timed out a message and we're awaiting the reply to the timed
    1815             :     // out message, we have to be careful what messages we process. Here's what
    1816             :     // can go wrong:
    1817             :     // 1. child sends a NOT_NESTED sync message S
    1818             :     // 2. parent sends a NESTED_INSIDE_SYNC sync message H at the same time
    1819             :     // 3. parent times out H
    1820             :     // 4. child starts processing H and sends a NESTED_INSIDE_SYNC message H' nested
    1821             :     //    within the same transaction
    1822             :     // 5. parent dispatches S and sends reply
    1823             :     // 6. child asserts because it instead expected a reply to H'.
    1824             :     //
    1825             :     // To solve this, we refuse to process S in the parent until we get a reply
    1826             :     // to H. More generally, let the timed out message be M. We don't process a
    1827             :     // message unless the child would need the response to that message in order
    1828             :     // to process M. Those messages are the ones that have a higher nested level
    1829             :     // than M or that are part of the same transaction as M.
    1830           0 :     if (aMsg.nested_level() < mTimedOutMessageNestedLevel ||
    1831           0 :         (aMsg.nested_level() == mTimedOutMessageNestedLevel
    1832           0 :          && aMsg.transaction_id() != mTimedOutMessageSeqno))
    1833             :     {
    1834           0 :         return false;
    1835             :     }
    1836             : 
    1837           0 :     return true;
    1838             : }
    1839             : 
    1840             : void
    1841         455 : MessageChannel::RunMessage(MessageTask& aTask)
    1842             : {
    1843         455 :     AssertWorkerThread();
    1844         455 :     mMonitor->AssertCurrentThreadOwns();
    1845             : 
    1846         455 :     Message& msg = aTask.Msg();
    1847             : 
    1848         455 :     if (!Connected()) {
    1849           0 :         ReportConnectionError("RunMessage");
    1850           0 :         return;
    1851             :     }
    1852             : 
    1853             :     // Check that we're going to run the first message that's valid to run.
    1854             : #ifdef DEBUG
    1855         483 :     for (MessageTask* task : mPending) {
    1856         483 :         if (task == &aTask) {
    1857         455 :             break;
    1858             :         }
    1859             : 
    1860          28 :         MOZ_ASSERT(!ShouldRunMessage(task->Msg()) ||
    1861             :                    aTask.Msg().priority() != task->Msg().priority());
    1862             : 
    1863             :     }
    1864             : #endif
    1865             : 
    1866         455 :     if (!mDeferred.empty()) {
    1867           0 :         MaybeUndeferIncall();
    1868             :     }
    1869             : 
    1870         455 :     if (!ShouldRunMessage(msg)) {
    1871           0 :         return;
    1872             :     }
    1873             : 
    1874         455 :     MOZ_RELEASE_ASSERT(aTask.isInList());
    1875         455 :     aTask.remove();
    1876             : 
    1877         455 :     if (!IsAlwaysDeferred(msg)) {
    1878          11 :         mMaybeDeferredPendingCount--;
    1879             :     }
    1880             : 
    1881         455 :     if (IsOnCxxStack() && msg.is_interrupt() && msg.is_reply()) {
    1882             :         // We probably just received a reply in a nested loop for an
    1883             :         // Interrupt call sent before entering that loop.
    1884           0 :         mOutOfTurnReplies[msg.seqno()] = Move(msg);
    1885           0 :         return;
    1886             :     }
    1887             : 
    1888         455 :     DispatchMessage(Move(msg));
    1889             : }
    1890             : 
    1891        7748 : NS_IMPL_ISUPPORTS_INHERITED(MessageChannel::MessageTask, CancelableRunnable, nsIRunnablePriority)
    1892             : 
    1893         473 : MessageChannel::MessageTask::MessageTask(MessageChannel* aChannel, Message&& aMessage)
    1894             :   : CancelableRunnable(StringFromIPCMessageType(aMessage.type()))
    1895             :   , mChannel(aChannel)
    1896         473 :   , mMessage(Move(aMessage))
    1897         946 :   , mScheduled(false)
    1898             : {
    1899         473 : }
    1900             : 
    1901             : nsresult
    1902         456 : MessageChannel::MessageTask::Run()
    1903             : {
    1904         456 :     if (!mChannel) {
    1905           0 :         return NS_OK;
    1906             :     }
    1907             : 
    1908         456 :     mChannel->AssertWorkerThread();
    1909         456 :     mChannel->mMonitor->AssertNotCurrentThreadOwns();
    1910             : 
    1911         909 :     MonitorAutoLock lock(*mChannel->mMonitor);
    1912             : 
    1913             :     // In case we choose not to run this message, we may need to be able to Post
    1914             :     // it again.
    1915         456 :     mScheduled = false;
    1916             : 
    1917         456 :     if (!isInList()) {
    1918           1 :         return NS_OK;
    1919             :     }
    1920             : 
    1921         455 :     mChannel->RunMessage(*this);
    1922         452 :     return NS_OK;
    1923             : }
    1924             : 
    1925             : // Warning: This method removes the receiver from whatever list it might be in.
    1926             : nsresult
    1927           0 : MessageChannel::MessageTask::Cancel()
    1928             : {
    1929           0 :     if (!mChannel) {
    1930           0 :         return NS_OK;
    1931             :     }
    1932             : 
    1933           0 :     mChannel->AssertWorkerThread();
    1934           0 :     mChannel->mMonitor->AssertNotCurrentThreadOwns();
    1935             : 
    1936           0 :     MonitorAutoLock lock(*mChannel->mMonitor);
    1937             : 
    1938           0 :     if (!isInList()) {
    1939           0 :         return NS_OK;
    1940             :     }
    1941           0 :     remove();
    1942             : 
    1943           0 :     if (!IsAlwaysDeferred(Msg())) {
    1944           0 :         mChannel->mMaybeDeferredPendingCount--;
    1945             :     }
    1946             : 
    1947           0 :     return NS_OK;
    1948             : }
    1949             : 
    1950             : void
    1951         473 : MessageChannel::MessageTask::Post()
    1952             : {
    1953         473 :     MOZ_RELEASE_ASSERT(!mScheduled);
    1954         473 :     MOZ_RELEASE_ASSERT(isInList());
    1955             : 
    1956         473 :     mScheduled = true;
    1957             : 
    1958         946 :     RefPtr<MessageTask> self = this;
    1959             :     nsCOMPtr<nsIEventTarget> eventTarget =
    1960         946 :         mChannel->mListener->GetMessageEventTarget(mMessage);
    1961             : 
    1962         473 :     if (eventTarget) {
    1963          80 :         eventTarget->Dispatch(self.forget(), NS_DISPATCH_NORMAL);
    1964             :     } else {
    1965         393 :         mChannel->mWorkerLoop->PostTask(self.forget());
    1966             :     }
    1967         473 : }
    1968             : 
    1969             : void
    1970           0 : MessageChannel::MessageTask::Clear()
    1971             : {
    1972           0 :     mChannel->AssertWorkerThread();
    1973             : 
    1974           0 :     mChannel = nullptr;
    1975           0 : }
    1976             : 
    1977             : NS_IMETHODIMP
    1978         251 : MessageChannel::MessageTask::GetPriority(uint32_t* aPriority)
    1979             : {
    1980         251 :   *aPriority = mMessage.priority() == Message::HIGH_PRIORITY ?
    1981             :                PRIORITY_HIGH : PRIORITY_NORMAL;
    1982         251 :   return NS_OK;
    1983             : }
    1984             : 
    1985             : void
    1986         455 : MessageChannel::DispatchMessage(Message &&aMsg)
    1987             : {
    1988         455 :     AssertWorkerThread();
    1989         455 :     mMonitor->AssertCurrentThreadOwns();
    1990             : 
    1991         907 :     Maybe<AutoNoJSAPI> nojsapi;
    1992         455 :     if (ScriptSettingsInitialized() && NS_IsMainThread())
    1993         281 :         nojsapi.emplace();
    1994             : 
    1995         907 :     nsAutoPtr<Message> reply;
    1996             : 
    1997         455 :     IPC_LOG("DispatchMessage: seqno=%d, xid=%d", aMsg.seqno(), aMsg.transaction_id());
    1998             : 
    1999             :     {
    2000         907 :         AutoEnterTransaction transaction(this, aMsg);
    2001             : 
    2002         455 :         int id = aMsg.transaction_id();
    2003         455 :         MOZ_RELEASE_ASSERT(!aMsg.is_sync() || id == transaction.TransactionID());
    2004             : 
    2005             :         {
    2006             : #ifdef MOZ_TASK_TRACER
    2007             :             Message::AutoTaskTracerRun tasktracerRun(aMsg);
    2008             : #endif
    2009         907 :             MonitorAutoUnlock unlock(*mMonitor);
    2010         907 :             CxxStackFrame frame(*this, IN_MESSAGE, &aMsg);
    2011             : 
    2012         455 :             mListener->ArtificialSleep();
    2013             : 
    2014         455 :             if (aMsg.is_sync())
    2015           3 :                 DispatchSyncMessage(aMsg, *getter_Transfers(reply));
    2016         452 :             else if (aMsg.is_interrupt())
    2017           0 :                 DispatchInterruptMessage(Move(aMsg), 0);
    2018             :             else
    2019         452 :                 DispatchAsyncMessage(aMsg);
    2020             : 
    2021         452 :             mListener->ArtificialSleep();
    2022             :         }
    2023             : 
    2024         452 :         if (reply && transaction.IsCanceled()) {
    2025             :             // The transaction has been canceled. Don't send a reply.
    2026           0 :             IPC_LOG("Nulling out reply due to cancellation, seqno=%d, xid=%d", aMsg.seqno(), id);
    2027           0 :             reply = nullptr;
    2028             :         }
    2029             :     }
    2030             : 
    2031         452 :     if (reply && ChannelConnected == mChannelState) {
    2032           3 :         IPC_LOG("Sending reply seqno=%d, xid=%d", aMsg.seqno(), aMsg.transaction_id());
    2033           3 :         mLink->SendMessage(reply.forget());
    2034             :     }
    2035         452 : }
    2036             : 
    2037             : void
    2038           3 : MessageChannel::DispatchSyncMessage(const Message& aMsg, Message*& aReply)
    2039             : {
    2040           3 :     AssertWorkerThread();
    2041             : 
    2042           3 :     mozilla::TimeStamp start = TimeStamp::Now();
    2043             : 
    2044           3 :     int nestedLevel = aMsg.nested_level();
    2045             : 
    2046           3 :     MOZ_RELEASE_ASSERT(nestedLevel == IPC::Message::NOT_NESTED || NS_IsMainThread());
    2047             : #ifdef MOZ_TASK_TRACER
    2048             :     AutoScopedLabel autolabel("sync message %s", aMsg.name());
    2049             : #endif
    2050             : 
    2051             :     MessageChannel* dummy;
    2052           3 :     MessageChannel*& blockingVar = mSide == ChildSide && NS_IsMainThread() ? gParentProcessBlocker : dummy;
    2053             : 
    2054             :     Result rv;
    2055             :     {
    2056           6 :         AutoSetValue<MessageChannel*> blocked(blockingVar, this);
    2057           3 :         rv = mListener->OnMessageReceived(aMsg, aReply);
    2058             :     }
    2059             : 
    2060           3 :     uint32_t latencyMs = round((TimeStamp::Now() - start).ToMilliseconds());
    2061           3 :     if (latencyMs >= kMinTelemetrySyncIPCLatencyMs) {
    2062             :         Telemetry::Accumulate(Telemetry::IPC_SYNC_RECEIVE_MS,
    2063           2 :                               nsDependentCString(aMsg.name()),
    2064           1 :                               latencyMs);
    2065             :     }
    2066             : 
    2067           3 :     if (!MaybeHandleError(rv, aMsg, "DispatchSyncMessage")) {
    2068           0 :         aReply = new Message();
    2069           0 :         aReply->set_sync();
    2070           0 :         aReply->set_nested_level(aMsg.nested_level());
    2071           0 :         aReply->set_reply();
    2072           0 :         aReply->set_reply_error();
    2073             :     }
    2074           3 :     aReply->set_seqno(aMsg.seqno());
    2075           3 :     aReply->set_transaction_id(aMsg.transaction_id());
    2076           3 : }
    2077             : 
    2078             : void
    2079         452 : MessageChannel::DispatchAsyncMessage(const Message& aMsg)
    2080             : {
    2081         452 :     AssertWorkerThread();
    2082         452 :     MOZ_RELEASE_ASSERT(!aMsg.is_interrupt() && !aMsg.is_sync());
    2083             : 
    2084         452 :     if (aMsg.routing_id() == MSG_ROUTING_NONE) {
    2085           0 :         MOZ_CRASH("unhandled special message!");
    2086             :     }
    2087             : 
    2088             :     Result rv;
    2089             :     {
    2090         452 :         int nestedLevel = aMsg.nested_level();
    2091         901 :         AutoSetValue<bool> async(mDispatchingAsyncMessage, true);
    2092         901 :         AutoSetValue<int> nestedLevelSet(mDispatchingAsyncMessageNestedLevel, nestedLevel);
    2093         452 :         rv = mListener->OnMessageReceived(aMsg);
    2094             :     }
    2095         449 :     MaybeHandleError(rv, aMsg, "DispatchAsyncMessage");
    2096         449 : }
    2097             : 
    2098             : void
    2099           0 : MessageChannel::DispatchInterruptMessage(Message&& aMsg, size_t stackDepth)
    2100             : {
    2101           0 :     AssertWorkerThread();
    2102           0 :     mMonitor->AssertNotCurrentThreadOwns();
    2103             : 
    2104           0 :     IPC_ASSERT(aMsg.is_interrupt() && !aMsg.is_reply(), "wrong message type");
    2105             : 
    2106           0 :     if (ShouldDeferInterruptMessage(aMsg, stackDepth)) {
    2107             :         // We now know the other side's stack has one more frame
    2108             :         // than we thought.
    2109           0 :         ++mRemoteStackDepthGuess; // decremented in MaybeProcessDeferred()
    2110           0 :         mDeferred.push(Move(aMsg));
    2111           0 :         return;
    2112             :     }
    2113             : 
    2114             :     // If we "lost" a race and need to process the other side's in-call, we
    2115             :     // don't need to fix up the mRemoteStackDepthGuess here, because we're just
    2116             :     // about to increment it, which will make it correct again.
    2117             : 
    2118             : #ifdef OS_WIN
    2119             :     SyncStackFrame frame(this, true);
    2120             : #endif
    2121             : 
    2122           0 :     nsAutoPtr<Message> reply;
    2123             : 
    2124           0 :     ++mRemoteStackDepthGuess;
    2125           0 :     Result rv = mListener->OnCallReceived(aMsg, *getter_Transfers(reply));
    2126           0 :     --mRemoteStackDepthGuess;
    2127             : 
    2128           0 :     if (!MaybeHandleError(rv, aMsg, "DispatchInterruptMessage")) {
    2129           0 :         reply = new Message();
    2130           0 :         reply->set_interrupt();
    2131           0 :         reply->set_reply();
    2132           0 :         reply->set_reply_error();
    2133             :     }
    2134           0 :     reply->set_seqno(aMsg.seqno());
    2135             : 
    2136           0 :     MonitorAutoLock lock(*mMonitor);
    2137           0 :     if (ChannelConnected == mChannelState) {
    2138           0 :         mLink->SendMessage(reply.forget());
    2139             :     }
    2140             : }
    2141             : 
    2142             : bool
    2143           0 : MessageChannel::ShouldDeferInterruptMessage(const Message& aMsg, size_t aStackDepth)
    2144             : {
    2145           0 :     AssertWorkerThread();
    2146             : 
    2147             :     // We may or may not own the lock in this function, so don't access any
    2148             :     // channel state.
    2149             : 
    2150           0 :     IPC_ASSERT(aMsg.is_interrupt() && !aMsg.is_reply(), "wrong message type");
    2151             : 
    2152             :     // Race detection: see the long comment near mRemoteStackDepthGuess in
    2153             :     // MessageChannel.h. "Remote" stack depth means our side, and "local" means
    2154             :     // the other side.
    2155           0 :     if (aMsg.interrupt_remote_stack_depth_guess() == RemoteViewOfStackDepth(aStackDepth)) {
    2156           0 :         return false;
    2157             :     }
    2158             : 
    2159             :     // Interrupt in-calls have raced. The winner, if there is one, gets to defer
    2160             :     // processing of the other side's in-call.
    2161             :     bool defer;
    2162             :     const char* winner;
    2163             :     const MessageInfo parentMsgInfo =
    2164           0 :         (mSide == ChildSide) ? MessageInfo(aMsg) : mInterruptStack.top();
    2165             :     const MessageInfo childMsgInfo =
    2166           0 :         (mSide == ChildSide) ? mInterruptStack.top() : MessageInfo(aMsg);
    2167           0 :     switch (mListener->MediateInterruptRace(parentMsgInfo, childMsgInfo))
    2168             :     {
    2169             :         case RIPChildWins:
    2170           0 :             winner = "child";
    2171           0 :             defer = (mSide == ChildSide);
    2172           0 :             break;
    2173             :         case RIPParentWins:
    2174           0 :             winner = "parent";
    2175           0 :             defer = (mSide != ChildSide);
    2176           0 :             break;
    2177             :         case RIPError:
    2178           0 :             MOZ_CRASH("NYI: 'Error' Interrupt race policy");
    2179             :         default:
    2180           0 :             MOZ_CRASH("not reached");
    2181             :     }
    2182             : 
    2183           0 :     IPC_LOG("race in %s: %s won",
    2184             :             (mSide == ChildSide) ? "child" : "parent",
    2185             :             winner);
    2186             : 
    2187           0 :     return defer;
    2188             : }
    2189             : 
    2190             : void
    2191           0 : MessageChannel::MaybeUndeferIncall()
    2192             : {
    2193           0 :     AssertWorkerThread();
    2194           0 :     mMonitor->AssertCurrentThreadOwns();
    2195             : 
    2196           0 :     if (mDeferred.empty())
    2197           0 :         return;
    2198             : 
    2199           0 :     size_t stackDepth = InterruptStackDepth();
    2200             : 
    2201           0 :     Message& deferred = mDeferred.top();
    2202             : 
    2203             :     // the other side can only *under*-estimate our actual stack depth
    2204           0 :     IPC_ASSERT(deferred.interrupt_remote_stack_depth_guess() <= stackDepth,
    2205             :                "fatal logic error");
    2206             : 
    2207           0 :     if (ShouldDeferInterruptMessage(deferred, stackDepth)) {
    2208           0 :         return;
    2209             :     }
    2210             : 
    2211             :     // maybe time to process this message
    2212           0 :     Message call(Move(deferred));
    2213           0 :     mDeferred.pop();
    2214             : 
    2215             :     // fix up fudge factor we added to account for race
    2216           0 :     IPC_ASSERT(0 < mRemoteStackDepthGuess, "fatal logic error");
    2217           0 :     --mRemoteStackDepthGuess;
    2218             : 
    2219           0 :     MOZ_RELEASE_ASSERT(call.nested_level() == IPC::Message::NOT_NESTED);
    2220           0 :     RefPtr<MessageTask> task = new MessageTask(this, Move(call));
    2221           0 :     mPending.insertBack(task);
    2222           0 :     MOZ_ASSERT(IsAlwaysDeferred(task->Msg()));
    2223           0 :     task->Post();
    2224             : }
    2225             : 
    2226             : void
    2227         845 : MessageChannel::EnteredCxxStack()
    2228             : {
    2229         845 :     mListener->EnteredCxxStack();
    2230         845 : }
    2231             : 
    2232             : void
    2233         842 : MessageChannel::ExitedCxxStack()
    2234             : {
    2235         842 :     mListener->ExitedCxxStack();
    2236         842 :     if (mSawInterruptOutMsg) {
    2237           0 :         MonitorAutoLock lock(*mMonitor);
    2238             :         // see long comment in OnMaybeDequeueOne()
    2239           0 :         EnqueuePendingMessages();
    2240           0 :         mSawInterruptOutMsg = false;
    2241             :     }
    2242         842 : }
    2243             : 
    2244             : void
    2245           0 : MessageChannel::EnteredCall()
    2246             : {
    2247           0 :     mListener->EnteredCall();
    2248           0 : }
    2249             : 
    2250             : void
    2251           0 : MessageChannel::ExitedCall()
    2252             : {
    2253           0 :     mListener->ExitedCall();
    2254           0 : }
    2255             : 
    2256             : void
    2257           3 : MessageChannel::EnteredSyncSend()
    2258             : {
    2259           3 :     mListener->OnEnteredSyncSend();
    2260           3 : }
    2261             : 
    2262             : void
    2263           3 : MessageChannel::ExitedSyncSend()
    2264             : {
    2265           3 :     mListener->OnExitedSyncSend();
    2266           3 : }
    2267             : 
    2268             : void
    2269           0 : MessageChannel::EnqueuePendingMessages()
    2270             : {
    2271           0 :     AssertWorkerThread();
    2272           0 :     mMonitor->AssertCurrentThreadOwns();
    2273             : 
    2274           0 :     MaybeUndeferIncall();
    2275             : 
    2276             :     // XXX performance tuning knob: could process all or k pending
    2277             :     // messages here, rather than enqueuing for later processing
    2278             : 
    2279           0 :     RepostAllMessages();
    2280           0 : }
    2281             : 
    2282             : static inline bool
    2283           3 : IsTimeoutExpired(PRIntervalTime aStart, PRIntervalTime aTimeout)
    2284             : {
    2285           3 :     return (aTimeout != PR_INTERVAL_NO_TIMEOUT) &&
    2286           3 :            (aTimeout <= (PR_IntervalNow() - aStart));
    2287             : }
    2288             : 
    2289             : bool
    2290           3 : MessageChannel::WaitResponse(bool aWaitTimedOut)
    2291             : {
    2292           3 :     if (aWaitTimedOut) {
    2293           0 :         if (mInTimeoutSecondHalf) {
    2294             :             // We've really timed out this time.
    2295           0 :             return false;
    2296             :         }
    2297             :         // Try a second time.
    2298           0 :         mInTimeoutSecondHalf = true;
    2299             :     } else {
    2300           3 :         mInTimeoutSecondHalf = false;
    2301             :     }
    2302           3 :     return true;
    2303             : }
    2304             : 
    2305             : #ifndef OS_WIN
    2306             : bool
    2307           3 : MessageChannel::WaitForSyncNotify(bool /* aHandleWindowsMessages */)
    2308             : {
    2309             : #ifdef DEBUG
    2310             :     // WARNING: We don't release the lock here. We can't because the link thread
    2311             :     // could signal at this time and we would miss it. Instead we require
    2312             :     // ArtificialTimeout() to be extremely simple.
    2313           3 :     if (mListener->ArtificialTimeout()) {
    2314           0 :         return false;
    2315             :     }
    2316             : #endif
    2317             : 
    2318           3 :     PRIntervalTime timeout = (kNoTimeout == mTimeoutMs) ?
    2319             :                              PR_INTERVAL_NO_TIMEOUT :
    2320           3 :                              PR_MillisecondsToInterval(mTimeoutMs);
    2321             :     // XXX could optimize away this syscall for "no timeout" case if desired
    2322           3 :     PRIntervalTime waitStart = PR_IntervalNow();
    2323             : 
    2324           3 :     mMonitor->Wait(timeout);
    2325             : 
    2326             :     // If the timeout didn't expire, we know we received an event. The
    2327             :     // converse is not true.
    2328           3 :     return WaitResponse(IsTimeoutExpired(waitStart, timeout));
    2329             : }
    2330             : 
    2331             : bool
    2332           0 : MessageChannel::WaitForInterruptNotify()
    2333             : {
    2334           0 :     return WaitForSyncNotify(true);
    2335             : }
    2336             : 
    2337             : void
    2338           3 : MessageChannel::NotifyWorkerThread()
    2339             : {
    2340           3 :     mMonitor->Notify();
    2341           3 : }
    2342             : #endif
    2343             : 
    2344             : bool
    2345           0 : MessageChannel::ShouldContinueFromTimeout()
    2346             : {
    2347           0 :     AssertWorkerThread();
    2348           0 :     mMonitor->AssertCurrentThreadOwns();
    2349             : 
    2350             :     bool cont;
    2351             :     {
    2352           0 :         MonitorAutoUnlock unlock(*mMonitor);
    2353           0 :         cont = mListener->ShouldContinueFromReplyTimeout();
    2354           0 :         mListener->ArtificialSleep();
    2355             :     }
    2356             : 
    2357             :     static enum { UNKNOWN, NOT_DEBUGGING, DEBUGGING } sDebuggingChildren = UNKNOWN;
    2358             : 
    2359           0 :     if (sDebuggingChildren == UNKNOWN) {
    2360           0 :         sDebuggingChildren = getenv("MOZ_DEBUG_CHILD_PROCESS") ||
    2361           0 :                              getenv("MOZ_DEBUG_CHILD_PAUSE")
    2362           0 :                                ? DEBUGGING
    2363             :                                : NOT_DEBUGGING;
    2364             :     }
    2365           0 :     if (sDebuggingChildren == DEBUGGING) {
    2366           0 :         return true;
    2367             :     }
    2368             : 
    2369           0 :     return cont;
    2370             : }
    2371             : 
    2372             : void
    2373           2 : MessageChannel::SetReplyTimeoutMs(int32_t aTimeoutMs)
    2374             : {
    2375             :     // Set channel timeout value. Since this is broken up into
    2376             :     // two period, the minimum timeout value is 2ms.
    2377           2 :     AssertWorkerThread();
    2378           2 :     mTimeoutMs = (aTimeoutMs <= 0)
    2379           4 :                  ? kNoTimeout
    2380           2 :                  : (int32_t)ceil((double)aTimeoutMs / 2.0);
    2381           2 : }
    2382             : 
    2383             : void
    2384          21 : MessageChannel::OnChannelConnected(int32_t peer_id)
    2385             : {
    2386          21 :     MOZ_RELEASE_ASSERT(!mPeerPidSet);
    2387          21 :     mPeerPidSet = true;
    2388          21 :     mPeerPid = peer_id;
    2389          42 :     RefPtr<CancelableRunnable> task = mOnChannelConnectedTask;
    2390          21 :     mWorkerLoop->PostTask(task.forget());
    2391          21 : }
    2392             : 
    2393             : void
    2394          19 : MessageChannel::DispatchOnChannelConnected()
    2395             : {
    2396          19 :     AssertWorkerThread();
    2397          19 :     MOZ_RELEASE_ASSERT(mPeerPidSet);
    2398          19 :     mListener->OnChannelConnected(mPeerPid);
    2399          19 : }
    2400             : 
    2401             : void
    2402           0 : MessageChannel::ReportMessageRouteError(const char* channelName) const
    2403             : {
    2404           0 :     PrintErrorMessage(mSide, channelName, "Need a route");
    2405           0 :     mListener->ProcessingError(MsgRouteError, "MsgRouteError");
    2406           0 : }
    2407             : 
    2408             : void
    2409           0 : MessageChannel::ReportConnectionError(const char* aChannelName, Message* aMsg) const
    2410             : {
    2411           0 :     AssertWorkerThread();
    2412           0 :     mMonitor->AssertCurrentThreadOwns();
    2413             : 
    2414           0 :     const char* errorMsg = nullptr;
    2415           0 :     switch (mChannelState) {
    2416             :       case ChannelClosed:
    2417           0 :         errorMsg = "Closed channel: cannot send/recv";
    2418           0 :         break;
    2419             :       case ChannelOpening:
    2420           0 :         errorMsg = "Opening channel: not yet ready for send/recv";
    2421           0 :         break;
    2422             :       case ChannelTimeout:
    2423           0 :         errorMsg = "Channel timeout: cannot send/recv";
    2424           0 :         break;
    2425             :       case ChannelClosing:
    2426           0 :         errorMsg = "Channel closing: too late to send/recv, messages will be lost";
    2427           0 :         break;
    2428             :       case ChannelError:
    2429           0 :         errorMsg = "Channel error: cannot send/recv";
    2430           0 :         break;
    2431             : 
    2432             :       default:
    2433           0 :         MOZ_CRASH("unreached");
    2434             :     }
    2435             : 
    2436           0 :     if (aMsg) {
    2437             :         char reason[512];
    2438           0 :         SprintfLiteral(reason,"(msgtype=0x%X,name=%s) %s",
    2439           0 :                        aMsg->type(), aMsg->name(), errorMsg);
    2440             : 
    2441           0 :         PrintErrorMessage(mSide, aChannelName, reason);
    2442             :     } else {
    2443           0 :         PrintErrorMessage(mSide, aChannelName, errorMsg);
    2444             :     }
    2445             : 
    2446           0 :     MonitorAutoUnlock unlock(*mMonitor);
    2447           0 :     mListener->ProcessingError(MsgDropped, errorMsg);
    2448           0 : }
    2449             : 
    2450             : bool
    2451         452 : MessageChannel::MaybeHandleError(Result code, const Message& aMsg, const char* channelName)
    2452             : {
    2453         452 :     if (MsgProcessed == code)
    2454         452 :         return true;
    2455             : 
    2456           0 :     const char* errorMsg = nullptr;
    2457           0 :     switch (code) {
    2458             :       case MsgNotKnown:
    2459           0 :         errorMsg = "Unknown message: not processed";
    2460           0 :         break;
    2461             :       case MsgNotAllowed:
    2462           0 :         errorMsg = "Message not allowed: cannot be sent/recvd in this state";
    2463           0 :         break;
    2464             :       case MsgPayloadError:
    2465           0 :         errorMsg = "Payload error: message could not be deserialized";
    2466           0 :         break;
    2467             :       case MsgProcessingError:
    2468           0 :         errorMsg = "Processing error: message was deserialized, but the handler returned false (indicating failure)";
    2469           0 :         break;
    2470             :       case MsgRouteError:
    2471           0 :         errorMsg = "Route error: message sent to unknown actor ID";
    2472           0 :         break;
    2473             :       case MsgValueError:
    2474           0 :         errorMsg = "Value error: message was deserialized, but contained an illegal value";
    2475           0 :         break;
    2476             : 
    2477             :     default:
    2478           0 :         MOZ_CRASH("unknown Result code");
    2479             :         return false;
    2480             :     }
    2481             : 
    2482             :     char reason[512];
    2483           0 :     const char* msgname = StringFromIPCMessageType(aMsg.type());
    2484           0 :     if (msgname[0] == '?') {
    2485           0 :         SprintfLiteral(reason,"(msgtype=0x%X) %s", aMsg.type(), errorMsg);
    2486             :     } else {
    2487           0 :         SprintfLiteral(reason,"%s %s", msgname, errorMsg);
    2488             :     }
    2489             : 
    2490           0 :     PrintErrorMessage(mSide, channelName, reason);
    2491             : 
    2492             :     // Error handled in mozilla::ipc::IPCResult.
    2493           0 :     if (code == MsgProcessingError) {
    2494           0 :         return false;
    2495             :     }
    2496             : 
    2497           0 :     mListener->ProcessingError(code, reason);
    2498             : 
    2499           0 :     return false;
    2500             : }
    2501             : 
    2502             : void
    2503           0 : MessageChannel::OnChannelErrorFromLink()
    2504             : {
    2505           0 :     AssertLinkThread();
    2506           0 :     mMonitor->AssertCurrentThreadOwns();
    2507             : 
    2508           0 :     IPC_LOG("OnChannelErrorFromLink");
    2509             : 
    2510           0 :     if (InterruptStackDepth() > 0)
    2511           0 :         NotifyWorkerThread();
    2512             : 
    2513           0 :     if (AwaitingSyncReply() || AwaitingIncomingMessage())
    2514           0 :         NotifyWorkerThread();
    2515             : 
    2516           0 :     if (ChannelClosing != mChannelState) {
    2517           0 :         if (mAbortOnError) {
    2518           0 :             MOZ_CRASH("Aborting on channel error.");
    2519             :         }
    2520           0 :         mChannelState = ChannelError;
    2521           0 :         mMonitor->Notify();
    2522             :     }
    2523             : 
    2524           0 :     PostErrorNotifyTask();
    2525           0 : }
    2526             : 
    2527             : void
    2528           0 : MessageChannel::NotifyMaybeChannelError()
    2529             : {
    2530           0 :     mMonitor->AssertNotCurrentThreadOwns();
    2531             : 
    2532             :     // TODO sort out Close() on this side racing with Close() on the other side
    2533           0 :     if (ChannelClosing == mChannelState) {
    2534             :         // the channel closed, but we received a "Goodbye" message warning us
    2535             :         // about it. no worries
    2536           0 :         mChannelState = ChannelClosed;
    2537           0 :         NotifyChannelClosed();
    2538           0 :         return;
    2539             :     }
    2540             : 
    2541           0 :     Clear();
    2542             : 
    2543             :     // Oops, error!  Let the listener know about it.
    2544           0 :     mChannelState = ChannelError;
    2545             : 
    2546             :     // IPDL assumes these notifications do not fire twice, so we do not let
    2547             :     // that happen.
    2548           0 :     if (mNotifiedChannelDone) {
    2549           0 :       return;
    2550             :     }
    2551           0 :     mNotifiedChannelDone = true;
    2552             : 
    2553             :     // After this, the channel may be deleted.  Based on the premise that
    2554             :     // mListener owns this channel, any calls back to this class that may
    2555             :     // work with mListener should still work on living objects.
    2556           0 :     mListener->OnChannelError();
    2557             : }
    2558             : 
    2559             : void
    2560           0 : MessageChannel::OnNotifyMaybeChannelError()
    2561             : {
    2562           0 :     AssertWorkerThread();
    2563           0 :     mMonitor->AssertNotCurrentThreadOwns();
    2564             : 
    2565           0 :     mChannelErrorTask = nullptr;
    2566             : 
    2567             :     // OnChannelError holds mMonitor when it posts this task and this
    2568             :     // task cannot be allowed to run until OnChannelError has
    2569             :     // exited. We enforce that order by grabbing the mutex here which
    2570             :     // should only continue once OnChannelError has completed.
    2571             :     {
    2572           0 :         MonitorAutoLock lock(*mMonitor);
    2573             :         // nothing to do here
    2574             :     }
    2575             : 
    2576           0 :     if (IsOnCxxStack()) {
    2577           0 :       mChannelErrorTask = NewNonOwningCancelableRunnableMethod(
    2578             :         "ipc::MessageChannel::OnNotifyMaybeChannelError",
    2579             :         this,
    2580           0 :         &MessageChannel::OnNotifyMaybeChannelError);
    2581           0 :       RefPtr<Runnable> task = mChannelErrorTask;
    2582             :       // 10 ms delay is completely arbitrary
    2583           0 :       mWorkerLoop->PostDelayedTask(task.forget(), 10);
    2584           0 :       return;
    2585             :     }
    2586             : 
    2587           0 :     NotifyMaybeChannelError();
    2588             : }
    2589             : 
    2590             : void
    2591           0 : MessageChannel::PostErrorNotifyTask()
    2592             : {
    2593           0 :     mMonitor->AssertCurrentThreadOwns();
    2594             : 
    2595           0 :     if (mChannelErrorTask)
    2596           0 :         return;
    2597             : 
    2598             :     // This must be the last code that runs on this thread!
    2599           0 :     mChannelErrorTask = NewNonOwningCancelableRunnableMethod(
    2600             :       "ipc::MessageChannel::OnNotifyMaybeChannelError",
    2601             :       this,
    2602           0 :       &MessageChannel::OnNotifyMaybeChannelError);
    2603           0 :     RefPtr<Runnable> task = mChannelErrorTask;
    2604           0 :     mWorkerLoop->PostTask(task.forget());
    2605             : }
    2606             : 
    2607             : // Special async message.
    2608           0 : class GoodbyeMessage : public IPC::Message
    2609             : {
    2610             : public:
    2611           0 :     GoodbyeMessage() :
    2612           0 :         IPC::Message(MSG_ROUTING_NONE, GOODBYE_MESSAGE_TYPE)
    2613             :     {
    2614           0 :     }
    2615             :     static bool Read(const Message* msg) {
    2616             :         return true;
    2617             :     }
    2618             :     void Log(const std::string& aPrefix, FILE* aOutf) const {
    2619             :         fputs("(special `Goodbye' message)", aOutf);
    2620             :     }
    2621             : };
    2622             : 
    2623             : void
    2624           0 : MessageChannel::SynchronouslyClose()
    2625             : {
    2626           0 :     AssertWorkerThread();
    2627           0 :     mMonitor->AssertCurrentThreadOwns();
    2628           0 :     mLink->SendClose();
    2629           0 :     while (ChannelClosed != mChannelState)
    2630           0 :         mMonitor->Wait();
    2631           0 : }
    2632             : 
    2633             : void
    2634           0 : MessageChannel::CloseWithError()
    2635             : {
    2636           0 :     AssertWorkerThread();
    2637             : 
    2638           0 :     MonitorAutoLock lock(*mMonitor);
    2639           0 :     if (ChannelConnected != mChannelState) {
    2640           0 :         return;
    2641             :     }
    2642           0 :     SynchronouslyClose();
    2643           0 :     mChannelState = ChannelError;
    2644           0 :     PostErrorNotifyTask();
    2645             : }
    2646             : 
    2647             : void
    2648           0 : MessageChannel::CloseWithTimeout()
    2649             : {
    2650           0 :     AssertWorkerThread();
    2651             : 
    2652           0 :     MonitorAutoLock lock(*mMonitor);
    2653           0 :     if (ChannelConnected != mChannelState) {
    2654           0 :         return;
    2655             :     }
    2656           0 :     SynchronouslyClose();
    2657           0 :     mChannelState = ChannelTimeout;
    2658             : }
    2659             : 
    2660             : void
    2661           0 : MessageChannel::Close()
    2662             : {
    2663           0 :     AssertWorkerThread();
    2664             : 
    2665             :     {
    2666           0 :         MonitorAutoLock lock(*mMonitor);
    2667             : 
    2668           0 :         if (ChannelError == mChannelState || ChannelTimeout == mChannelState) {
    2669             :             // See bug 538586: if the listener gets deleted while the
    2670             :             // IO thread's NotifyChannelError event is still enqueued
    2671             :             // and subsequently deletes us, then the error event will
    2672             :             // also be deleted and the listener will never be notified
    2673             :             // of the channel error.
    2674           0 :             if (mListener) {
    2675           0 :                 MonitorAutoUnlock unlock(*mMonitor);
    2676           0 :                 NotifyMaybeChannelError();
    2677             :             }
    2678           0 :             return;
    2679             :         }
    2680             : 
    2681           0 :         if (ChannelOpening == mChannelState) {
    2682             :             // SynchronouslyClose() waits for an ack from the other side, so
    2683             :             // the opening sequence should complete before this returns.
    2684           0 :             SynchronouslyClose();
    2685           0 :             mChannelState = ChannelError;
    2686           0 :             NotifyMaybeChannelError();
    2687           0 :             return;
    2688             :         }
    2689             : 
    2690           0 :         if (ChannelClosed == mChannelState) {
    2691             :             // XXX be strict about this until there's a compelling reason
    2692             :             // to relax
    2693           0 :             MOZ_CRASH("Close() called on closed channel!");
    2694             :         }
    2695             : 
    2696             :         // Notify the other side that we're about to close our socket. If we've
    2697             :         // already received a Goodbye from the other side (and our state is
    2698             :         // ChannelClosing), there's no reason to send one.
    2699           0 :         if (ChannelConnected == mChannelState) {
    2700           0 :           mLink->SendMessage(new GoodbyeMessage());
    2701             :         }
    2702           0 :         SynchronouslyClose();
    2703             :     }
    2704             : 
    2705           0 :     NotifyChannelClosed();
    2706             : }
    2707             : 
    2708             : void
    2709           0 : MessageChannel::NotifyChannelClosed()
    2710             : {
    2711           0 :     mMonitor->AssertNotCurrentThreadOwns();
    2712             : 
    2713           0 :     if (ChannelClosed != mChannelState)
    2714           0 :         MOZ_CRASH("channel should have been closed!");
    2715             : 
    2716           0 :     Clear();
    2717             : 
    2718             :     // IPDL assumes these notifications do not fire twice, so we do not let
    2719             :     // that happen.
    2720           0 :     if (mNotifiedChannelDone) {
    2721           0 :       return;
    2722             :     }
    2723           0 :     mNotifiedChannelDone = true;
    2724             : 
    2725             :     // OK, the IO thread just closed the channel normally.  Let the
    2726             :     // listener know about it. After this point the channel may be
    2727             :     // deleted.
    2728           0 :     mListener->OnChannelClose();
    2729             : }
    2730             : 
    2731             : void
    2732           0 : MessageChannel::DebugAbort(const char* file, int line, const char* cond,
    2733             :                            const char* why,
    2734             :                            bool reply)
    2735             : {
    2736           0 :     printf_stderr("###!!! [MessageChannel][%s][%s:%d] "
    2737             :                   "Assertion (%s) failed.  %s %s\n",
    2738           0 :                   mSide == ChildSide ? "Child" : "Parent",
    2739             :                   file, line, cond,
    2740             :                   why,
    2741           0 :                   reply ? "(reply)" : "");
    2742             :     // technically we need the mutex for this, but we're dying anyway
    2743           0 :     DumpInterruptStack("  ");
    2744           0 :     printf_stderr("  remote Interrupt stack guess: %" PRIuSIZE "\n",
    2745           0 :                   mRemoteStackDepthGuess);
    2746           0 :     printf_stderr("  deferred stack size: %" PRIuSIZE "\n",
    2747           0 :                   mDeferred.size());
    2748           0 :     printf_stderr("  out-of-turn Interrupt replies stack size: %" PRIuSIZE "\n",
    2749           0 :                   mOutOfTurnReplies.size());
    2750             : 
    2751           0 :     MessageQueue pending = Move(mPending);
    2752           0 :     while (!pending.isEmpty()) {
    2753           0 :         printf_stderr("    [ %s%s ]\n",
    2754           0 :                       pending.getFirst()->Msg().is_interrupt() ? "intr" :
    2755           0 :                       (pending.getFirst()->Msg().is_sync() ? "sync" : "async"),
    2756           0 :                       pending.getFirst()->Msg().is_reply() ? "reply" : "");
    2757           0 :         pending.popFirst();
    2758             :     }
    2759             : 
    2760           0 :     NS_RUNTIMEABORT(why);
    2761           0 : }
    2762             : 
    2763             : void
    2764           0 : MessageChannel::DumpInterruptStack(const char* const pfx) const
    2765             : {
    2766           0 :     NS_WARNING_ASSERTION(
    2767             :       MessageLoop::current() != mWorkerLoop,
    2768             :       "The worker thread had better be paused in a debugger!");
    2769             : 
    2770           0 :     printf_stderr("%sMessageChannel 'backtrace':\n", pfx);
    2771             : 
    2772             :     // print a python-style backtrace, first frame to last
    2773           0 :     for (uint32_t i = 0; i < mCxxStackFrames.length(); ++i) {
    2774             :         int32_t id;
    2775             :         const char* dir;
    2776             :         const char* sems;
    2777             :         const char* name;
    2778           0 :         mCxxStackFrames[i].Describe(&id, &dir, &sems, &name);
    2779             : 
    2780             :         printf_stderr("%s[(%u) %s %s %s(actor=%d) ]\n", pfx,
    2781           0 :                       i, dir, sems, name, id);
    2782             :     }
    2783           0 : }
    2784             : 
    2785             : int32_t
    2786           0 : MessageChannel::GetTopmostMessageRoutingId() const
    2787             : {
    2788           0 :     MOZ_RELEASE_ASSERT(MessageLoop::current() == mWorkerLoop);
    2789           0 :     if (mCxxStackFrames.empty()) {
    2790           0 :         return MSG_ROUTING_NONE;
    2791             :     }
    2792           0 :     const InterruptFrame& frame = mCxxStackFrames.back();
    2793           0 :     return frame.GetRoutingId();
    2794             : }
    2795             : 
    2796             : void
    2797           0 : MessageChannel::EndTimeout()
    2798             : {
    2799           0 :     mMonitor->AssertCurrentThreadOwns();
    2800             : 
    2801           0 :     IPC_LOG("Ending timeout of seqno=%d", mTimedOutMessageSeqno);
    2802           0 :     mTimedOutMessageSeqno = 0;
    2803           0 :     mTimedOutMessageNestedLevel = 0;
    2804             : 
    2805           0 :     RepostAllMessages();
    2806           0 : }
    2807             : 
    2808             : void
    2809           0 : MessageChannel::RepostAllMessages()
    2810             : {
    2811           0 :     bool needRepost = false;
    2812           0 :     for (MessageTask* task : mPending) {
    2813           0 :         if (!task->IsScheduled()) {
    2814           0 :             needRepost = true;
    2815             :         }
    2816             :     }
    2817           0 :     if (!needRepost) {
    2818             :         // If everything is already scheduled to run, do nothing.
    2819           0 :         return;
    2820             :     }
    2821             : 
    2822             :     // In some cases we may have deferred dispatch of some messages in the
    2823             :     // queue. Now we want to run them again. However, we can't just re-post
    2824             :     // those messages since the messages after them in mPending would then be
    2825             :     // before them in the event queue. So instead we cancel everything and
    2826             :     // re-post all messages in the correct order.
    2827           0 :     MessageQueue queue = Move(mPending);
    2828           0 :     while (RefPtr<MessageTask> task = queue.popFirst()) {
    2829           0 :         RefPtr<MessageTask> newTask = new MessageTask(this, Move(task->Msg()));
    2830           0 :         mPending.insertBack(newTask);
    2831           0 :         newTask->Post();
    2832           0 :     }
    2833             : 
    2834           0 :     AssertMaybeDeferredCountCorrect();
    2835             : }
    2836             : 
    2837             : void
    2838           0 : MessageChannel::CancelTransaction(int transaction)
    2839             : {
    2840           0 :     mMonitor->AssertCurrentThreadOwns();
    2841             : 
    2842             :     // When we cancel a transaction, we need to behave as if there's no longer
    2843             :     // any IPC on the stack. Anything we were dispatching or sending will get
    2844             :     // canceled. Consequently, we have to update the state variables below.
    2845             :     //
    2846             :     // We also need to ensure that when any IPC functions on the stack return,
    2847             :     // they don't reset these values using an RAII class like AutoSetValue. To
    2848             :     // avoid that, these RAII classes check if the variable they set has been
    2849             :     // tampered with (by us). If so, they don't reset the variable to the old
    2850             :     // value.
    2851             : 
    2852           0 :     IPC_LOG("CancelTransaction: xid=%d", transaction);
    2853             : 
    2854             :     // An unusual case: We timed out a transaction which the other side then
    2855             :     // cancelled. In this case we just leave the timedout state and try to
    2856             :     // forget this ever happened.
    2857           0 :     if (transaction == mTimedOutMessageSeqno) {
    2858           0 :         IPC_LOG("Cancelled timed out message %d", mTimedOutMessageSeqno);
    2859           0 :         EndTimeout();
    2860             : 
    2861             :         // Normally mCurrentTransaction == 0 here. But it can be non-zero if:
    2862             :         // 1. Parent sends NESTED_INSIDE_SYNC message H.
    2863             :         // 2. Parent times out H.
    2864             :         // 3. Child dispatches H and sends nested message H' (same transaction).
    2865             :         // 4. Parent dispatches H' and cancels.
    2866           0 :         MOZ_RELEASE_ASSERT(!mTransactionStack || mTransactionStack->TransactionID() == transaction);
    2867           0 :         if (mTransactionStack) {
    2868           0 :             mTransactionStack->Cancel();
    2869             :         }
    2870             :     } else {
    2871           0 :         MOZ_RELEASE_ASSERT(mTransactionStack->TransactionID() == transaction);
    2872           0 :         mTransactionStack->Cancel();
    2873             :     }
    2874             : 
    2875           0 :     bool foundSync = false;
    2876           0 :     for (MessageTask* p = mPending.getFirst(); p; ) {
    2877           0 :         Message &msg = p->Msg();
    2878             : 
    2879             :         // If there was a race between the parent and the child, then we may
    2880             :         // have a queued sync message. We want to drop this message from the
    2881             :         // queue since if will get cancelled along with the transaction being
    2882             :         // cancelled. This happens if the message in the queue is NESTED_INSIDE_SYNC.
    2883           0 :         if (msg.is_sync() && msg.nested_level() != IPC::Message::NOT_NESTED) {
    2884           0 :             MOZ_RELEASE_ASSERT(!foundSync);
    2885           0 :             MOZ_RELEASE_ASSERT(msg.transaction_id() != transaction);
    2886           0 :             IPC_LOG("Removing msg from queue seqno=%d xid=%d", msg.seqno(), msg.transaction_id());
    2887           0 :             foundSync = true;
    2888           0 :             if (!IsAlwaysDeferred(msg)) {
    2889           0 :                 mMaybeDeferredPendingCount--;
    2890             :             }
    2891           0 :             p = p->removeAndGetNext();
    2892           0 :             continue;
    2893             :         }
    2894             : 
    2895           0 :         p = p->getNext();
    2896             :     }
    2897             : 
    2898           0 :     AssertMaybeDeferredCountCorrect();
    2899           0 : }
    2900             : 
    2901             : bool
    2902           0 : MessageChannel::IsInTransaction() const
    2903             : {
    2904           0 :     MonitorAutoLock lock(*mMonitor);
    2905           0 :     return !!mTransactionStack;
    2906             : }
    2907             : 
    2908             : void
    2909           0 : MessageChannel::CancelCurrentTransaction()
    2910             : {
    2911           0 :     MonitorAutoLock lock(*mMonitor);
    2912           0 :     if (DispatchingSyncMessageNestedLevel() >= IPC::Message::NESTED_INSIDE_SYNC) {
    2913           0 :         if (DispatchingSyncMessageNestedLevel() == IPC::Message::NESTED_INSIDE_CPOW ||
    2914           0 :             DispatchingAsyncMessageNestedLevel() == IPC::Message::NESTED_INSIDE_CPOW)
    2915             :         {
    2916           0 :             mListener->IntentionalCrash();
    2917             :         }
    2918             : 
    2919           0 :         IPC_LOG("Cancel requested: current xid=%d", CurrentNestedInsideSyncTransaction());
    2920           0 :         MOZ_RELEASE_ASSERT(DispatchingSyncMessage());
    2921           0 :         CancelMessage *cancel = new CancelMessage(CurrentNestedInsideSyncTransaction());
    2922           0 :         CancelTransaction(CurrentNestedInsideSyncTransaction());
    2923           0 :         mLink->SendMessage(cancel);
    2924             :     }
    2925           0 : }
    2926             : 
    2927             : void
    2928        1230 : CancelCPOWs()
    2929             : {
    2930        1230 :     if (gParentProcessBlocker) {
    2931           0 :         mozilla::Telemetry::Accumulate(mozilla::Telemetry::IPC_TRANSACTION_CANCEL, true);
    2932           0 :         gParentProcessBlocker->CancelCurrentTransaction();
    2933             :     }
    2934        1230 : }
    2935             : 
    2936             : } // namespace ipc
    2937             : } // namespace mozilla

Generated by: LCOV version 1.13