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
|