MessageChannel.cpp (82323B)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: sw=2 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 <math.h> 11 12 #include <utility> 13 14 #include "CrashAnnotations.h" 15 #include "base/waitable_event.h" 16 #include "mozilla/Assertions.h" 17 #include "mozilla/CycleCollectedJSContext.h" 18 #include "mozilla/FlowMarkers.h" 19 #include "mozilla/IntentionalCrash.h" 20 #include "mozilla/Logging.h" 21 #include "mozilla/Monitor.h" 22 #include "mozilla/Mutex.h" 23 #include "mozilla/ProfilerMarkers.h" 24 #include "mozilla/Sprintf.h" 25 #include "mozilla/StaticMutex.h" 26 #include "mozilla/glean/IpcMetrics.h" 27 #include "mozilla/TimeStamp.h" 28 #include "mozilla/dom/ScriptSettings.h" 29 #include "mozilla/ipc/NodeController.h" 30 #include "mozilla/ipc/ProcessChild.h" 31 #include "mozilla/ipc/ProtocolUtils.h" 32 #include "nsAppRunner.h" 33 #include "nsContentUtils.h" 34 #include "nsIDirectTaskDispatcher.h" 35 #include "nsTHashMap.h" 36 #include "nsDebug.h" 37 #include "nsExceptionHandler.h" 38 #include "nsIMemoryReporter.h" 39 #include "nsISupportsImpl.h" 40 #include "nsPrintfCString.h" 41 #include "nsThreadUtils.h" 42 43 #ifdef XP_WIN 44 # include "mozilla/gfx/Logging.h" 45 #endif 46 47 #ifdef FUZZING_SNAPSHOT 48 # include "mozilla/fuzzing/IPCFuzzController.h" 49 #endif 50 51 // Undo the damage done by mozzconf.h 52 #undef compress 53 54 static mozilla::LazyLogModule sLogModule("ipc"); 55 #define IPC_LOG(...) MOZ_LOG(sLogModule, LogLevel::Debug, (__VA_ARGS__)) 56 57 /* 58 * IPC design: 59 * 60 * There are two kinds of messages: async and sync. Sync messages are blocking. 61 * 62 * Terminology: To dispatch a message Foo is to run the RecvFoo code for 63 * it. This is also called "handling" the message. 64 * 65 * Sync and async messages can sometimes "nest" inside other sync messages 66 * (i.e., while waiting for the sync reply, we can dispatch the inner 67 * message). The three possible nesting levels are NOT_NESTED, 68 * NESTED_INSIDE_SYNC, and NESTED_INSIDE_CPOW. The intended uses are: 69 * NOT_NESTED - most messages. 70 * NESTED_INSIDE_SYNC - CPOW-related messages, which are always sync 71 * and can go in either direction. 72 * NESTED_INSIDE_CPOW - messages where we don't want to dispatch 73 * incoming CPOWs while waiting for the response. 74 * These nesting levels are ordered: NOT_NESTED, NESTED_INSIDE_SYNC, 75 * NESTED_INSIDE_CPOW. Async messages cannot be NESTED_INSIDE_SYNC but they can 76 * be NESTED_INSIDE_CPOW. 77 * 78 * To avoid jank, the parent process is not allowed to send NOT_NESTED sync 79 * messages. When a process is waiting for a response to a sync message M0, it 80 * will dispatch an incoming message M if: 81 * 1. M has a higher nesting level than M0, or 82 * 2. if M has the same nesting level as M0 and we're in the child, or 83 * 3. if M has the same nesting level as M0 and it was sent by the other side 84 * while dispatching M0. 85 * The idea is that messages with higher nesting should take precendence. The 86 * purpose of rule 2 is to handle a race where both processes send to each other 87 * simultaneously. In this case, we resolve the race in favor of the parent (so 88 * the child dispatches first). 89 * 90 * Messages satisfy the following properties: 91 * A. When waiting for a response to a sync message, we won't dispatch any 92 * messages of a lower nesting level. 93 * B. Messages of the same nesting level will be dispatched roughly in the 94 * order they were sent. The exception is when the parent and child send 95 * sync messages to each other simulataneously. In this case, the parent's 96 * message is dispatched first. While it is dispatched, the child may send 97 * further nested messages, and these messages may be dispatched before the 98 * child's original message. We can consider ordering to be preserved here 99 * because we pretend that the child's original message wasn't sent until 100 * after the parent's message is finished being dispatched. 101 * 102 * When waiting for a sync message reply, we dispatch an async message only if 103 * it is NESTED_INSIDE_CPOW. Normally NESTED_INSIDE_CPOW async 104 * messages are sent only from the child. However, the parent can send 105 * NESTED_INSIDE_CPOW async messages when it is creating a bridged protocol. 106 */ 107 108 using namespace mozilla; 109 using namespace mozilla::ipc; 110 111 using mozilla::MonitorAutoLock; 112 using mozilla::MonitorAutoUnlock; 113 using mozilla::dom::AutoNoJSAPI; 114 115 #define IPC_ASSERT(_cond, ...) \ 116 do { \ 117 AssertWorkerThread(); \ 118 mMonitor->AssertCurrentThreadOwns(); \ 119 if (!(_cond)) DebugAbort(__FILE__, __LINE__, #_cond, ##__VA_ARGS__); \ 120 } while (0) 121 122 static MessageChannel* gParentProcessBlocker = nullptr; 123 124 namespace mozilla { 125 namespace ipc { 126 127 // static 128 bool MessageChannel::sIsPumpingMessages = false; 129 130 class AutoEnterTransaction { 131 public: 132 explicit AutoEnterTransaction(MessageChannel* aChan, 133 IPC::Message::seqno_t aMsgSeqno, 134 IPC::Message::seqno_t aTransactionID, 135 int aNestedLevel) MOZ_REQUIRES(*aChan->mMonitor) 136 : mChan(aChan), 137 mActive(true), 138 mOutgoing(true), 139 mNestedLevel(aNestedLevel), 140 mSeqno(aMsgSeqno), 141 mTransaction(aTransactionID), 142 mNext(mChan->mTransactionStack) { 143 mChan->mMonitor->AssertCurrentThreadOwns(); 144 mChan->mTransactionStack = this; 145 } 146 147 explicit AutoEnterTransaction(MessageChannel* aChan, 148 const IPC::Message& aMessage) 149 MOZ_REQUIRES(*aChan->mMonitor) 150 : mChan(aChan), 151 mActive(true), 152 mOutgoing(false), 153 mNestedLevel(aMessage.nested_level()), 154 mSeqno(aMessage.seqno()), 155 mTransaction(aMessage.transaction_id()), 156 mNext(mChan->mTransactionStack) { 157 mChan->mMonitor->AssertCurrentThreadOwns(); 158 159 if (!aMessage.is_sync()) { 160 mActive = false; 161 return; 162 } 163 164 mChan->mTransactionStack = this; 165 } 166 167 ~AutoEnterTransaction() { 168 mChan->mMonitor->AssertCurrentThreadOwns(); 169 if (mActive) { 170 mChan->mTransactionStack = mNext; 171 } 172 } 173 174 void Cancel() { 175 mChan->mMonitor->AssertCurrentThreadOwns(); 176 AutoEnterTransaction* cur = mChan->mTransactionStack; 177 MOZ_RELEASE_ASSERT(cur == this); 178 while (cur && cur->mNestedLevel != IPC::Message::NOT_NESTED) { 179 // Note that, in the following situation, we will cancel multiple 180 // transactions: 181 // 1. Parent sends NESTED_INSIDE_SYNC message P1 to child. 182 // 2. Child sends NESTED_INSIDE_SYNC message C1 to child. 183 // 3. Child dispatches P1, parent blocks. 184 // 4. Child cancels. 185 // In this case, both P1 and C1 are cancelled. The parent will 186 // remove C1 from its queue when it gets the cancellation message. 187 MOZ_RELEASE_ASSERT(cur->mActive); 188 cur->mActive = false; 189 cur = cur->mNext; 190 } 191 192 mChan->mTransactionStack = cur; 193 194 MOZ_RELEASE_ASSERT(IsComplete()); 195 } 196 197 bool AwaitingSyncReply() const { 198 MOZ_RELEASE_ASSERT(mActive); 199 if (mOutgoing) { 200 return true; 201 } 202 return mNext ? mNext->AwaitingSyncReply() : false; 203 } 204 205 int AwaitingSyncReplyNestedLevel() const { 206 MOZ_RELEASE_ASSERT(mActive); 207 if (mOutgoing) { 208 return mNestedLevel; 209 } 210 return mNext ? mNext->AwaitingSyncReplyNestedLevel() : 0; 211 } 212 213 bool DispatchingSyncMessage() const { 214 MOZ_RELEASE_ASSERT(mActive); 215 if (!mOutgoing) { 216 return true; 217 } 218 return mNext ? mNext->DispatchingSyncMessage() : false; 219 } 220 221 int DispatchingSyncMessageNestedLevel() const { 222 MOZ_RELEASE_ASSERT(mActive); 223 if (!mOutgoing) { 224 return mNestedLevel; 225 } 226 return mNext ? mNext->DispatchingSyncMessageNestedLevel() : 0; 227 } 228 229 int NestedLevel() const { 230 MOZ_RELEASE_ASSERT(mActive); 231 return mNestedLevel; 232 } 233 234 IPC::Message::seqno_t SequenceNumber() const { 235 MOZ_RELEASE_ASSERT(mActive); 236 return mSeqno; 237 } 238 239 IPC::Message::seqno_t TransactionID() const { 240 MOZ_RELEASE_ASSERT(mActive); 241 return mTransaction; 242 } 243 244 void ReceivedReply(UniquePtr<IPC::Message> aMessage) { 245 MOZ_RELEASE_ASSERT(aMessage->seqno() == mSeqno); 246 MOZ_RELEASE_ASSERT(aMessage->transaction_id() == mTransaction); 247 MOZ_RELEASE_ASSERT(!mReply); 248 IPC_LOG("Reply received on worker thread: seqno=%" PRId64, mSeqno); 249 mReply = std::move(aMessage); 250 MOZ_RELEASE_ASSERT(IsComplete()); 251 } 252 253 void HandleReply(UniquePtr<IPC::Message> aMessage) { 254 mChan->mMonitor->AssertCurrentThreadOwns(); 255 AutoEnterTransaction* cur = mChan->mTransactionStack; 256 MOZ_RELEASE_ASSERT(cur == this); 257 while (cur) { 258 MOZ_RELEASE_ASSERT(cur->mActive); 259 if (aMessage->seqno() == cur->mSeqno) { 260 cur->ReceivedReply(std::move(aMessage)); 261 break; 262 } 263 cur = cur->mNext; 264 MOZ_RELEASE_ASSERT(cur); 265 } 266 } 267 268 bool IsComplete() { return !mActive || mReply; } 269 270 bool IsOutgoing() { return mOutgoing; } 271 272 bool IsCanceled() { return !mActive; } 273 274 bool IsBottom() const { return !mNext; } 275 276 bool IsError() { 277 MOZ_RELEASE_ASSERT(mReply); 278 return mReply->is_reply_error(); 279 } 280 281 UniquePtr<IPC::Message> GetReply() { return std::move(mReply); } 282 283 private: 284 MessageChannel* mChan; 285 286 // Active is true if this transaction is on the mChan->mTransactionStack 287 // stack. Generally we're not on the stack if the transaction was canceled 288 // or if it was for a message that doesn't require transactions (an async 289 // message). 290 bool mActive; 291 292 // Is this stack frame for an outgoing message? 293 bool mOutgoing; 294 295 // Properties of the message being sent/received. 296 int mNestedLevel; 297 IPC::Message::seqno_t mSeqno; 298 IPC::Message::seqno_t mTransaction; 299 300 // Next item in mChan->mTransactionStack. 301 AutoEnterTransaction* mNext; 302 303 // Pointer the a reply received for this message, if one was received. 304 UniquePtr<IPC::Message> mReply; 305 }; 306 307 class ChannelCountReporter final : public nsIMemoryReporter { 308 ~ChannelCountReporter() = default; 309 310 struct ChannelCounts { 311 size_t mNow; 312 size_t mMax; 313 314 ChannelCounts() : mNow(0), mMax(0) {} 315 316 void Inc() { 317 ++mNow; 318 if (mMax < mNow) { 319 mMax = mNow; 320 } 321 } 322 323 void Dec() { 324 MOZ_ASSERT(mNow > 0); 325 --mNow; 326 } 327 }; 328 329 using CountTable = nsTHashMap<nsDepCharHashKey, ChannelCounts>; 330 331 static StaticMutex sChannelCountMutex; 332 static CountTable* sChannelCounts MOZ_GUARDED_BY(sChannelCountMutex); 333 334 public: 335 NS_DECL_THREADSAFE_ISUPPORTS 336 337 NS_IMETHOD 338 CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData, 339 bool aAnonymize) override { 340 AutoTArray<std::pair<const char*, ChannelCounts>, 16> counts; 341 { 342 StaticMutexAutoLock countLock(sChannelCountMutex); 343 if (!sChannelCounts) { 344 return NS_OK; 345 } 346 counts.SetCapacity(sChannelCounts->Count()); 347 for (const auto& entry : *sChannelCounts) { 348 counts.AppendElement(std::pair{entry.GetKey(), entry.GetData()}); 349 } 350 } 351 352 for (const auto& entry : counts) { 353 nsPrintfCString pathNow("ipc-channels/%s", entry.first); 354 nsPrintfCString pathMax("ipc-channels-peak/%s", entry.first); 355 nsPrintfCString descNow( 356 "Number of IPC channels for" 357 " top-level actor type %s", 358 entry.first); 359 nsPrintfCString descMax( 360 "Peak number of IPC channels for" 361 " top-level actor type %s", 362 entry.first); 363 364 aHandleReport->Callback(""_ns, pathNow, KIND_OTHER, UNITS_COUNT, 365 entry.second.mNow, descNow, aData); 366 aHandleReport->Callback(""_ns, pathMax, KIND_OTHER, UNITS_COUNT, 367 entry.second.mMax, descMax, aData); 368 } 369 return NS_OK; 370 } 371 372 static void Increment(const char* aName) { 373 StaticMutexAutoLock countLock(sChannelCountMutex); 374 if (!sChannelCounts) { 375 sChannelCounts = new CountTable; 376 } 377 sChannelCounts->LookupOrInsert(aName).Inc(); 378 } 379 380 static void Decrement(const char* aName) { 381 StaticMutexAutoLock countLock(sChannelCountMutex); 382 MOZ_ASSERT(sChannelCounts); 383 sChannelCounts->LookupOrInsert(aName).Dec(); 384 } 385 }; 386 387 StaticMutex ChannelCountReporter::sChannelCountMutex; 388 ChannelCountReporter::CountTable* ChannelCountReporter::sChannelCounts; 389 390 NS_IMPL_ISUPPORTS(ChannelCountReporter, nsIMemoryReporter) 391 392 // In child processes, the first MessageChannel is created before 393 // XPCOM is initialized enough to construct the memory reporter 394 // manager. This retries every time a MessageChannel is constructed, 395 // which is good enough in practice. 396 template <class Reporter> 397 static void TryRegisterStrongMemoryReporter() { 398 static Atomic<bool> registered; 399 if (registered.compareExchange(false, true)) { 400 RefPtr<Reporter> reporter = new Reporter(); 401 if (NS_FAILED(RegisterStrongMemoryReporter(reporter))) { 402 registered = false; 403 } 404 } 405 } 406 407 MessageChannel::MessageChannel(const char* aName, IToplevelProtocol* aListener) 408 : mName(aName), mListener(aListener), mMonitor(new RefCountedMonitor()) { 409 MOZ_COUNT_CTOR(ipc::MessageChannel); 410 411 #ifdef XP_WIN 412 mEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr); 413 MOZ_RELEASE_ASSERT(mEvent, "CreateEvent failed! Nothing is going to work!"); 414 #endif 415 416 TryRegisterStrongMemoryReporter<ChannelCountReporter>(); 417 } 418 419 MessageChannel::~MessageChannel() { 420 MOZ_COUNT_DTOR(ipc::MessageChannel); 421 MonitorAutoLock lock(*mMonitor); 422 MOZ_RELEASE_ASSERT(!mOnCxxStack, 423 "MessageChannel destroyed while code on CxxStack"); 424 #ifdef XP_WIN 425 if (mEvent) { 426 BOOL ok = CloseHandle(mEvent); 427 mEvent = nullptr; 428 429 if (!ok) { 430 gfxDevCrash(mozilla::gfx::LogReason::MessageChannelCloseFailure) 431 << "MessageChannel failed to close. GetLastError: " << GetLastError(); 432 } 433 MOZ_RELEASE_ASSERT(ok); 434 } else { 435 gfxDevCrash(mozilla::gfx::LogReason::MessageChannelCloseFailure) 436 << "MessageChannel destructor ran without an mEvent Handle"; 437 } 438 #endif 439 440 // Make sure that the MessageChannel was closed (and therefore cleared) before 441 // it was destroyed. We can't properly close the channel at this point, as it 442 // would be unsafe to invoke our listener's callbacks, and we may be being 443 // destroyed on a thread other than `mWorkerThread`. 444 if (!IsClosedLocked()) { 445 CrashReporter::RecordAnnotationCString( 446 CrashReporter::Annotation::IPCFatalErrorProtocol, mName); 447 switch (mChannelState) { 448 case ChannelConnected: 449 MOZ_CRASH( 450 "MessageChannel destroyed without being closed " 451 "(mChannelState == ChannelConnected)."); 452 break; 453 case ChannelClosing: 454 MOZ_CRASH( 455 "MessageChannel destroyed without being closed " 456 "(mChannelState == ChannelClosing)."); 457 break; 458 case ChannelError: 459 MOZ_CRASH( 460 "MessageChannel destroyed without being closed " 461 "(mChannelState == ChannelError)."); 462 break; 463 default: 464 MOZ_CRASH("MessageChannel destroyed without being closed."); 465 } 466 } 467 468 // Double-check other properties for thoroughness. 469 MOZ_RELEASE_ASSERT(!mLink); 470 MOZ_RELEASE_ASSERT(!mChannelErrorTask); 471 MOZ_RELEASE_ASSERT(mPending.isEmpty()); 472 MOZ_RELEASE_ASSERT(!mShutdownTask); 473 } 474 475 #ifdef DEBUG 476 void MessageChannel::AssertMaybeDeferredCountCorrect() { 477 mMonitor->AssertCurrentThreadOwns(); 478 479 size_t count = 0; 480 for (MessageTask* task : mPending) { 481 task->AssertMonitorHeld(*mMonitor); 482 if (!IsAlwaysDeferred(*task->Msg())) { 483 count++; 484 } 485 } 486 487 MOZ_ASSERT(count == mMaybeDeferredPendingCount); 488 } 489 #endif 490 491 // This function returns the current transaction ID. Since the notion of a 492 // "current transaction" can be hard to define when messages race with each 493 // other and one gets canceled and the other doesn't, we require that this 494 // function is only called when the current transaction is known to be for a 495 // NESTED_INSIDE_SYNC message. In that case, we know for sure what the caller is 496 // looking for. 497 auto MessageChannel::CurrentNestedInsideSyncTransaction() const -> seqno_t { 498 mMonitor->AssertCurrentThreadOwns(); 499 if (!mTransactionStack) { 500 return 0; 501 } 502 MOZ_RELEASE_ASSERT(mTransactionStack->NestedLevel() == 503 IPC::Message::NESTED_INSIDE_SYNC); 504 return mTransactionStack->TransactionID(); 505 } 506 507 bool MessageChannel::TestOnlyIsTransactionComplete() const { 508 AssertWorkerThread(); 509 MonitorAutoLock lock(*mMonitor); 510 return !mTransactionStack || mTransactionStack->IsComplete(); 511 } 512 513 bool MessageChannel::AwaitingSyncReply() const { 514 mMonitor->AssertCurrentThreadOwns(); 515 return mTransactionStack ? mTransactionStack->AwaitingSyncReply() : false; 516 } 517 518 int MessageChannel::AwaitingSyncReplyNestedLevel() const { 519 mMonitor->AssertCurrentThreadOwns(); 520 return mTransactionStack ? mTransactionStack->AwaitingSyncReplyNestedLevel() 521 : 0; 522 } 523 524 bool MessageChannel::DispatchingSyncMessage() const { 525 mMonitor->AssertCurrentThreadOwns(); 526 return mTransactionStack ? mTransactionStack->DispatchingSyncMessage() 527 : false; 528 } 529 530 int MessageChannel::DispatchingSyncMessageNestedLevel() const { 531 mMonitor->AssertCurrentThreadOwns(); 532 return mTransactionStack 533 ? mTransactionStack->DispatchingSyncMessageNestedLevel() 534 : 0; 535 } 536 537 static void PrintErrorMessage(Side side, const char* channelName, 538 const char* msg) { 539 printf_stderr("\n###!!! [%s][%s] Error: %s\n\n", StringFromIPCSide(side), 540 channelName, msg); 541 } 542 543 bool MessageChannel::Connected() const { 544 mMonitor->AssertCurrentThreadOwns(); 545 return ChannelConnected == mChannelState; 546 } 547 548 bool MessageChannel::ConnectedOrClosing() const { 549 mMonitor->AssertCurrentThreadOwns(); 550 return ChannelConnected == mChannelState || ChannelClosing == mChannelState; 551 } 552 553 bool MessageChannel::CanSend() const { 554 if (!mMonitor) { 555 return false; 556 } 557 MonitorAutoLock lock(*mMonitor); 558 return Connected(); 559 } 560 561 void MessageChannel::Clear() { 562 AssertWorkerThread(); 563 mMonitor->AssertCurrentThreadOwns(); 564 MOZ_DIAGNOSTIC_ASSERT(IsClosedLocked(), "MessageChannel cleared too early?"); 565 MOZ_ASSERT(ChannelClosed == mChannelState || ChannelError == mChannelState); 566 567 // Don't clear mWorkerThread; we use it in AssertWorkerThread(). 568 // 569 // Also don't clear mListener. If we clear it, then sending a message 570 // through this channel after it's Clear()'ed can cause this process to 571 // crash. 572 573 if (mShutdownTask) { 574 mShutdownTask->Clear(); 575 mWorkerThread->UnregisterShutdownTask(mShutdownTask); 576 } 577 mShutdownTask = nullptr; 578 579 if (NS_IsMainThread() && gParentProcessBlocker == this) { 580 gParentProcessBlocker = nullptr; 581 } 582 583 SetIsCrossProcess(false); 584 585 mLink = nullptr; 586 587 if (mChannelErrorTask) { 588 mChannelErrorTask->Cancel(); 589 mChannelErrorTask = nullptr; 590 } 591 592 if (mFlushLazySendTask) { 593 mFlushLazySendTask->Cancel(); 594 mFlushLazySendTask = nullptr; 595 } 596 597 // Free up any memory used by pending messages. 598 mPending.clear(); 599 600 mMaybeDeferredPendingCount = 0; 601 } 602 603 bool MessageChannel::Open(ScopedPort aPort, Side aSide, 604 const nsID& aMessageChannelId, 605 nsISerialEventTarget* aEventTarget) { 606 nsCOMPtr<nsISerialEventTarget> eventTarget = 607 aEventTarget ? aEventTarget : GetCurrentSerialEventTarget(); 608 MOZ_RELEASE_ASSERT(eventTarget, 609 "Must open MessageChannel on a nsISerialEventTarget"); 610 MOZ_RELEASE_ASSERT(eventTarget->IsOnCurrentThread(), 611 "Must open MessageChannel from worker thread"); 612 613 auto shutdownTask = MakeRefPtr<WorkerTargetShutdownTask>(eventTarget, this); 614 nsresult rv = eventTarget->RegisterShutdownTask(shutdownTask); 615 MOZ_ASSERT(rv != NS_ERROR_NOT_IMPLEMENTED, 616 "target for MessageChannel must support shutdown tasks"); 617 if (rv == NS_ERROR_UNEXPECTED) { 618 // If shutdown tasks have already started running, dispatch our shutdown 619 // task manually. 620 NS_WARNING("Opening MessageChannel on EventTarget in shutdown"); 621 rv = eventTarget->Dispatch(shutdownTask->AsRunnable()); 622 } 623 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv), 624 "error registering ShutdownTask for MessageChannel"); 625 626 { 627 MonitorAutoLock lock(*mMonitor); 628 MOZ_RELEASE_ASSERT(!mLink, "Open() called > once"); 629 MOZ_RELEASE_ASSERT(ChannelClosed == mChannelState, "Not currently closed"); 630 MOZ_ASSERT(mSide == UnknownSide); 631 632 mMessageChannelId = aMessageChannelId; 633 mWorkerThread = eventTarget; 634 mShutdownTask = shutdownTask; 635 mLink = MakeUnique<PortLink>(this, std::move(aPort)); 636 mChannelState = ChannelConnected; 637 mSide = aSide; 638 } 639 640 // Notify our listener that the underlying IPC channel has been established. 641 // IProtocol will use this callback to create the ActorLifecycleProxy, and 642 // perform an `AddRef` call to keep the actor alive until the channel is 643 // disconnected. 644 // 645 // We unlock our monitor before calling `OnIPCChannelOpened` to ensure that 646 // any calls back into `MessageChannel` do not deadlock. At this point, we may 647 // be receiving messages on the IO thread, however we cannot process them on 648 // the worker thread or have notified our listener until after this function 649 // returns. 650 mListener->OnIPCChannelOpened(); 651 return true; 652 } 653 654 static Side GetOppSide(Side aSide) { 655 switch (aSide) { 656 case ChildSide: 657 return ParentSide; 658 case ParentSide: 659 return ChildSide; 660 default: 661 return UnknownSide; 662 } 663 } 664 665 bool MessageChannel::Open(MessageChannel* aTargetChan, 666 nsISerialEventTarget* aEventTarget, Side aSide) { 667 // Opens a connection to another thread in the same process. 668 669 MOZ_ASSERT(aTargetChan, "Need a target channel"); 670 671 nsID channelId = nsID::GenerateUUID(); 672 673 std::pair<ScopedPort, ScopedPort> ports = 674 NodeController::GetSingleton()->CreatePortPair(); 675 676 // NOTE: This dispatch must be sync as it captures locals by non-owning 677 // reference, however we can't use `NS_DispatchAndSpinEventLoopUntilComplete` 678 // as that will spin a nested event loop, and doesn't work with certain types 679 // of calling event targets. 680 base::WaitableEvent event(/* manual_reset */ true, 681 /* initially_signaled */ false); 682 MOZ_ALWAYS_SUCCEEDS(aEventTarget->Dispatch(NS_NewCancelableRunnableFunction( 683 "ipc::MessageChannel::OpenAsOtherThread", [&]() { 684 aTargetChan->Open(std::move(ports.second), GetOppSide(aSide), channelId, 685 aEventTarget); 686 event.Signal(); 687 }))); 688 bool ok = event.Wait(); 689 MOZ_RELEASE_ASSERT(ok); 690 691 // Now that the other side has connected, open the port on our side. 692 return Open(std::move(ports.first), aSide, channelId); 693 } 694 695 bool MessageChannel::OpenOnSameThread(MessageChannel* aTargetChan, 696 mozilla::ipc::Side aSide) { 697 auto [porta, portb] = NodeController::GetSingleton()->CreatePortPair(); 698 699 nsID channelId = nsID::GenerateUUID(); 700 701 aTargetChan->mIsSameThreadChannel = true; 702 mIsSameThreadChannel = true; 703 704 auto* currentThread = GetCurrentSerialEventTarget(); 705 return aTargetChan->Open(std::move(portb), GetOppSide(aSide), channelId, 706 currentThread) && 707 Open(std::move(porta), aSide, channelId, currentThread); 708 } 709 710 bool MessageChannel::Send(UniquePtr<Message> aMsg, seqno_t* aSeqno) { 711 MOZ_RELEASE_ASSERT(!aMsg->is_sync()); 712 MOZ_RELEASE_ASSERT(aMsg->nested_level() != IPC::Message::NESTED_INSIDE_SYNC); 713 MOZ_RELEASE_ASSERT(aMsg->routing_id() != MSG_ROUTING_NONE); 714 AssertWorkerThread(); 715 mMonitor->AssertNotCurrentThreadOwns(); 716 717 AutoSetValue<bool> setOnCxxStack(mOnCxxStack, true); 718 719 if (aMsg->seqno() == 0) { 720 aMsg->set_seqno(NextSeqno()); 721 } 722 if (aSeqno) { 723 *aSeqno = aMsg->seqno(); 724 } 725 726 MonitorAutoLock lock(*mMonitor); 727 if (!Connected()) { 728 ReportConnectionError("Send", aMsg->type()); 729 return false; 730 } 731 732 AddProfilerMarker(*aMsg, MessageDirection::eSending); 733 SendMessageToLink(std::move(aMsg)); 734 return true; 735 } 736 737 void MessageChannel::SendMessageToLink(UniquePtr<Message> aMsg) { 738 AssertWorkerThread(); 739 mMonitor->AssertCurrentThreadOwns(); 740 741 // If the channel is not cross-process, there's no reason to be lazy, so we 742 // ignore the flag in that case. 743 if (aMsg->is_lazy_send() && mIsCrossProcess) { 744 // If this is the first lazy message in the queue and our worker thread 745 // supports direct task dispatch, dispatch a task to flush messages, 746 // ensuring we don't leave them pending forever. 747 if (!mFlushLazySendTask) { 748 if (nsCOMPtr<nsIDirectTaskDispatcher> dispatcher = 749 do_QueryInterface(mWorkerThread)) { 750 mFlushLazySendTask = new FlushLazySendMessagesRunnable(this); 751 MOZ_ALWAYS_SUCCEEDS( 752 dispatcher->DispatchDirectTask(do_AddRef(mFlushLazySendTask))); 753 } 754 } 755 if (mFlushLazySendTask) { 756 mFlushLazySendTask->PushMessage(std::move(aMsg)); 757 return; 758 } 759 } 760 761 if (mFlushLazySendTask) { 762 FlushLazySendMessages(); 763 } 764 mLink->SendMessage(std::move(aMsg)); 765 } 766 767 void MessageChannel::FlushLazySendMessages() { 768 AssertWorkerThread(); 769 mMonitor->AssertCurrentThreadOwns(); 770 771 // Clean up any SendLazyTask which might be pending. 772 auto messages = mFlushLazySendTask->TakeMessages(); 773 mFlushLazySendTask = nullptr; 774 775 // Send all lazy messages, then clear the queue. 776 for (auto& msg : messages) { 777 mLink->SendMessage(std::move(msg)); 778 } 779 } 780 781 class BuildIDsMatchMessage : public IPC::Message { 782 public: 783 BuildIDsMatchMessage() 784 : IPC::Message(MSG_ROUTING_NONE, BUILD_IDS_MATCH_MESSAGE_TYPE) {} 785 void Log(const std::string& aPrefix, FILE* aOutf) const { 786 fputs("(special `Build IDs match' message)", aOutf); 787 } 788 }; 789 790 // Send the parent a special async message to confirm when the parent and child 791 // are of the same buildID. Skips sending the message and returns false if the 792 // buildIDs don't match. This is a minor variation on 793 // MessageChannel::Send(Message* aMsg). 794 bool MessageChannel::SendBuildIDsMatchMessage(const char* aParentBuildID) { 795 MOZ_ASSERT(!XRE_IsParentProcess()); 796 797 nsCString parentBuildID(aParentBuildID); 798 nsCString childBuildID(mozilla::PlatformBuildID()); 799 800 if (parentBuildID != childBuildID) { 801 // The build IDs didn't match, usually because an update occurred in the 802 // background. 803 return false; 804 } 805 806 auto msg = MakeUnique<BuildIDsMatchMessage>(); 807 808 MOZ_RELEASE_ASSERT(!msg->is_sync()); 809 MOZ_RELEASE_ASSERT(msg->nested_level() != IPC::Message::NESTED_INSIDE_SYNC); 810 811 AssertWorkerThread(); 812 mMonitor->AssertNotCurrentThreadOwns(); 813 // Don't check for MSG_ROUTING_NONE. 814 815 MonitorAutoLock lock(*mMonitor); 816 if (!Connected()) { 817 ReportConnectionError("SendBuildIDsMatchMessage", msg->type()); 818 return false; 819 } 820 821 #if defined(MOZ_DEBUG) && defined(ENABLE_TESTS) 822 // Technically, the behavior is interesting for any kind of process 823 // but when exercising tests, we want to crash only a content process and 824 // avoid making noise with other kind of processes crashing 825 if (const char* dontSend = PR_GetEnv("MOZ_BUILDID_MATCH_DONTSEND")) { 826 if (dontSend[0] == '1') { 827 // Bug 1732999: We are going to crash, so we need to advise leak check 828 // tooling to avoid intermittent missing leakcheck 829 NoteIntentionalCrash(XRE_GetProcessTypeString()); 830 if (XRE_IsContentProcess()) { 831 return false; 832 } 833 } 834 } 835 #endif 836 837 SendMessageToLink(std::move(msg)); 838 return true; 839 } 840 841 class CancelMessage : public IPC::Message { 842 public: 843 explicit CancelMessage(seqno_t transaction) 844 : IPC::Message(MSG_ROUTING_NONE, CANCEL_MESSAGE_TYPE) { 845 set_transaction_id(transaction); 846 } 847 static bool Read(const Message* msg) { return true; } 848 void Log(const std::string& aPrefix, FILE* aOutf) const { 849 fputs("(special `Cancel' message)", aOutf); 850 } 851 }; 852 853 bool MessageChannel::MaybeInterceptSpecialIOMessage(const Message& aMsg) { 854 mMonitor->AssertCurrentThreadOwns(); 855 856 if (MSG_ROUTING_NONE == aMsg.routing_id()) { 857 if (GOODBYE_MESSAGE_TYPE == aMsg.type()) { 858 // We've received a GOODBYE message, close the connection and mark 859 // ourselves as "Closing". 860 mLink->Close(); 861 mChannelState = ChannelClosing; 862 if (LoggingEnabledFor(mListener->GetProtocolName(), mSide)) { 863 printf( 864 "[%s %u] NOTE: %s%s actor received `Goodbye' message. Closing " 865 "channel.\n", 866 XRE_GeckoProcessTypeToString(XRE_GetProcessType()), 867 static_cast<uint32_t>(base::GetCurrentProcId()), 868 mListener->GetProtocolName(), StringFromIPCSide(mSide)); 869 } 870 871 // Notify the worker thread that the connection has been closed, as we 872 // will not receive an `OnChannelErrorFromLink` after calling 873 // `mLink->Close()`. 874 if (AwaitingSyncReply()) { 875 NotifyWorkerThread(); 876 } 877 PostErrorNotifyTask(); 878 return true; 879 } else if (CANCEL_MESSAGE_TYPE == aMsg.type()) { 880 IPC_LOG("Cancel from message"); 881 CancelTransaction(aMsg.transaction_id()); 882 NotifyWorkerThread(); 883 return true; 884 } else if (BUILD_IDS_MATCH_MESSAGE_TYPE == aMsg.type()) { 885 IPC_LOG("Build IDs match message"); 886 mBuildIDsConfirmedMatch = true; 887 return true; 888 } else if (IMPENDING_SHUTDOWN_MESSAGE_TYPE == aMsg.type()) { 889 IPC_LOG("Impending Shutdown received"); 890 ProcessChild::NotifiedImpendingShutdown(); 891 return true; 892 } 893 } 894 return false; 895 } 896 897 /* static */ 898 bool MessageChannel::IsAlwaysDeferred(const Message& aMsg) { 899 // If a message is not NESTED_INSIDE_CPOW and not sync, then we always defer 900 // it. 901 return aMsg.nested_level() != IPC::Message::NESTED_INSIDE_CPOW && 902 !aMsg.is_sync(); 903 } 904 905 bool MessageChannel::ShouldDeferMessage(const Message& aMsg) { 906 // Never defer messages that have the highest nested level, even async 907 // ones. This is safe because only the child can send these messages, so 908 // they can never nest. 909 if (aMsg.nested_level() == IPC::Message::NESTED_INSIDE_CPOW) { 910 MOZ_ASSERT(!IsAlwaysDeferred(aMsg)); 911 return false; 912 } 913 914 // Unless they're NESTED_INSIDE_CPOW, we always defer async messages. 915 // Note that we never send an async NESTED_INSIDE_SYNC message. 916 if (!aMsg.is_sync()) { 917 MOZ_RELEASE_ASSERT(aMsg.nested_level() == IPC::Message::NOT_NESTED); 918 MOZ_ASSERT(IsAlwaysDeferred(aMsg)); 919 return true; 920 } 921 922 MOZ_ASSERT(!IsAlwaysDeferred(aMsg)); 923 924 int msgNestedLevel = aMsg.nested_level(); 925 int waitingNestedLevel = AwaitingSyncReplyNestedLevel(); 926 927 // Always defer if the nested level of the incoming message is less than the 928 // nested level of the message we're awaiting. 929 if (msgNestedLevel < waitingNestedLevel) return true; 930 931 // Never defer if the message has strictly greater nested level. 932 if (msgNestedLevel > waitingNestedLevel) return false; 933 934 // When both sides send sync messages of the same nested level, we resolve the 935 // race by dispatching in the child and deferring the incoming message in 936 // the parent. However, the parent still needs to dispatch nested sync 937 // messages. 938 // 939 // Deferring in the parent only sort of breaks message ordering. When the 940 // child's message comes in, we can pretend the child hasn't quite 941 // finished sending it yet. Since the message is sync, we know that the 942 // child hasn't moved on yet. 943 return mSide == ParentSide && 944 aMsg.transaction_id() != CurrentNestedInsideSyncTransaction(); 945 } 946 947 class IPCFlowMarker : public BaseMarkerType<IPCFlowMarker> { 948 public: 949 static constexpr const char* Name = "IPCFlowMarker"; 950 951 using MS = MarkerSchema; 952 static constexpr MS::PayloadField PayloadFields[] = { 953 {"name", MS::InputType::CString, "Details", MS::Format::String, 954 MS::PayloadFlags::Searchable}, 955 {"flow", MS::InputType::Uint64, "Flow", MS::Format::Flow, 956 MS::PayloadFlags::Searchable}}; 957 958 static constexpr MS::Location Locations[] = {MS::Location::MarkerChart, 959 MS::Location::MarkerTable}; 960 static constexpr const char* TableLabel = 961 "{marker.data.name}(flow={marker.data.flow})"; 962 static constexpr const char* ChartLabel = "{marker.name}"; 963 964 static constexpr MS::ETWMarkerGroup Group = MS::ETWMarkerGroup::Generic; 965 966 static constexpr bool IsStackBased = true; 967 968 static void StreamJSONMarkerData( 969 mozilla::baseprofiler::SpliceableJSONWriter& aWriter, 970 IPC::Message::msgid_t aMessageType, Flow aFlow) { 971 aWriter.StringProperty( 972 "name", 973 mozilla::MakeStringSpan(IPC::StringFromIPCMessageType(aMessageType))); 974 aWriter.FlowProperty("flow", aFlow); 975 } 976 }; 977 978 static uint64_t LossyNarrowChannelId(const nsID& aID) { 979 // We xor both halves of the UUID together so that the parts of the id where 980 // the variant (m2) and version (m3[0]) get xored with random bits from the 981 // other halve. 982 uint64_t bits[2]; 983 memcpy(&bits, &aID, sizeof(bits)); 984 985 return bits[0] ^ bits[1]; 986 } 987 988 void MessageChannel::OnMessageReceivedFromLink(UniquePtr<Message> aMsg) { 989 mMonitor->AssertCurrentThreadOwns(); 990 MOZ_ASSERT(mChannelState == ChannelConnected); 991 992 if (MaybeInterceptSpecialIOMessage(*aMsg)) { 993 return; 994 } 995 996 mListener->OnChannelReceivedMessage(*aMsg); 997 998 // If we're awaiting a sync reply, we know that it needs to be immediately 999 // handled to unblock us. 1000 if (aMsg->is_sync() && aMsg->is_reply()) { 1001 IPC_LOG("Received reply seqno=%" PRId64 " xid=%" PRId64, aMsg->seqno(), 1002 aMsg->transaction_id()); 1003 1004 if (aMsg->seqno() == mTimedOutMessageSeqno) { 1005 // Drop the message, but allow future sync messages to be sent. 1006 IPC_LOG("Received reply to timedout message; igoring; xid=%" PRId64, 1007 mTimedOutMessageSeqno); 1008 EndTimeout(); 1009 return; 1010 } 1011 1012 MOZ_RELEASE_ASSERT(AwaitingSyncReply()); 1013 MOZ_RELEASE_ASSERT(!mTimedOutMessageSeqno); 1014 1015 mTransactionStack->HandleReply(std::move(aMsg)); 1016 NotifyWorkerThread(); 1017 return; 1018 } 1019 1020 // Nested messages cannot be compressed. 1021 MOZ_RELEASE_ASSERT(aMsg->compress_type() == IPC::Message::COMPRESSION_NONE || 1022 aMsg->nested_level() == IPC::Message::NOT_NESTED); 1023 1024 if (aMsg->compress_type() == IPC::Message::COMPRESSION_ENABLED && 1025 !mPending.isEmpty()) { 1026 auto* last = mPending.getLast(); 1027 last->AssertMonitorHeld(*mMonitor); 1028 bool compress = last->Msg()->type() == aMsg->type() && 1029 last->Msg()->routing_id() == aMsg->routing_id(); 1030 if (compress) { 1031 // This message type has compression enabled, and the back of the 1032 // queue was the same message type and routed to the same destination. 1033 // Replace it with the newer message. 1034 MOZ_RELEASE_ASSERT(last->Msg()->compress_type() == 1035 IPC::Message::COMPRESSION_ENABLED); 1036 last->Msg() = std::move(aMsg); 1037 return; 1038 } 1039 } else if (aMsg->compress_type() == IPC::Message::COMPRESSION_ALL && 1040 !mPending.isEmpty()) { 1041 for (MessageTask* p = mPending.getLast(); p; p = p->getPrevious()) { 1042 p->AssertMonitorHeld(*mMonitor); 1043 if (p->Msg()->type() == aMsg->type() && 1044 p->Msg()->routing_id() == aMsg->routing_id()) { 1045 // This message type has compression enabled, and the queue 1046 // holds a message with the same message type and routed to the 1047 // same destination. Erase it. Note that, since we always 1048 // compress these redundancies, There Can Be Only One. 1049 MOZ_RELEASE_ASSERT(p->Msg()->compress_type() == 1050 IPC::Message::COMPRESSION_ALL); 1051 MOZ_RELEASE_ASSERT(IsAlwaysDeferred(*p->Msg())); 1052 p->remove(); 1053 break; 1054 } 1055 } 1056 } 1057 1058 bool alwaysDeferred = IsAlwaysDeferred(*aMsg); 1059 1060 bool shouldWakeUp = AwaitingSyncReply() && !ShouldDeferMessage(*aMsg); 1061 1062 IPC_LOG("Receive from link; seqno=%" PRId64 ", xid=%" PRId64 1063 ", shouldWakeUp=%d", 1064 aMsg->seqno(), aMsg->transaction_id(), shouldWakeUp); 1065 1066 struct FlowMarkerDispatch { 1067 FlowMarkerDispatch(msgid_t type, Flow flow) : type(type), flow(flow) { 1068 if (profiler_feature_active(ProfilerFeature::Flows)) { 1069 options.Set(MarkerTiming::InstantNow()); 1070 } 1071 } 1072 ~FlowMarkerDispatch() { 1073 if (!options.IsTimingUnspecified()) { 1074 options.TimingRef().SetIntervalEnd(); 1075 profiler_add_marker("IPCDispatch", baseprofiler::category::OTHER, 1076 std::move(options), IPCFlowMarker{}, type, flow); 1077 } 1078 } 1079 msgid_t type; 1080 Flow flow; 1081 MarkerOptions options; 1082 }; 1083 1084 // We want this marker to span the time when Post is called so that we 1085 // can inherit the connection to the runnable. 1086 FlowMarkerDispatch marker( 1087 aMsg->type(), Flow::Global(static_cast<uint64_t>(aMsg->seqno()) ^ 1088 LossyNarrowChannelId(mMessageChannelId))); 1089 1090 // There are two cases we're concerned about, relating to the state of the 1091 // worker thread: 1092 // 1093 // (1) We are waiting on a sync reply - worker thread is blocked on the 1094 // IPC monitor. 1095 // - If the message is NESTED_INSIDE_SYNC, we wake up the worker thread to 1096 // deliver the message depending on ShouldDeferMessage. Otherwise, we 1097 // leave it in the mPending queue, posting a task to the worker event 1098 // loop, where it will be processed once the synchronous reply has been 1099 // received. 1100 // 1101 // (2) We are not waiting on a reply. 1102 // - We post a task to the worker event loop. 1103 // 1104 // Note that, we may notify the worker thread even though the monitor is not 1105 // blocked. This is okay, since we always check for pending events before 1106 // blocking again. 1107 1108 RefPtr<MessageTask> task = new MessageTask(this, std::move(aMsg)); 1109 mPending.insertBack(task); 1110 1111 if (!alwaysDeferred) { 1112 mMaybeDeferredPendingCount++; 1113 } 1114 1115 if (shouldWakeUp) { 1116 NotifyWorkerThread(); 1117 } 1118 1119 // Although we usually don't need to post a message task if 1120 // shouldWakeUp is true, it's easier to post anyway than to have to 1121 // guarantee that every Send call processes everything it's supposed to 1122 // before returning. 1123 task->AssertMonitorHeld(*mMonitor); 1124 task->Post(); 1125 } 1126 1127 void MessageChannel::PeekMessages( 1128 const std::function<bool(const Message& aMsg)>& aInvoke) { 1129 // FIXME: We shouldn't be holding the lock for aInvoke! 1130 MonitorAutoLock lock(*mMonitor); 1131 1132 for (MessageTask* it : mPending) { 1133 it->AssertMonitorHeld(*mMonitor); 1134 const Message& msg = *it->Msg(); 1135 if (!aInvoke(msg)) { 1136 break; 1137 } 1138 } 1139 } 1140 1141 void MessageChannel::ProcessPendingRequests( 1142 ActorLifecycleProxy* aProxy, AutoEnterTransaction& aTransaction) { 1143 mMonitor->AssertCurrentThreadOwns(); 1144 1145 AssertMaybeDeferredCountCorrect(); 1146 if (mMaybeDeferredPendingCount == 0) { 1147 return; 1148 } 1149 1150 IPC_LOG("ProcessPendingRequests for seqno=%" PRId64 ", xid=%" PRId64, 1151 aTransaction.SequenceNumber(), aTransaction.TransactionID()); 1152 1153 // Loop until there aren't any more nested messages to process. 1154 for (;;) { 1155 // If we canceled during ProcessPendingRequest, then we need to leave 1156 // immediately because the results of ShouldDeferMessage will be 1157 // operating with weird state (as if no Send is in progress). That could 1158 // cause even NOT_NESTED sync messages to be processed (but not 1159 // NOT_NESTED async messages), which would break message ordering. 1160 if (aTransaction.IsCanceled()) { 1161 return; 1162 } 1163 1164 Vector<UniquePtr<Message>> toProcess; 1165 1166 for (MessageTask* p = mPending.getFirst(); p;) { 1167 p->AssertMonitorHeld(*mMonitor); 1168 UniquePtr<Message>& msg = p->Msg(); 1169 1170 MOZ_RELEASE_ASSERT(!aTransaction.IsCanceled(), 1171 "Calling ShouldDeferMessage when cancelled"); 1172 bool defer = ShouldDeferMessage(*msg); 1173 1174 // Only log the interesting messages. 1175 if (msg->is_sync() || 1176 msg->nested_level() == IPC::Message::NESTED_INSIDE_CPOW) { 1177 IPC_LOG("ShouldDeferMessage(seqno=%" PRId64 ") = %d", msg->seqno(), 1178 defer); 1179 } 1180 1181 if (!defer) { 1182 MOZ_ASSERT(!IsAlwaysDeferred(*msg)); 1183 1184 if (!toProcess.append(std::move(msg))) MOZ_CRASH(); 1185 1186 mMaybeDeferredPendingCount--; 1187 1188 p = p->removeAndGetNext(); 1189 continue; 1190 } 1191 p = p->getNext(); 1192 } 1193 1194 if (toProcess.empty()) { 1195 break; 1196 } 1197 1198 // Processing these messages could result in more messages, so we 1199 // loop around to check for more afterwards. 1200 1201 for (auto& msg : toProcess) { 1202 ProcessPendingRequest(aProxy, std::move(msg)); 1203 } 1204 } 1205 1206 AssertMaybeDeferredCountCorrect(); 1207 } 1208 1209 bool MessageChannel::Send(UniquePtr<Message> aMsg, UniquePtr<Message>* aReply) { 1210 mozilla::TimeStamp start = TimeStamp::Now(); 1211 1212 // Sanity checks. 1213 AssertWorkerThread(); 1214 mMonitor->AssertNotCurrentThreadOwns(); 1215 MOZ_RELEASE_ASSERT(!mIsSameThreadChannel, 1216 "sync send over same-thread channel will deadlock!"); 1217 1218 RefPtr<ActorLifecycleProxy> proxy = Listener()->GetLifecycleProxy(); 1219 1220 #ifdef XP_WIN 1221 SyncStackFrame frame(this); 1222 NeuteredWindowRegion neuteredRgn(mFlags & 1223 REQUIRE_DEFERRED_MESSAGE_PROTECTION); 1224 #endif 1225 1226 AutoSetValue<bool> setOnCxxStack(mOnCxxStack, true); 1227 1228 MonitorAutoLock lock(*mMonitor); 1229 1230 if (mTimedOutMessageSeqno) { 1231 // Don't bother sending another sync message if a previous one timed out 1232 // and we haven't received a reply for it. Once the original timed-out 1233 // message receives a reply, we'll be able to send more sync messages 1234 // again. 1235 IPC_LOG("Send() failed due to previous timeout"); 1236 mLastSendError = SyncSendError::PreviousTimeout; 1237 return false; 1238 } 1239 1240 if (DispatchingSyncMessageNestedLevel() == IPC::Message::NOT_NESTED && 1241 aMsg->nested_level() > IPC::Message::NOT_NESTED) { 1242 // Don't allow sending CPOWs while we're dispatching a sync message. 1243 IPC_LOG("Nested level forbids send"); 1244 mLastSendError = SyncSendError::SendingCPOWWhileDispatchingSync; 1245 return false; 1246 } 1247 1248 if (DispatchingSyncMessageNestedLevel() == IPC::Message::NESTED_INSIDE_CPOW || 1249 DispatchingAsyncMessageNestedLevel() == 1250 IPC::Message::NESTED_INSIDE_CPOW) { 1251 // Generally only the parent dispatches urgent messages. And the only 1252 // sync messages it can send are NESTED_INSIDE_SYNC. Mainly we want to 1253 // ensure here that we don't return false for non-CPOW messages. 1254 MOZ_RELEASE_ASSERT(aMsg->nested_level() == 1255 IPC::Message::NESTED_INSIDE_SYNC); 1256 IPC_LOG("Sending while dispatching urgent message"); 1257 mLastSendError = SyncSendError::SendingCPOWWhileDispatchingUrgent; 1258 return false; 1259 } 1260 1261 if (aMsg->nested_level() < DispatchingSyncMessageNestedLevel() || 1262 aMsg->nested_level() < AwaitingSyncReplyNestedLevel()) { 1263 MOZ_RELEASE_ASSERT(DispatchingSyncMessage() || DispatchingAsyncMessage()); 1264 IPC_LOG("Cancel from Send"); 1265 auto cancel = 1266 MakeUnique<CancelMessage>(CurrentNestedInsideSyncTransaction()); 1267 CancelTransaction(CurrentNestedInsideSyncTransaction()); 1268 SendMessageToLink(std::move(cancel)); 1269 } 1270 1271 IPC_ASSERT(aMsg->is_sync(), "can only Send() sync messages here"); 1272 1273 IPC_ASSERT(aMsg->nested_level() >= DispatchingSyncMessageNestedLevel(), 1274 "can't send sync message of a lesser nested level than what's " 1275 "being dispatched"); 1276 IPC_ASSERT(AwaitingSyncReplyNestedLevel() <= aMsg->nested_level(), 1277 "nested sync message sends must be of increasing nested level"); 1278 IPC_ASSERT( 1279 DispatchingSyncMessageNestedLevel() != IPC::Message::NESTED_INSIDE_CPOW, 1280 "not allowed to send messages while dispatching urgent messages"); 1281 1282 IPC_ASSERT( 1283 DispatchingAsyncMessageNestedLevel() != IPC::Message::NESTED_INSIDE_CPOW, 1284 "not allowed to send messages while dispatching urgent messages"); 1285 1286 if (!Connected()) { 1287 ReportConnectionError("SendAndWait", aMsg->type()); 1288 mLastSendError = SyncSendError::NotConnectedBeforeSend; 1289 return false; 1290 } 1291 1292 aMsg->set_seqno(NextSeqno()); 1293 1294 seqno_t seqno = aMsg->seqno(); 1295 int nestedLevel = aMsg->nested_level(); 1296 msgid_t replyType = aMsg->type() + 1; 1297 1298 AutoEnterTransaction* stackTop = mTransactionStack; 1299 1300 // If the most recent message on the stack is NESTED_INSIDE_SYNC, then our 1301 // message should nest inside that and we use the same transaction 1302 // ID. Otherwise we need a new transaction ID (so we use the seqno of the 1303 // message we're sending). 1304 bool nest = 1305 stackTop && stackTop->NestedLevel() == IPC::Message::NESTED_INSIDE_SYNC; 1306 seqno_t transaction = nest ? stackTop->TransactionID() : seqno; 1307 aMsg->set_transaction_id(transaction); 1308 1309 AutoEnterTransaction transact(this, seqno, transaction, nestedLevel); 1310 1311 IPC_LOG("Send seqno=%" PRId64 ", xid=%" PRId64, seqno, transaction); 1312 1313 // aMsg will be destroyed soon, let's keep its type. 1314 const char* msgName = aMsg->name(); 1315 const msgid_t msgType = aMsg->type(); 1316 1317 AddProfilerMarker(*aMsg, MessageDirection::eSending); 1318 SendMessageToLink(std::move(aMsg)); 1319 1320 while (true) { 1321 MOZ_RELEASE_ASSERT(!transact.IsCanceled()); 1322 ProcessPendingRequests(proxy, transact); 1323 if (transact.IsComplete()) { 1324 break; 1325 } 1326 if (!Connected()) { 1327 ReportConnectionError("Send", msgType); 1328 mLastSendError = SyncSendError::DisconnectedDuringSend; 1329 return false; 1330 } 1331 1332 MOZ_RELEASE_ASSERT(!mTimedOutMessageSeqno); 1333 MOZ_RELEASE_ASSERT(!transact.IsComplete()); 1334 MOZ_RELEASE_ASSERT(mTransactionStack == &transact); 1335 1336 bool maybeTimedOut = !WaitForSyncNotify(); 1337 1338 if (mListener->NeedArtificialSleep()) { 1339 MonitorAutoUnlock unlock(*mMonitor); 1340 mListener->ArtificialSleep(); 1341 } 1342 1343 if (!Connected()) { 1344 ReportConnectionError("SendAndWait", msgType); 1345 mLastSendError = SyncSendError::DisconnectedDuringSend; 1346 return false; 1347 } 1348 1349 if (transact.IsCanceled()) { 1350 break; 1351 } 1352 1353 MOZ_RELEASE_ASSERT(mTransactionStack == &transact); 1354 1355 // We only time out a message if it initiated a new transaction (i.e., 1356 // if neither side has any other message Sends on the stack). 1357 bool canTimeOut = transact.IsBottom(); 1358 if (maybeTimedOut && canTimeOut && !ShouldContinueFromTimeout()) { 1359 // Since ShouldContinueFromTimeout drops the lock, we need to 1360 // re-check all our conditions here. We shouldn't time out if any of 1361 // these things happen because there won't be a reply to the timed 1362 // out message in these cases. 1363 if (transact.IsComplete()) { 1364 break; 1365 } 1366 1367 IPC_LOG("Timing out Send: xid=%" PRId64, transaction); 1368 1369 mTimedOutMessageSeqno = seqno; 1370 mTimedOutMessageNestedLevel = nestedLevel; 1371 mLastSendError = SyncSendError::TimedOut; 1372 return false; 1373 } 1374 1375 if (transact.IsCanceled()) { 1376 break; 1377 } 1378 } 1379 1380 if (transact.IsCanceled()) { 1381 IPC_LOG("Other side canceled seqno=%" PRId64 ", xid=%" PRId64, seqno, 1382 transaction); 1383 mLastSendError = SyncSendError::CancelledAfterSend; 1384 return false; 1385 } 1386 1387 if (transact.IsError()) { 1388 IPC_LOG("Error: seqno=%" PRId64 ", xid=%" PRId64, seqno, transaction); 1389 mLastSendError = SyncSendError::ReplyError; 1390 return false; 1391 } 1392 1393 uint32_t latencyMs = round((TimeStamp::Now() - start).ToMilliseconds()); 1394 IPC_LOG("Got reply: seqno=%" PRId64 ", xid=%" PRId64 1395 ", msgName=%s, latency=%ums", 1396 seqno, transaction, msgName, latencyMs); 1397 1398 UniquePtr<Message> reply = transact.GetReply(); 1399 1400 MOZ_RELEASE_ASSERT(reply); 1401 MOZ_RELEASE_ASSERT(reply->is_reply(), "expected reply"); 1402 MOZ_RELEASE_ASSERT(!reply->is_reply_error()); 1403 MOZ_RELEASE_ASSERT(reply->seqno() == seqno); 1404 MOZ_RELEASE_ASSERT(reply->type() == replyType, "wrong reply type"); 1405 MOZ_RELEASE_ASSERT(reply->is_sync()); 1406 1407 AddProfilerMarker(*reply, MessageDirection::eReceiving); 1408 1409 *aReply = std::move(reply); 1410 return true; 1411 } 1412 1413 bool MessageChannel::HasPendingEvents() { 1414 AssertWorkerThread(); 1415 mMonitor->AssertCurrentThreadOwns(); 1416 return ConnectedOrClosing() && !mPending.isEmpty(); 1417 } 1418 1419 bool MessageChannel::ProcessPendingRequest(ActorLifecycleProxy* aProxy, 1420 UniquePtr<Message> aUrgent) { 1421 AssertWorkerThread(); 1422 mMonitor->AssertCurrentThreadOwns(); 1423 1424 IPC_LOG("Process pending: seqno=%" PRId64 ", xid=%" PRId64, aUrgent->seqno(), 1425 aUrgent->transaction_id()); 1426 1427 // keep the error relevant information 1428 msgid_t msgType = aUrgent->type(); 1429 1430 DispatchMessage(aProxy, std::move(aUrgent)); 1431 if (!ConnectedOrClosing()) { 1432 ReportConnectionError("ProcessPendingRequest", msgType); 1433 return false; 1434 } 1435 1436 return true; 1437 } 1438 1439 bool MessageChannel::ShouldRunMessage(const Message& aMsg) { 1440 if (!mTimedOutMessageSeqno) { 1441 return true; 1442 } 1443 1444 // If we've timed out a message and we're awaiting the reply to the timed 1445 // out message, we have to be careful what messages we process. Here's what 1446 // can go wrong: 1447 // 1. child sends a NOT_NESTED sync message S 1448 // 2. parent sends a NESTED_INSIDE_SYNC sync message H at the same time 1449 // 3. parent times out H 1450 // 4. child starts processing H and sends a NESTED_INSIDE_SYNC message H' 1451 // nested within the same transaction 1452 // 5. parent dispatches S and sends reply 1453 // 6. child asserts because it instead expected a reply to H'. 1454 // 1455 // To solve this, we refuse to process S in the parent until we get a reply 1456 // to H. More generally, let the timed out message be M. We don't process a 1457 // message unless the child would need the response to that message in order 1458 // to process M. Those messages are the ones that have a higher nested level 1459 // than M or that are part of the same transaction as M. 1460 if (aMsg.nested_level() < mTimedOutMessageNestedLevel || 1461 (aMsg.nested_level() == mTimedOutMessageNestedLevel && 1462 aMsg.transaction_id() != mTimedOutMessageSeqno)) { 1463 return false; 1464 } 1465 1466 return true; 1467 } 1468 1469 void MessageChannel::RunMessage(ActorLifecycleProxy* aProxy, 1470 MessageTask& aTask) { 1471 AssertWorkerThread(); 1472 mMonitor->AssertCurrentThreadOwns(); 1473 aTask.AssertMonitorHeld(*mMonitor); 1474 1475 UniquePtr<Message>& msg = aTask.Msg(); 1476 1477 if (!ConnectedOrClosing()) { 1478 ReportConnectionError("RunMessage", msg->type()); 1479 return; 1480 } 1481 1482 // Check that we're going to run the first message that's valid to run. 1483 #if 0 1484 # ifdef DEBUG 1485 for (MessageTask* task : mPending) { 1486 if (task == &aTask) { 1487 break; 1488 } 1489 1490 MOZ_ASSERT(!ShouldRunMessage(*task->Msg()) || 1491 aTask.Msg()->priority() != task->Msg()->priority()); 1492 1493 } 1494 # endif 1495 #endif 1496 1497 if (!ShouldRunMessage(*msg)) { 1498 return; 1499 } 1500 1501 MOZ_RELEASE_ASSERT(aTask.isInList()); 1502 aTask.remove(); 1503 1504 if (!IsAlwaysDeferred(*msg)) { 1505 mMaybeDeferredPendingCount--; 1506 } 1507 1508 DispatchMessage(aProxy, std::move(msg)); 1509 } 1510 1511 NS_IMPL_ISUPPORTS_INHERITED(MessageChannel::MessageTask, CancelableRunnable, 1512 nsIRunnablePriority, nsIRunnableIPCMessageType) 1513 1514 static uint32_t ToRunnablePriority(IPC::Message::PriorityValue aPriority) { 1515 switch (aPriority) { 1516 case IPC::Message::LOW_PRIORITY: 1517 return nsIRunnablePriority::PRIORITY_LOW; 1518 case IPC::Message::NORMAL_PRIORITY: 1519 return nsIRunnablePriority::PRIORITY_NORMAL; 1520 case IPC::Message::INPUT_PRIORITY: 1521 return nsIRunnablePriority::PRIORITY_INPUT_HIGH; 1522 case IPC::Message::VSYNC_PRIORITY: 1523 return nsIRunnablePriority::PRIORITY_VSYNC; 1524 case IPC::Message::MEDIUMHIGH_PRIORITY: 1525 return nsIRunnablePriority::PRIORITY_MEDIUMHIGH; 1526 case IPC::Message::CONTROL_PRIORITY: 1527 return nsIRunnablePriority::PRIORITY_CONTROL; 1528 default: 1529 MOZ_ASSERT_UNREACHABLE(); 1530 return nsIRunnablePriority::PRIORITY_NORMAL; 1531 } 1532 } 1533 1534 MessageChannel::MessageTask::MessageTask(MessageChannel* aChannel, 1535 UniquePtr<Message> aMessage) 1536 : CancelableRunnable(aMessage->name()), 1537 mMonitor(aChannel->mMonitor), 1538 mChannel(aChannel), 1539 mMessage(std::move(aMessage)), 1540 mPriority(ToRunnablePriority(mMessage->priority())), 1541 mScheduled(false) 1542 #ifdef FUZZING_SNAPSHOT 1543 , 1544 mIsFuzzMsg(mMessage->IsFuzzMsg()), 1545 mFuzzStopped(false) 1546 #endif 1547 { 1548 MOZ_DIAGNOSTIC_ASSERT(mMessage, "message may not be null"); 1549 #ifdef FUZZING_SNAPSHOT 1550 if (mIsFuzzMsg) { 1551 MOZ_FUZZING_IPC_MT_CTOR(); 1552 } 1553 #endif 1554 } 1555 1556 MessageChannel::MessageTask::~MessageTask() { 1557 #ifdef FUZZING_SNAPSHOT 1558 // We track fuzzing messages until their run is complete. To make sure 1559 // that we don't miss messages that are for some reason destroyed without 1560 // being run (e.g. canceled), we catch this condition in the destructor. 1561 if (mIsFuzzMsg && !mFuzzStopped) { 1562 MOZ_FUZZING_IPC_MT_STOP(); 1563 } else if (!mIsFuzzMsg && !fuzzing::Nyx::instance().started()) { 1564 MOZ_FUZZING_IPC_PRE_FUZZ_MT_STOP(); 1565 } 1566 #endif 1567 } 1568 1569 nsresult MessageChannel::MessageTask::Run() { 1570 mMonitor->AssertNotCurrentThreadOwns(); 1571 1572 // Drop the toplevel actor's lifecycle proxy outside of our monitor if we take 1573 // it, as destroying our ActorLifecycleProxy reference can acquire the 1574 // monitor. 1575 RefPtr<ActorLifecycleProxy> proxy; 1576 1577 MonitorAutoLock lock(*mMonitor); 1578 1579 // In case we choose not to run this message, we may need to be able to Post 1580 // it again. 1581 mScheduled = false; 1582 1583 if (!isInList()) { 1584 return NS_OK; 1585 } 1586 1587 Channel()->AssertWorkerThread(); 1588 mMonitor->AssertSameMonitor(*Channel()->mMonitor); 1589 1590 #ifdef FUZZING_SNAPSHOT 1591 if (!mIsFuzzMsg) { 1592 if (fuzzing::Nyx::instance().started() && XRE_IsParentProcess() && 1593 Channel()->IsCrossProcess()) { 1594 // Once we started fuzzing, prevent non-fuzzing tasks from being 1595 // run and potentially blocking worker threads. 1596 // 1597 // TODO: This currently blocks all MessageTasks from running, not 1598 // just those belonging to the target process pair. We currently 1599 // do this for performance reasons, but it should be re-evaluated 1600 // at a later stage when we found a better snapshot point. 1601 return NS_OK; 1602 } 1603 // Record all running tasks prior to fuzzing, so we can wait for 1604 // them to settle before snapshotting. 1605 MOZ_FUZZING_IPC_PRE_FUZZ_MT_RUN(); 1606 } 1607 #endif 1608 1609 proxy = Channel()->Listener()->GetLifecycleProxy(); 1610 Channel()->RunMessage(proxy, *this); 1611 1612 #ifdef FUZZING_SNAPSHOT 1613 if (mIsFuzzMsg && !mFuzzStopped) { 1614 MOZ_FUZZING_IPC_MT_STOP(); 1615 mFuzzStopped = true; 1616 } 1617 #endif 1618 return NS_OK; 1619 } 1620 1621 // Warning: This method removes the receiver from whatever list it might be in. 1622 nsresult MessageChannel::MessageTask::Cancel() { 1623 mMonitor->AssertNotCurrentThreadOwns(); 1624 1625 MonitorAutoLock lock(*mMonitor); 1626 1627 if (!isInList()) { 1628 return NS_OK; 1629 } 1630 1631 Channel()->AssertWorkerThread(); 1632 mMonitor->AssertSameMonitor(*Channel()->mMonitor); 1633 if (!IsAlwaysDeferred(*Msg())) { 1634 Channel()->mMaybeDeferredPendingCount--; 1635 } 1636 1637 remove(); 1638 1639 #ifdef FUZZING_SNAPSHOT 1640 if (mIsFuzzMsg && !mFuzzStopped) { 1641 MOZ_FUZZING_IPC_MT_STOP(); 1642 mFuzzStopped = true; 1643 } 1644 #endif 1645 1646 return NS_OK; 1647 } 1648 1649 void MessageChannel::MessageTask::Post() { 1650 mMonitor->AssertCurrentThreadOwns(); 1651 mMonitor->AssertSameMonitor(*Channel()->mMonitor); 1652 MOZ_RELEASE_ASSERT(!mScheduled); 1653 MOZ_RELEASE_ASSERT(isInList()); 1654 1655 mScheduled = true; 1656 1657 Channel()->mWorkerThread->Dispatch(do_AddRef(this)); 1658 } 1659 1660 NS_IMETHODIMP 1661 MessageChannel::MessageTask::GetPriority(uint32_t* aPriority) { 1662 *aPriority = mPriority; 1663 return NS_OK; 1664 } 1665 1666 NS_IMETHODIMP 1667 MessageChannel::MessageTask::GetType(uint32_t* aType) { 1668 mMonitor->AssertNotCurrentThreadOwns(); 1669 1670 MonitorAutoLock lock(*mMonitor); 1671 if (!mMessage) { 1672 // If mMessage has been moved already elsewhere, we can't know what the type 1673 // has been. 1674 return NS_ERROR_FAILURE; 1675 } 1676 1677 *aType = mMessage->type(); 1678 return NS_OK; 1679 } 1680 1681 void MessageChannel::DispatchMessage(ActorLifecycleProxy* aProxy, 1682 UniquePtr<Message> aMsg) { 1683 AssertWorkerThread(); 1684 mMonitor->AssertCurrentThreadOwns(); 1685 1686 Maybe<AutoNoJSAPI> nojsapi; 1687 if (NS_IsMainThread() && CycleCollectedJSContext::Get()) { 1688 nojsapi.emplace(); 1689 } 1690 1691 UniquePtr<Message> reply; 1692 1693 #ifdef FUZZING_SNAPSHOT 1694 if (IsCrossProcess()) { 1695 aMsg = mozilla::fuzzing::IPCFuzzController::instance().replaceIPCMessage( 1696 std::move(aMsg)); 1697 } 1698 #endif 1699 1700 IPC_LOG("DispatchMessage: seqno=%" PRId64 ", xid=%" PRId64, aMsg->seqno(), 1701 aMsg->transaction_id()); 1702 AddProfilerMarker(*aMsg, MessageDirection::eReceiving); 1703 1704 { 1705 AutoEnterTransaction transaction(this, *aMsg); 1706 1707 seqno_t id = aMsg->transaction_id(); 1708 MOZ_RELEASE_ASSERT(!aMsg->is_sync() || id == transaction.TransactionID()); 1709 1710 { 1711 MonitorAutoUnlock unlock(*mMonitor); 1712 AutoSetValue<bool> setOnCxxStack(mOnCxxStack, true); 1713 1714 mListener->ArtificialSleep(); 1715 1716 if (aMsg->is_sync()) { 1717 DispatchSyncMessage(aProxy, *aMsg, reply); 1718 } else { 1719 DispatchAsyncMessage(aProxy, *aMsg); 1720 } 1721 1722 mListener->ArtificialSleep(); 1723 } 1724 1725 if (reply && transaction.IsCanceled()) { 1726 // The transaction has been canceled. Don't send a reply. 1727 IPC_LOG("Nulling out reply due to cancellation, seqno=%" PRId64 1728 ", xid=%" PRId64, 1729 aMsg->seqno(), id); 1730 reply = nullptr; 1731 } 1732 } 1733 1734 #ifdef FUZZING_SNAPSHOT 1735 if (aMsg->IsFuzzMsg()) { 1736 mozilla::fuzzing::IPCFuzzController::instance().syncAfterReplace(); 1737 } 1738 #endif 1739 1740 if (reply && ChannelConnected == mChannelState) { 1741 IPC_LOG("Sending reply seqno=%" PRId64 ", xid=%" PRId64, aMsg->seqno(), 1742 aMsg->transaction_id()); 1743 AddProfilerMarker(*reply, MessageDirection::eSending); 1744 1745 SendMessageToLink(std::move(reply)); 1746 } 1747 } 1748 1749 void MessageChannel::DispatchSyncMessage(ActorLifecycleProxy* aProxy, 1750 const Message& aMsg, 1751 UniquePtr<Message>& aReply) { 1752 AssertWorkerThread(); 1753 1754 int nestedLevel = aMsg.nested_level(); 1755 1756 MOZ_RELEASE_ASSERT(nestedLevel == IPC::Message::NOT_NESTED || 1757 NS_IsMainThread()); 1758 1759 MessageChannel* dummy; 1760 MessageChannel*& blockingVar = 1761 mSide == ChildSide && NS_IsMainThread() ? gParentProcessBlocker : dummy; 1762 1763 Result rv; 1764 { 1765 AutoSetValue<MessageChannel*> blocked(blockingVar, this); 1766 rv = aProxy->Get()->OnMessageReceived(aMsg, aReply); 1767 } 1768 1769 if (!MaybeHandleError(rv, aMsg, "DispatchSyncMessage")) { 1770 aReply = Message::ForSyncDispatchError(aMsg.nested_level()); 1771 } 1772 aReply->set_seqno(aMsg.seqno()); 1773 aReply->set_transaction_id(aMsg.transaction_id()); 1774 } 1775 1776 void MessageChannel::DispatchAsyncMessage(ActorLifecycleProxy* aProxy, 1777 const Message& aMsg) { 1778 AssertWorkerThread(); 1779 MOZ_RELEASE_ASSERT(!aMsg.is_sync()); 1780 1781 if (aMsg.routing_id() == MSG_ROUTING_NONE) { 1782 NS_WARNING("unhandled special message!"); 1783 MaybeHandleError(MsgNotKnown, aMsg, "DispatchAsyncMessage"); 1784 return; 1785 } 1786 1787 Result rv; 1788 { 1789 int nestedLevel = aMsg.nested_level(); 1790 AutoSetValue<bool> async(mDispatchingAsyncMessage, true); 1791 AutoSetValue<int> nestedLevelSet(mDispatchingAsyncMessageNestedLevel, 1792 nestedLevel); 1793 rv = aProxy->Get()->OnMessageReceived(aMsg); 1794 } 1795 MaybeHandleError(rv, aMsg, "DispatchAsyncMessage"); 1796 } 1797 1798 void MessageChannel::EnqueuePendingMessages() { 1799 AssertWorkerThread(); 1800 mMonitor->AssertCurrentThreadOwns(); 1801 1802 // XXX performance tuning knob: could process all or k pending 1803 // messages here, rather than enqueuing for later processing 1804 1805 RepostAllMessages(); 1806 } 1807 1808 bool MessageChannel::WaitResponse(bool aWaitTimedOut) { 1809 AssertWorkerThread(); 1810 if (aWaitTimedOut) { 1811 if (mInTimeoutSecondHalf) { 1812 // We've really timed out this time. 1813 return false; 1814 } 1815 // Try a second time. 1816 mInTimeoutSecondHalf = true; 1817 } else { 1818 mInTimeoutSecondHalf = false; 1819 } 1820 return true; 1821 } 1822 1823 #ifndef XP_WIN 1824 bool MessageChannel::WaitForSyncNotify() { 1825 AssertWorkerThread(); 1826 # ifdef DEBUG 1827 // WARNING: We don't release the lock here. We can't because the link 1828 // could signal at this time and we would miss it. Instead we require 1829 // ArtificialTimeout() to be extremely simple. 1830 if (mListener->ArtificialTimeout()) { 1831 return false; 1832 } 1833 # endif 1834 1835 MOZ_RELEASE_ASSERT(!mIsSameThreadChannel, 1836 "Wait on same-thread channel will deadlock!"); 1837 1838 TimeDuration timeout = (kNoTimeout == mTimeoutMs) 1839 ? TimeDuration::Forever() 1840 : TimeDuration::FromMilliseconds(mTimeoutMs); 1841 CVStatus status = mMonitor->Wait(timeout); 1842 1843 // If the timeout didn't expire, we know we received an event. The 1844 // converse is not true. 1845 return WaitResponse(status == CVStatus::Timeout); 1846 } 1847 1848 void MessageChannel::NotifyWorkerThread() { mMonitor->Notify(); } 1849 #endif 1850 1851 bool MessageChannel::ShouldContinueFromTimeout() { 1852 AssertWorkerThread(); 1853 mMonitor->AssertCurrentThreadOwns(); 1854 1855 bool cont; 1856 { 1857 MonitorAutoUnlock unlock(*mMonitor); 1858 cont = mListener->ShouldContinueFromReplyTimeout(); 1859 mListener->ArtificialSleep(); 1860 } 1861 1862 static enum { 1863 UNKNOWN, 1864 NOT_DEBUGGING, 1865 DEBUGGING 1866 } sDebuggingChildren = UNKNOWN; 1867 1868 if (sDebuggingChildren == UNKNOWN) { 1869 sDebuggingChildren = 1870 getenv("MOZ_DEBUG_CHILD_PROCESS") || getenv("MOZ_DEBUG_CHILD_PAUSE") 1871 ? DEBUGGING 1872 : NOT_DEBUGGING; 1873 } 1874 if (sDebuggingChildren == DEBUGGING) { 1875 return true; 1876 } 1877 1878 return cont; 1879 } 1880 1881 void MessageChannel::SetReplyTimeoutMs(int32_t aTimeoutMs) { 1882 // Set channel timeout value. Since this is broken up into 1883 // two period, the minimum timeout value is 2ms. 1884 AssertWorkerThread(); 1885 mTimeoutMs = 1886 (aTimeoutMs <= 0) ? kNoTimeout : (int32_t)ceil((double)aTimeoutMs / 2.0); 1887 } 1888 1889 void MessageChannel::ReportConnectionError(const char* aFunctionName, 1890 const uint32_t aMsgType) const { 1891 AssertWorkerThread(); 1892 mMonitor->AssertCurrentThreadOwns(); 1893 1894 const char* errorMsg = nullptr; 1895 switch (mChannelState) { 1896 case ChannelClosed: 1897 errorMsg = "Closed channel: cannot send/recv"; 1898 break; 1899 case ChannelClosing: 1900 errorMsg = "Channel closing: too late to send, messages will be lost"; 1901 break; 1902 case ChannelError: 1903 errorMsg = "Channel error: cannot send/recv"; 1904 break; 1905 1906 default: 1907 MOZ_CRASH("unreached"); 1908 } 1909 1910 // IPC connection errors are fairly common, especially "Channel closing: too 1911 // late to send/recv, messages will be lost", so shouldn't be being reported 1912 // on release builds, as that's misleading as to their severity. 1913 NS_WARNING(nsPrintfCString("IPC Connection Error: [%s][%s] %s(msgname=%s) %s", 1914 StringFromIPCSide(mSide), mName, aFunctionName, 1915 IPC::StringFromIPCMessageType(aMsgType), errorMsg) 1916 .get()); 1917 1918 MonitorAutoUnlock unlock(*mMonitor); 1919 mListener->ProcessingError(MsgDropped, errorMsg); 1920 } 1921 1922 bool MessageChannel::MaybeHandleError(Result code, const Message& aMsg, 1923 const char* channelName) { 1924 if (MsgProcessed == code) return true; 1925 1926 #ifdef FUZZING_SNAPSHOT 1927 mozilla::fuzzing::IPCFuzzController::instance().OnMessageError(code, aMsg); 1928 #endif 1929 1930 const char* errorMsg = nullptr; 1931 switch (code) { 1932 case MsgDropped: 1933 errorMsg = "Message dropped: message could not be delivered"; 1934 break; 1935 case MsgNotKnown: 1936 errorMsg = "Unknown message: not processed"; 1937 break; 1938 case MsgNotAllowed: 1939 errorMsg = "Message not allowed: cannot be sent/recvd in this state"; 1940 break; 1941 case MsgPayloadError: 1942 errorMsg = "Payload error: message could not be deserialized"; 1943 break; 1944 case MsgProcessingError: 1945 errorMsg = 1946 "Processing error: message was deserialized, but the handler " 1947 "returned false (indicating failure)"; 1948 break; 1949 case MsgValueError: 1950 errorMsg = 1951 "Value error: message was deserialized, but contained an illegal " 1952 "value"; 1953 break; 1954 1955 default: 1956 MOZ_CRASH("unknown Result code"); 1957 return false; 1958 } 1959 1960 char reason[512]; 1961 const char* msgname = aMsg.name(); 1962 if (msgname[0] == '?') { 1963 SprintfLiteral(reason, "(msgtype=0x%X) %s", aMsg.type(), errorMsg); 1964 } else { 1965 SprintfLiteral(reason, "%s %s", msgname, errorMsg); 1966 } 1967 1968 PrintErrorMessage(mSide, channelName, reason); 1969 1970 // Error handled in mozilla::ipc::IPCResult. 1971 if (code == MsgProcessingError) { 1972 return false; 1973 } 1974 1975 mListener->ProcessingError(code, reason); 1976 1977 return false; 1978 } 1979 1980 void MessageChannel::OnChannelErrorFromLink() { 1981 mMonitor->AssertCurrentThreadOwns(); 1982 MOZ_ASSERT(mChannelState == ChannelConnected); 1983 1984 IPC_LOG("OnChannelErrorFromLink"); 1985 1986 if (AwaitingSyncReply()) { 1987 NotifyWorkerThread(); 1988 } 1989 1990 if (mAbortOnError) { 1991 // mAbortOnError is set by main actors (e.g., ContentChild) to ensure 1992 // that the process terminates even if normal shutdown is prevented. 1993 // A MOZ_CRASH() here is not helpful because crash reporting relies 1994 // on the parent process which we know is dead or otherwise unusable. 1995 // 1996 // Additionally, the parent process can (and often is) killed on Android 1997 // when apps are backgrounded. We don't need to report a crash for 1998 // normal behavior in that case. 1999 printf_stderr("Exiting due to channel error.\n"); 2000 ProcessChild::QuickExit(); 2001 } 2002 mChannelState = ChannelError; 2003 mMonitor->Notify(); 2004 2005 PostErrorNotifyTask(); 2006 } 2007 2008 void MessageChannel::NotifyMaybeChannelError(ReleasableMonitorAutoLock& aLock) { 2009 AssertWorkerThread(); 2010 mMonitor->AssertCurrentThreadOwns(); 2011 aLock.AssertCurrentThreadOwns(); 2012 MOZ_ASSERT(mChannelState != ChannelConnected); 2013 2014 if (ChannelClosing == mChannelState || ChannelClosed == mChannelState) { 2015 // the channel closed, but we received a "Goodbye" message warning us 2016 // about it. no worries 2017 mChannelState = ChannelClosed; 2018 NotifyChannelClosed(aLock); 2019 return; 2020 } 2021 2022 MOZ_ASSERT(ChannelError == mChannelState); 2023 2024 Clear(); 2025 2026 // IPDL assumes these notifications do not fire twice, so we do not let 2027 // that happen. 2028 if (mNotifiedChannelDone) { 2029 return; 2030 } 2031 mNotifiedChannelDone = true; 2032 2033 // Let our listener know that the channel errored. This may cause the 2034 // channel to be deleted. Release our caller's `MonitorAutoLock` before 2035 // invoking the listener, as this may call back into MessageChannel, and/or 2036 // cause the channel to be destroyed. 2037 aLock.Unlock(); 2038 mListener->OnChannelError(); 2039 } 2040 2041 void MessageChannel::OnNotifyMaybeChannelError() { 2042 AssertWorkerThread(); 2043 mMonitor->AssertNotCurrentThreadOwns(); 2044 2045 // This lock guard may be reset by `NotifyMaybeChannelError` before invoking 2046 // listener callbacks which may destroy this `MessageChannel`. 2047 // 2048 // Acquiring the lock here also allows us to ensure that 2049 // `OnChannelErrorFromLink` has finished running before this task is allowed 2050 // to continue. 2051 ReleasableMonitorAutoLock lock(*mMonitor); 2052 2053 mChannelErrorTask = nullptr; 2054 2055 if (IsOnCxxStack()) { 2056 // This used to post a 10ms delayed task; however not all 2057 // nsISerialEventTarget implementations support delayed dispatch. 2058 // The delay being completely arbitrary, we may not as well have any. 2059 PostErrorNotifyTask(); 2060 return; 2061 } 2062 2063 // This call may destroy `this`. 2064 NotifyMaybeChannelError(lock); 2065 } 2066 2067 void MessageChannel::PostErrorNotifyTask() { 2068 mMonitor->AssertCurrentThreadOwns(); 2069 2070 if (mChannelErrorTask) { 2071 return; 2072 } 2073 2074 // This must be the last code that runs on this thread! 2075 mChannelErrorTask = NewNonOwningCancelableRunnableMethod( 2076 "ipc::MessageChannel::OnNotifyMaybeChannelError", this, 2077 &MessageChannel::OnNotifyMaybeChannelError); 2078 mWorkerThread->Dispatch(do_AddRef(mChannelErrorTask)); 2079 } 2080 2081 // Special async message. 2082 class GoodbyeMessage : public IPC::Message { 2083 public: 2084 GoodbyeMessage() : IPC::Message(MSG_ROUTING_NONE, GOODBYE_MESSAGE_TYPE) {} 2085 static bool Read(const Message* msg) { return true; } 2086 void Log(const std::string& aPrefix, FILE* aOutf) const { 2087 fputs("(special `Goodbye' message)", aOutf); 2088 } 2089 }; 2090 2091 void MessageChannel::InduceConnectionError() { 2092 MonitorAutoLock lock(*mMonitor); 2093 2094 // Either connected or closing, immediately convert to an error and notify. 2095 switch (mChannelState) { 2096 case ChannelConnected: 2097 // The channel is still actively connected. Immediately shut down the 2098 // connection with our peer and simulate it invoking 2099 // OnChannelErrorFromLink on us. 2100 // 2101 // This will update the state to ChannelError, preventing new messages 2102 // from being processed, leading to an error being reported asynchronously 2103 // to our listener. 2104 mLink->Close(); 2105 OnChannelErrorFromLink(); 2106 return; 2107 2108 case ChannelClosing: 2109 // An notify task has already been posted. Update mChannelState to stop 2110 // processing new messages and treat the notification as an error. 2111 mChannelState = ChannelError; 2112 return; 2113 2114 default: 2115 // Either already closed or errored. Nothing to do. 2116 MOZ_ASSERT(mChannelState == ChannelClosed || 2117 mChannelState == ChannelError); 2118 return; 2119 } 2120 } 2121 2122 void MessageChannel::NotifyImpendingShutdown() { 2123 UniquePtr<Message> msg = 2124 MakeUnique<Message>(MSG_ROUTING_NONE, IMPENDING_SHUTDOWN_MESSAGE_TYPE); 2125 MonitorAutoLock lock(*mMonitor); 2126 if (Connected()) { 2127 SendMessageToLink(std::move(msg)); 2128 } 2129 } 2130 2131 void MessageChannel::Close() { 2132 AssertWorkerThread(); 2133 mMonitor->AssertNotCurrentThreadOwns(); 2134 2135 // This lock guard may be reset by `Notify{ChannelClosed,MaybeChannelError}` 2136 // before invoking listener callbacks which may destroy this `MessageChannel`. 2137 ReleasableMonitorAutoLock lock(*mMonitor); 2138 2139 switch (mChannelState) { 2140 case ChannelError: 2141 // See bug 538586: if the listener gets deleted while the 2142 // IO thread's NotifyChannelError event is still enqueued 2143 // and subsequently deletes us, then the error event will 2144 // also be deleted and the listener will never be notified 2145 // of the channel error. 2146 NotifyMaybeChannelError(lock); 2147 return; 2148 case ChannelClosed: 2149 // Slightly unexpected but harmless; ignore. See bug 1554244. 2150 return; 2151 2152 default: 2153 // Notify the other side that we're about to close our socket. If we've 2154 // already received a Goodbye from the other side (and our state is 2155 // ChannelClosing), there's no reason to send one. 2156 if (ChannelConnected == mChannelState) { 2157 SendMessageToLink(MakeUnique<GoodbyeMessage>()); 2158 } 2159 mLink->Close(); 2160 mChannelState = ChannelClosed; 2161 NotifyChannelClosed(lock); 2162 return; 2163 } 2164 } 2165 2166 void MessageChannel::NotifyChannelClosed(ReleasableMonitorAutoLock& aLock) { 2167 AssertWorkerThread(); 2168 mMonitor->AssertCurrentThreadOwns(); 2169 aLock.AssertCurrentThreadOwns(); 2170 2171 if (ChannelClosed != mChannelState) { 2172 MOZ_CRASH("channel should have been closed!"); 2173 } 2174 2175 Clear(); 2176 2177 // IPDL assumes these notifications do not fire twice, so we do not let 2178 // that happen. 2179 if (mNotifiedChannelDone) { 2180 return; 2181 } 2182 mNotifiedChannelDone = true; 2183 2184 // Let our listener know that the channel was closed. This may cause the 2185 // channel to be deleted. Release our caller's `MonitorAutoLock` before 2186 // invoking the listener, as this may call back into MessageChannel, and/or 2187 // cause the channel to be destroyed. 2188 aLock.Unlock(); 2189 mListener->OnChannelClose(); 2190 } 2191 2192 void MessageChannel::DebugAbort(const char* file, int line, const char* cond, 2193 const char* why, bool reply) { 2194 AssertWorkerThread(); 2195 mMonitor->AssertCurrentThreadOwns(); 2196 2197 printf_stderr( 2198 "###!!! [MessageChannel][%s][%s:%d] " 2199 "Assertion (%s) failed. %s %s\n", 2200 StringFromIPCSide(mSide), file, line, cond, why, reply ? "(reply)" : ""); 2201 2202 MessageQueue pending = std::move(mPending); 2203 while (!pending.isEmpty()) { 2204 pending.getFirst()->AssertMonitorHeld(*mMonitor); 2205 printf_stderr(" [ %s%s ]\n", 2206 pending.getFirst()->Msg()->is_sync() ? "sync" : "async", 2207 pending.getFirst()->Msg()->is_reply() ? "reply" : ""); 2208 pending.popFirst(); 2209 } 2210 2211 MOZ_CRASH_UNSAFE(why); 2212 } 2213 2214 void MessageChannel::AddProfilerMarker(const IPC::Message& aMessage, 2215 MessageDirection aDirection) { 2216 mMonitor->AssertCurrentThreadOwns(); 2217 2218 if (profiler_feature_active(ProfilerFeature::Flows) && 2219 // If one of the profiler mutexes is locked on this thread, don't 2220 // record markers, because we don't want to expose profiler IPCs due to 2221 // the profiler itself, and also to avoid possible re-entrancy issues. 2222 !profiler_is_locked_on_current_thread()) { 2223 if (aDirection == MessageDirection::eSending) { 2224 auto flow = Flow::Global(aMessage.seqno() ^ 2225 LossyNarrowChannelId(mMessageChannelId)); 2226 profiler_add_marker("IPC", baseprofiler::category::OTHER, 2227 MarkerTiming::InstantNow(), IPCFlowMarker{}, 2228 aMessage.type(), flow); 2229 } 2230 } 2231 2232 if (profiler_feature_active(ProfilerFeature::IPCMessages)) { 2233 base::ProcessId pid = mListener->OtherPidMaybeInvalid(); 2234 // Only record markers for IPCs with a valid pid. 2235 // And if one of the profiler mutexes is locked on this thread, don't record 2236 // markers, because we don't want to expose profiler IPCs due to the 2237 // profiler itself, and also to avoid possible re-entrancy issues. 2238 if (pid != base::kInvalidProcessId && 2239 !profiler_is_locked_on_current_thread()) { 2240 // The current timestamp must be given to the `IPCMarker` payload. 2241 const TimeStamp now = TimeStamp::Now(); 2242 bool isThreadBeingProfiled = 2243 profiler_thread_is_being_profiled_for_markers(); 2244 PROFILER_MARKER( 2245 "IPC", IPC, 2246 mozilla::MarkerOptions( 2247 mozilla::MarkerTiming::InstantAt(now), 2248 // If the thread is being profiled, add the marker to 2249 // the current thread. If the thread is not being 2250 // profiled, add the marker to the main thread. It 2251 // will appear in the main thread's IPC track. Profiler analysis 2252 // UI correlates all the IPC markers from different threads and 2253 // generates processed markers. 2254 isThreadBeingProfiled ? mozilla::MarkerThreadId::CurrentThread() 2255 : mozilla::MarkerThreadId::MainThread()), 2256 IPCMarker, now, now, pid, aMessage.seqno(), aMessage.type(), mSide, 2257 aDirection, MessagePhase::Endpoint, aMessage.is_sync(), 2258 // aOriginThreadId: If the thread is being profiled, do not include a 2259 // thread ID, as it's the same as the markers. Only include this field 2260 // when the marker is being sent from another thread. 2261 isThreadBeingProfiled ? mozilla::MarkerThreadId{} 2262 : mozilla::MarkerThreadId::CurrentThread()); 2263 } 2264 } 2265 } 2266 2267 void MessageChannel::EndTimeout() { 2268 mMonitor->AssertCurrentThreadOwns(); 2269 2270 IPC_LOG("Ending timeout of seqno=%" PRId64, mTimedOutMessageSeqno); 2271 mTimedOutMessageSeqno = 0; 2272 mTimedOutMessageNestedLevel = 0; 2273 2274 RepostAllMessages(); 2275 } 2276 2277 void MessageChannel::RepostAllMessages() { 2278 mMonitor->AssertCurrentThreadOwns(); 2279 2280 bool needRepost = false; 2281 for (MessageTask* task : mPending) { 2282 task->AssertMonitorHeld(*mMonitor); 2283 if (!task->IsScheduled()) { 2284 needRepost = true; 2285 break; 2286 } 2287 } 2288 if (!needRepost) { 2289 // If everything is already scheduled to run, do nothing. 2290 return; 2291 } 2292 2293 // In some cases we may have deferred dispatch of some messages in the 2294 // queue. Now we want to run them again. However, we can't just re-post 2295 // those messages since the messages after them in mPending would then be 2296 // before them in the event queue. So instead we cancel everything and 2297 // re-post all messages in the correct order. 2298 MessageQueue queue = std::move(mPending); 2299 while (RefPtr<MessageTask> task = queue.popFirst()) { 2300 task->AssertMonitorHeld(*mMonitor); 2301 RefPtr<MessageTask> newTask = new MessageTask(this, std::move(task->Msg())); 2302 newTask->AssertMonitorHeld(*mMonitor); 2303 mPending.insertBack(newTask); 2304 newTask->Post(); 2305 } 2306 2307 AssertMaybeDeferredCountCorrect(); 2308 } 2309 2310 void MessageChannel::CancelTransaction(seqno_t transaction) { 2311 mMonitor->AssertCurrentThreadOwns(); 2312 2313 // When we cancel a transaction, we need to behave as if there's no longer 2314 // any IPC on the stack. Anything we were dispatching or sending will get 2315 // canceled. Consequently, we have to update the state variables below. 2316 // 2317 // We also need to ensure that when any IPC functions on the stack return, 2318 // they don't reset these values using an RAII class like AutoSetValue. To 2319 // avoid that, these RAII classes check if the variable they set has been 2320 // tampered with (by us). If so, they don't reset the variable to the old 2321 // value. 2322 2323 IPC_LOG("CancelTransaction: xid=%" PRId64, transaction); 2324 2325 // An unusual case: We timed out a transaction which the other side then 2326 // cancelled. In this case we just leave the timedout state and try to 2327 // forget this ever happened. 2328 if (transaction == mTimedOutMessageSeqno) { 2329 IPC_LOG("Cancelled timed out message %" PRId64, mTimedOutMessageSeqno); 2330 EndTimeout(); 2331 2332 // Normally mCurrentTransaction == 0 here. But it can be non-zero if: 2333 // 1. Parent sends NESTED_INSIDE_SYNC message H. 2334 // 2. Parent times out H. 2335 // 3. Child dispatches H and sends nested message H' (same transaction). 2336 // 4. Parent dispatches H' and cancels. 2337 MOZ_RELEASE_ASSERT(!mTransactionStack || 2338 mTransactionStack->TransactionID() == transaction); 2339 if (mTransactionStack) { 2340 mTransactionStack->Cancel(); 2341 } 2342 } else { 2343 MOZ_RELEASE_ASSERT(mTransactionStack->TransactionID() == transaction); 2344 mTransactionStack->Cancel(); 2345 } 2346 2347 bool foundSync = false; 2348 for (MessageTask* p = mPending.getFirst(); p;) { 2349 p->AssertMonitorHeld(*mMonitor); 2350 UniquePtr<Message>& msg = p->Msg(); 2351 2352 // If there was a race between the parent and the child, then we may 2353 // have a queued sync message. We want to drop this message from the 2354 // queue since if will get cancelled along with the transaction being 2355 // cancelled. This happens if the message in the queue is 2356 // NESTED_INSIDE_SYNC. 2357 if (msg->is_sync() && msg->nested_level() != IPC::Message::NOT_NESTED) { 2358 MOZ_RELEASE_ASSERT(!foundSync); 2359 MOZ_RELEASE_ASSERT(msg->transaction_id() != transaction); 2360 IPC_LOG("Removing msg from queue seqno=%" PRId64 " xid=%" PRId64, 2361 msg->seqno(), msg->transaction_id()); 2362 foundSync = true; 2363 if (!IsAlwaysDeferred(*msg)) { 2364 mMaybeDeferredPendingCount--; 2365 } 2366 p = p->removeAndGetNext(); 2367 continue; 2368 } 2369 2370 p = p->getNext(); 2371 } 2372 2373 AssertMaybeDeferredCountCorrect(); 2374 } 2375 2376 void MessageChannel::CancelCurrentTransaction() { 2377 MonitorAutoLock lock(*mMonitor); 2378 if (DispatchingSyncMessageNestedLevel() >= IPC::Message::NESTED_INSIDE_SYNC) { 2379 if (DispatchingSyncMessageNestedLevel() == 2380 IPC::Message::NESTED_INSIDE_CPOW || 2381 DispatchingAsyncMessageNestedLevel() == 2382 IPC::Message::NESTED_INSIDE_CPOW) { 2383 mListener->IntentionalCrash(); 2384 } 2385 2386 IPC_LOG("Cancel requested: current xid=%" PRId64, 2387 CurrentNestedInsideSyncTransaction()); 2388 MOZ_RELEASE_ASSERT(DispatchingSyncMessage()); 2389 auto cancel = 2390 MakeUnique<CancelMessage>(CurrentNestedInsideSyncTransaction()); 2391 CancelTransaction(CurrentNestedInsideSyncTransaction()); 2392 SendMessageToLink(std::move(cancel)); 2393 } 2394 } 2395 2396 void CancelCPOWs() { 2397 MOZ_ASSERT(NS_IsMainThread()); 2398 2399 if (gParentProcessBlocker) { 2400 mozilla::glean::ipc::transaction_cancel 2401 .EnumGet(mozilla::glean::ipc::TransactionCancelLabel::eTrue) 2402 .Add(); 2403 gParentProcessBlocker->CancelCurrentTransaction(); 2404 } 2405 } 2406 2407 bool MessageChannel::IsCrossProcess() const { 2408 mMonitor->AssertCurrentThreadOwns(); 2409 return mIsCrossProcess; 2410 } 2411 2412 void MessageChannel::SetIsCrossProcess(bool aIsCrossProcess) { 2413 mMonitor->AssertCurrentThreadOwns(); 2414 if (aIsCrossProcess == mIsCrossProcess) { 2415 return; 2416 } 2417 mIsCrossProcess = aIsCrossProcess; 2418 if (mIsCrossProcess) { 2419 ChannelCountReporter::Increment(mName); 2420 } else { 2421 ChannelCountReporter::Decrement(mName); 2422 } 2423 } 2424 2425 NS_IMPL_ISUPPORTS(MessageChannel::WorkerTargetShutdownTask, 2426 nsITargetShutdownTask) 2427 2428 MessageChannel::WorkerTargetShutdownTask::WorkerTargetShutdownTask( 2429 nsISerialEventTarget* aTarget, MessageChannel* aChannel) 2430 : mTarget(aTarget), mChannel(aChannel) {} 2431 2432 void MessageChannel::WorkerTargetShutdownTask::TargetShutdown() { 2433 MOZ_RELEASE_ASSERT(mTarget->IsOnCurrentThread()); 2434 IPC_LOG("Closing channel due to event target shutdown"); 2435 if (MessageChannel* channel = std::exchange(mChannel, nullptr)) { 2436 channel->Close(); 2437 } 2438 } 2439 2440 void MessageChannel::WorkerTargetShutdownTask::Clear() { 2441 MOZ_RELEASE_ASSERT(mTarget->IsOnCurrentThread()); 2442 mChannel = nullptr; 2443 } 2444 2445 NS_IMPL_ISUPPORTS_INHERITED0(MessageChannel::FlushLazySendMessagesRunnable, 2446 CancelableRunnable) 2447 2448 MessageChannel::FlushLazySendMessagesRunnable::FlushLazySendMessagesRunnable( 2449 MessageChannel* aChannel) 2450 : CancelableRunnable("MessageChannel::FlushLazyMessagesRunnable"), 2451 mChannel(aChannel) {} 2452 2453 NS_IMETHODIMP MessageChannel::FlushLazySendMessagesRunnable::Run() { 2454 if (mChannel) { 2455 MonitorAutoLock lock(*mChannel->mMonitor); 2456 MOZ_ASSERT(mChannel->mFlushLazySendTask == this); 2457 mChannel->FlushLazySendMessages(); 2458 } 2459 return NS_OK; 2460 } 2461 2462 nsresult MessageChannel::FlushLazySendMessagesRunnable::Cancel() { 2463 mQueue.Clear(); 2464 mChannel = nullptr; 2465 return NS_OK; 2466 } 2467 2468 void MessageChannel::FlushLazySendMessagesRunnable::PushMessage( 2469 UniquePtr<Message> aMsg) { 2470 MOZ_ASSERT(mChannel); 2471 mQueue.AppendElement(std::move(aMsg)); 2472 } 2473 2474 nsTArray<UniquePtr<IPC::Message>> 2475 MessageChannel::FlushLazySendMessagesRunnable::TakeMessages() { 2476 mChannel = nullptr; 2477 return std::move(mQueue); 2478 } 2479 2480 } // namespace ipc 2481 } // namespace mozilla