Bug 1858509 add thread-safety annotations around MediaSourceDemuxer::mMonitor r=alwu
[gecko.git] / ipc / glue / BackgroundImpl.cpp
blobbba09c261ae837766d3248cb19b7d49e68566d39
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "BackgroundChild.h"
8 #include "BackgroundParent.h"
10 #include "BackgroundChildImpl.h"
11 #include "BackgroundParentImpl.h"
12 #include "MainThreadUtils.h"
13 #include "base/process_util.h"
14 #include "base/task.h"
15 #include "FileDescriptor.h"
16 #include "GeckoProfiler.h"
17 #include "InputStreamUtils.h"
18 #include "mozilla/Assertions.h"
19 #include "mozilla/Atomics.h"
20 #include "mozilla/ClearOnShutdown.h"
21 #include "mozilla/DebugOnly.h"
22 #include "mozilla/MozPromise.h"
23 #include "mozilla/Services.h"
24 #include "mozilla/SpinEventLoopUntil.h"
25 #include "mozilla/StaticPtr.h"
26 #include "mozilla/Unused.h"
27 #include "mozilla/dom/ContentChild.h"
28 #include "mozilla/dom/ContentParent.h"
29 #include "mozilla/dom/File.h"
30 #include "mozilla/dom/WorkerPrivate.h"
31 #include "mozilla/dom/WorkerRef.h"
32 #include "mozilla/ipc/BackgroundStarterChild.h"
33 #include "mozilla/ipc/BackgroundStarterParent.h"
34 #include "mozilla/ipc/Endpoint.h"
35 #include "mozilla/ipc/PBackgroundStarter.h"
36 #include "mozilla/ipc/ProtocolTypes.h"
37 #include "nsCOMPtr.h"
38 #include "nsIEventTarget.h"
39 #include "nsIObserver.h"
40 #include "nsIObserverService.h"
41 #include "nsIRunnable.h"
42 #include "nsISupportsImpl.h"
43 #include "nsIThread.h"
44 #include "nsITimer.h"
45 #include "nsTArray.h"
46 #include "nsThreadUtils.h"
47 #include "nsTraceRefcnt.h"
48 #include "nsXULAppAPI.h"
49 #include "nsXPCOMPrivate.h"
50 #include "prthread.h"
52 #include <functional>
54 #ifdef RELEASE_OR_BETA
55 # define THREADSAFETY_ASSERT MOZ_ASSERT
56 #else
57 # define THREADSAFETY_ASSERT MOZ_RELEASE_ASSERT
58 #endif
60 #define CRASH_IN_CHILD_PROCESS(_msg) \
61 do { \
62 if (XRE_IsParentProcess()) { \
63 MOZ_ASSERT(false, _msg); \
64 } else { \
65 MOZ_CRASH(_msg); \
66 } \
67 } while (0)
69 using namespace mozilla;
70 using namespace mozilla::dom;
71 using namespace mozilla::ipc;
72 using namespace mozilla::net;
74 namespace {
76 class ChildImpl;
78 // -----------------------------------------------------------------------------
79 // Utility Functions
80 // -----------------------------------------------------------------------------
82 void AssertIsOnMainThread() { THREADSAFETY_ASSERT(NS_IsMainThread()); }
84 // -----------------------------------------------------------------------------
85 // ParentImpl Declaration
86 // -----------------------------------------------------------------------------
88 class ParentImpl final : public BackgroundParentImpl {
89 friend class ChildImpl;
90 friend class mozilla::ipc::BackgroundParent;
91 friend class mozilla::ipc::BackgroundStarterParent;
93 private:
94 class ShutdownObserver;
96 struct MOZ_STACK_CLASS TimerCallbackClosure {
97 nsIThread* mThread;
98 nsTArray<IToplevelProtocol*>* mLiveActors;
100 TimerCallbackClosure(nsIThread* aThread,
101 nsTArray<IToplevelProtocol*>* aLiveActors)
102 : mThread(aThread), mLiveActors(aLiveActors) {
103 AssertIsInMainProcess();
104 AssertIsOnMainThread();
105 MOZ_ASSERT(aThread);
106 MOZ_ASSERT(aLiveActors);
110 // The length of time we will wait at shutdown for all actors to clean
111 // themselves up before forcing them to be destroyed.
112 static const uint32_t kShutdownTimerDelayMS = 10000;
114 // This is only modified on the main thread. It is null if the thread does not
115 // exist or is shutting down.
116 static StaticRefPtr<nsIThread> sBackgroundThread;
118 // This is created and destroyed on the main thread but only modified on the
119 // background thread. It is specific to each instance of sBackgroundThread.
120 static nsTArray<IToplevelProtocol*>* sLiveActorsForBackgroundThread;
122 // This is only modified on the main thread.
123 static StaticRefPtr<nsITimer> sShutdownTimer;
125 // This exists so that that [Assert]IsOnBackgroundThread() can continue to
126 // work during shutdown.
127 static Atomic<PRThread*> sBackgroundPRThread;
129 // Maintains a count of live actors so that the background thread can be shut
130 // down when it is no longer needed.
131 // May be incremented on either the background thread (by an existing actor)
132 // or on the main thread, but must be decremented on the main thread.
133 static Atomic<uint64_t> sLiveActorCount;
135 // This is only modified on the main thread. It is true after the shutdown
136 // observer is registered and is never unset thereafter.
137 static bool sShutdownObserverRegistered;
139 // This is only modified on the main thread. It prevents us from trying to
140 // create the background thread after application shutdown has started.
141 static bool sShutdownHasStarted;
143 // null if this is a same-process actor.
144 const RefPtr<ThreadsafeContentParentHandle> mContent;
146 // Set when the actor is opened successfully and used to handle shutdown
147 // hangs. Only touched on the background thread.
148 nsTArray<IToplevelProtocol*>* mLiveActorArray;
150 // Set at construction to indicate whether this parent actor corresponds to a
151 // child actor in another process or to a child actor from a different thread
152 // in the same process.
153 const bool mIsOtherProcessActor;
155 // Set after ActorDestroy has been called. Only touched on the background
156 // thread.
157 bool mActorDestroyed;
159 public:
160 static bool IsOnBackgroundThread() {
161 return PR_GetCurrentThread() == sBackgroundPRThread;
164 static void AssertIsOnBackgroundThread() {
165 THREADSAFETY_ASSERT(IsOnBackgroundThread());
168 // `ParentImpl` instances need to be deleted on the main thread, despite IPC
169 // controlling them on a background thread. Use `_WITH_DELETE_ON_MAIN_THREAD`
170 // to force destruction to occur on the desired thread.
171 NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_DELETE_ON_MAIN_THREAD(ParentImpl,
172 override)
174 void Destroy();
176 private:
177 // Forwarded from BackgroundParent.
178 static bool IsOtherProcessActor(PBackgroundParent* aBackgroundActor);
180 // Forwarded from BackgroundParent.
181 static ThreadsafeContentParentHandle* GetContentParentHandle(
182 PBackgroundParent* aBackgroundActor);
184 // Forwarded from BackgroundParent.
185 static uint64_t GetChildID(PBackgroundParent* aBackgroundActor);
187 // Forwarded from BackgroundParent.
188 static void KillHardAsync(PBackgroundParent* aBackgroundActor,
189 const nsACString& aReason);
191 // Forwarded from BackgroundParent.
192 static bool AllocStarter(ContentParent* aContent,
193 Endpoint<PBackgroundStarterParent>&& aEndpoint,
194 bool aCrossProcess = true);
196 static bool CreateBackgroundThread();
198 static void ShutdownBackgroundThread();
200 static void ShutdownTimerCallback(nsITimer* aTimer, void* aClosure);
202 // NOTE: ParentImpl could be used in 2 cases below.
203 // 1. Within the parent process.
204 // 2. Between parent process and content process.
205 // |aContent| should be not null for case 2. For cases 1, it's null.
206 explicit ParentImpl(ThreadsafeContentParentHandle* aContent,
207 bool aIsOtherProcessActor)
208 : mContent(aContent),
209 mLiveActorArray(nullptr),
210 mIsOtherProcessActor(aIsOtherProcessActor),
211 mActorDestroyed(false) {
212 AssertIsInMainProcess();
213 MOZ_ASSERT_IF(!aIsOtherProcessActor, XRE_IsParentProcess());
216 ~ParentImpl() {
217 AssertIsInMainProcess();
218 AssertIsOnMainThread();
221 void MainThreadActorDestroy();
223 void SetLiveActorArray(nsTArray<IToplevelProtocol*>* aLiveActorArray) {
224 AssertIsInMainProcess();
225 AssertIsOnBackgroundThread();
226 MOZ_ASSERT(aLiveActorArray);
227 MOZ_ASSERT(!aLiveActorArray->Contains(this));
228 MOZ_ASSERT(!mLiveActorArray);
229 MOZ_ASSERT(mIsOtherProcessActor);
231 mLiveActorArray = aLiveActorArray;
232 mLiveActorArray->AppendElement(this);
235 // These methods are only called by IPDL.
236 virtual void ActorDestroy(ActorDestroyReason aWhy) override;
239 // -----------------------------------------------------------------------------
240 // ChildImpl Declaration
241 // -----------------------------------------------------------------------------
243 class ChildImpl final : public BackgroundChildImpl {
244 friend class mozilla::ipc::BackgroundChild;
245 friend class mozilla::ipc::BackgroundChildImpl;
246 friend class mozilla::ipc::BackgroundStarterChild;
248 typedef base::ProcessId ProcessId;
250 class ShutdownObserver;
252 public:
253 struct ThreadLocalInfo {
254 ThreadLocalInfo()
255 #ifdef DEBUG
256 : mClosed(false)
257 #endif
261 RefPtr<ChildImpl> mActor;
262 UniquePtr<BackgroundChildImpl::ThreadLocal> mConsumerThreadLocal;
263 #ifdef DEBUG
264 bool mClosed;
265 #endif
268 private:
269 // A thread-local index that is not valid.
270 static constexpr unsigned int kBadThreadLocalIndex =
271 static_cast<unsigned int>(-1);
273 // ThreadInfoWrapper encapsulates ThreadLocalInfo and ThreadLocalIndex and
274 // also provides some common functions for creating PBackground IPC actor.
275 class ThreadInfoWrapper final {
276 friend class ChildImpl;
278 public:
279 using ActorCreateFunc = void (*)(ThreadLocalInfo*, unsigned int,
280 nsIEventTarget*, ChildImpl**);
282 ThreadInfoWrapper() = default;
284 void Startup() {
285 MOZ_ASSERT(mThreadLocalIndex == kBadThreadLocalIndex,
286 "ThreadInfoWrapper::Startup() called more than once!");
288 PRStatus status =
289 PR_NewThreadPrivateIndex(&mThreadLocalIndex, ThreadLocalDestructor);
290 MOZ_RELEASE_ASSERT(status == PR_SUCCESS,
291 "PR_NewThreadPrivateIndex failed!");
293 MOZ_ASSERT(mThreadLocalIndex != kBadThreadLocalIndex);
296 void Shutdown() {
297 if (sShutdownHasStarted) {
298 MOZ_ASSERT_IF(mThreadLocalIndex != kBadThreadLocalIndex,
299 !PR_GetThreadPrivate(mThreadLocalIndex));
300 return;
303 if (mThreadLocalIndex == kBadThreadLocalIndex) {
304 return;
307 RefPtr<BackgroundStarterChild> starter;
309 auto lock = mStarter.Lock();
310 starter = lock->forget();
312 if (starter) {
313 CloseStarter(starter);
316 ThreadLocalInfo* threadLocalInfo;
317 #ifdef DEBUG
318 threadLocalInfo =
319 static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(mThreadLocalIndex));
320 MOZ_ASSERT(!threadLocalInfo);
321 #endif
323 threadLocalInfo = mMainThreadInfo;
324 if (threadLocalInfo) {
325 #ifdef DEBUG
326 MOZ_ASSERT(!threadLocalInfo->mClosed);
327 threadLocalInfo->mClosed = true;
328 #endif
330 ThreadLocalDestructor(threadLocalInfo);
331 mMainThreadInfo = nullptr;
335 template <typename Actor>
336 void InitStarter(Actor* aActor) {
337 AssertIsOnMainThread();
339 // Create a pair of endpoints and send them to the other process.
340 Endpoint<PBackgroundStarterParent> parent;
341 Endpoint<PBackgroundStarterChild> child;
342 MOZ_ALWAYS_SUCCEEDS(PBackgroundStarter::CreateEndpoints(
343 aActor->OtherPid(), base::GetCurrentProcId(), &parent, &child));
344 MOZ_ALWAYS_TRUE(aActor->SendInitBackground(std::move(parent)));
346 InitStarter(std::move(child));
349 void InitStarter(Endpoint<PBackgroundStarterChild>&& aEndpoint) {
350 AssertIsOnMainThread();
352 base::ProcessId otherPid = aEndpoint.OtherPid();
354 nsCOMPtr<nsISerialEventTarget> taskQueue;
355 MOZ_ALWAYS_SUCCEEDS(NS_CreateBackgroundTaskQueue(
356 "PBackgroundStarter Queue", getter_AddRefs(taskQueue)));
358 RefPtr<BackgroundStarterChild> starter =
359 new BackgroundStarterChild(otherPid, taskQueue);
361 taskQueue->Dispatch(NS_NewRunnableFunction(
362 "PBackgroundStarterChild Init",
363 [starter, endpoint = std::move(aEndpoint)]() mutable {
364 MOZ_ALWAYS_TRUE(endpoint.Bind(starter));
365 }));
367 // Swap in the newly initialized `BackgroundStarterChild`, and close the
368 // previous one if we're replacing an existing PBackgroundStarterChild
369 // instance.
370 RefPtr<BackgroundStarterChild> prevStarter;
372 auto lock = mStarter.Lock();
373 prevStarter = lock->forget();
374 *lock = starter.forget();
376 if (prevStarter) {
377 CloseStarter(prevStarter);
381 void CloseForCurrentThread() {
382 MOZ_ASSERT(!NS_IsMainThread());
384 if (mThreadLocalIndex == kBadThreadLocalIndex) {
385 return;
388 auto* threadLocalInfo =
389 static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(mThreadLocalIndex));
391 if (!threadLocalInfo) {
392 return;
395 #ifdef DEBUG
396 MOZ_ASSERT(!threadLocalInfo->mClosed);
397 threadLocalInfo->mClosed = true;
398 #endif
400 // Clearing the thread local will synchronously close the actor.
401 DebugOnly<PRStatus> status =
402 PR_SetThreadPrivate(mThreadLocalIndex, nullptr);
403 MOZ_ASSERT(status == PR_SUCCESS);
406 PBackgroundChild* GetOrCreateForCurrentThread() {
407 // Processes can be told to do final CC's during shutdown even though
408 // they never finished starting (and thus call this), because they
409 // hadn't gotten far enough to call Startup() before shutdown began.
410 if (mThreadLocalIndex == kBadThreadLocalIndex) {
411 NS_ERROR("BackgroundChild::Startup() was never called");
412 return nullptr;
414 if (NS_IsMainThread() && ChildImpl::sShutdownHasStarted) {
415 return nullptr;
418 auto* threadLocalInfo = NS_IsMainThread()
419 ? mMainThreadInfo
420 : static_cast<ThreadLocalInfo*>(
421 PR_GetThreadPrivate(mThreadLocalIndex));
423 if (!threadLocalInfo) {
424 auto newInfo = MakeUnique<ThreadLocalInfo>();
426 if (NS_IsMainThread()) {
427 mMainThreadInfo = newInfo.get();
428 } else {
429 if (PR_SetThreadPrivate(mThreadLocalIndex, newInfo.get()) !=
430 PR_SUCCESS) {
431 CRASH_IN_CHILD_PROCESS("PR_SetThreadPrivate failed!");
432 return nullptr;
436 threadLocalInfo = newInfo.release();
439 if (threadLocalInfo->mActor) {
440 return threadLocalInfo->mActor;
443 RefPtr<BackgroundStarterChild> starter;
445 auto lock = mStarter.Lock();
446 starter = *lock;
448 if (!starter) {
449 CRASH_IN_CHILD_PROCESS("No BackgroundStarterChild");
450 return nullptr;
453 Endpoint<PBackgroundParent> parent;
454 Endpoint<PBackgroundChild> child;
455 nsresult rv;
456 rv = PBackground::CreateEndpoints(
457 starter->mOtherPid, base::GetCurrentProcId(), &parent, &child);
458 if (NS_FAILED(rv)) {
459 NS_WARNING("Failed to create top level actor!");
460 return nullptr;
463 RefPtr<ChildImpl> strongActor = new ChildImpl();
464 if (!child.Bind(strongActor)) {
465 CRASH_IN_CHILD_PROCESS("Failed to bind ChildImpl!");
466 return nullptr;
468 strongActor->SetActorAlive();
469 threadLocalInfo->mActor = strongActor;
471 // Dispatch to the background task queue to create the relevant actor in
472 // the remote process.
473 starter->mTaskQueue->Dispatch(NS_NewRunnableFunction(
474 "PBackground GetOrCreateForCurrentThread",
475 [starter, endpoint = std::move(parent)]() mutable {
476 if (!starter->SendInitBackground(std::move(endpoint))) {
477 NS_WARNING("Failed to create toplevel actor");
479 }));
480 return strongActor;
483 private:
484 static void CloseStarter(BackgroundStarterChild* aStarter) {
485 aStarter->mTaskQueue->Dispatch(NS_NewRunnableFunction(
486 "PBackgroundStarterChild Close",
487 [starter = RefPtr{aStarter}] { starter->Close(); }));
490 // This is only modified on the main thread. It is the thread-local index
491 // that we use to store the BackgroundChild for each thread.
492 unsigned int mThreadLocalIndex = kBadThreadLocalIndex;
494 // On the main thread, we store TLS in this global instead of in
495 // mThreadLocalIndex. That way, cooperative main threads all share the same
496 // thread info.
497 ThreadLocalInfo* mMainThreadInfo = nullptr;
499 // The starter which will be used to launch PBackground instances of this
500 // type. Only modified on the main thread, but may be read by any thread
501 // wanting to start background actors.
502 StaticDataMutex<StaticRefPtr<BackgroundStarterChild>> mStarter{"mStarter"};
505 // For PBackground between parent and content process.
506 static ThreadInfoWrapper sParentAndContentProcessThreadInfo;
508 // This is only modified on the main thread. It prevents us from trying to
509 // create the background thread after application shutdown has started.
510 static bool sShutdownHasStarted;
512 #if defined(DEBUG) || !defined(RELEASE_OR_BETA)
513 nsISerialEventTarget* mOwningEventTarget;
514 #endif
516 #ifdef DEBUG
517 bool mActorWasAlive;
518 bool mActorDestroyed;
519 #endif
521 public:
522 static void Shutdown();
524 void AssertIsOnOwningThread() {
525 THREADSAFETY_ASSERT(mOwningEventTarget);
527 #ifdef RELEASE_OR_BETA
528 DebugOnly<bool> current;
529 #else
530 bool current;
531 #endif
532 THREADSAFETY_ASSERT(
533 NS_SUCCEEDED(mOwningEventTarget->IsOnCurrentThread(&current)));
534 THREADSAFETY_ASSERT(current);
537 void AssertActorDestroyed() {
538 MOZ_ASSERT(mActorDestroyed, "ChildImpl::ActorDestroy not called in time");
541 explicit ChildImpl()
542 #if defined(DEBUG) || !defined(RELEASE_OR_BETA)
543 : mOwningEventTarget(GetCurrentSerialEventTarget())
544 #endif
545 #ifdef DEBUG
547 mActorWasAlive(false),
548 mActorDestroyed(false)
549 #endif
551 AssertIsOnOwningThread();
554 void SetActorAlive() {
555 AssertIsOnOwningThread();
556 MOZ_ASSERT(!mActorWasAlive);
557 MOZ_ASSERT(!mActorDestroyed);
559 #ifdef DEBUG
560 mActorWasAlive = true;
561 #endif
564 NS_INLINE_DECL_REFCOUNTING(ChildImpl, override)
566 private:
567 // Forwarded from BackgroundChild.
568 static void Startup();
570 // Forwarded from BackgroundChild.
571 static PBackgroundChild* GetForCurrentThread();
573 // Forwarded from BackgroundChild.
574 static PBackgroundChild* GetOrCreateForCurrentThread();
576 static void CloseForCurrentThread();
578 // Forwarded from BackgroundChildImpl.
579 static BackgroundChildImpl::ThreadLocal* GetThreadLocalForCurrentThread();
581 // Forwarded from BackgroundChild.
582 static void InitContentStarter(mozilla::dom::ContentChild* aContent);
584 static void ThreadLocalDestructor(void* aThreadLocal);
586 // This class is reference counted.
587 ~ChildImpl() { MOZ_ASSERT_IF(mActorWasAlive, mActorDestroyed); }
589 // Only called by IPDL.
590 virtual void ActorDestroy(ActorDestroyReason aWhy) override;
593 // -----------------------------------------------------------------------------
594 // ParentImpl Helper Declarations
595 // -----------------------------------------------------------------------------
597 class ParentImpl::ShutdownObserver final : public nsIObserver {
598 public:
599 ShutdownObserver() { AssertIsOnMainThread(); }
601 NS_DECL_ISUPPORTS
602 NS_DECL_NSIOBSERVER
604 private:
605 ~ShutdownObserver() { AssertIsOnMainThread(); }
608 // -----------------------------------------------------------------------------
609 // ChildImpl Helper Declarations
610 // -----------------------------------------------------------------------------
612 class ChildImpl::ShutdownObserver final : public nsIObserver {
613 public:
614 ShutdownObserver() { AssertIsOnMainThread(); }
616 NS_DECL_ISUPPORTS
617 NS_DECL_NSIOBSERVER
619 private:
620 ~ShutdownObserver() { AssertIsOnMainThread(); }
623 } // namespace
625 namespace mozilla {
626 namespace ipc {
628 bool IsOnBackgroundThread() { return ParentImpl::IsOnBackgroundThread(); }
630 #ifdef DEBUG
632 void AssertIsOnBackgroundThread() { ParentImpl::AssertIsOnBackgroundThread(); }
634 #endif // DEBUG
636 } // namespace ipc
637 } // namespace mozilla
639 // -----------------------------------------------------------------------------
640 // BackgroundParent Public Methods
641 // -----------------------------------------------------------------------------
643 // static
644 bool BackgroundParent::IsOtherProcessActor(
645 PBackgroundParent* aBackgroundActor) {
646 return ParentImpl::IsOtherProcessActor(aBackgroundActor);
649 // static
650 ThreadsafeContentParentHandle* BackgroundParent::GetContentParentHandle(
651 PBackgroundParent* aBackgroundActor) {
652 return ParentImpl::GetContentParentHandle(aBackgroundActor);
655 // static
656 uint64_t BackgroundParent::GetChildID(PBackgroundParent* aBackgroundActor) {
657 return ParentImpl::GetChildID(aBackgroundActor);
660 // static
661 void BackgroundParent::KillHardAsync(PBackgroundParent* aBackgroundActor,
662 const nsACString& aReason) {
663 ParentImpl::KillHardAsync(aBackgroundActor, aReason);
666 // static
667 bool BackgroundParent::AllocStarter(
668 ContentParent* aContent, Endpoint<PBackgroundStarterParent>&& aEndpoint) {
669 return ParentImpl::AllocStarter(aContent, std::move(aEndpoint));
672 // -----------------------------------------------------------------------------
673 // BackgroundChild Public Methods
674 // -----------------------------------------------------------------------------
676 // static
677 void BackgroundChild::Startup() { ChildImpl::Startup(); }
679 // static
680 PBackgroundChild* BackgroundChild::GetForCurrentThread() {
681 return ChildImpl::GetForCurrentThread();
684 // static
685 PBackgroundChild* BackgroundChild::GetOrCreateForCurrentThread() {
686 return ChildImpl::GetOrCreateForCurrentThread();
689 // static
690 void BackgroundChild::CloseForCurrentThread() {
691 ChildImpl::CloseForCurrentThread();
694 // static
695 void BackgroundChild::InitContentStarter(ContentChild* aContent) {
696 ChildImpl::InitContentStarter(aContent);
699 // -----------------------------------------------------------------------------
700 // BackgroundChildImpl Public Methods
701 // -----------------------------------------------------------------------------
703 // static
704 BackgroundChildImpl::ThreadLocal*
705 BackgroundChildImpl::GetThreadLocalForCurrentThread() {
706 return ChildImpl::GetThreadLocalForCurrentThread();
709 // -----------------------------------------------------------------------------
710 // ParentImpl Static Members
711 // -----------------------------------------------------------------------------
713 StaticRefPtr<nsIThread> ParentImpl::sBackgroundThread;
715 nsTArray<IToplevelProtocol*>* ParentImpl::sLiveActorsForBackgroundThread;
717 StaticRefPtr<nsITimer> ParentImpl::sShutdownTimer;
719 Atomic<PRThread*> ParentImpl::sBackgroundPRThread;
721 Atomic<uint64_t> ParentImpl::sLiveActorCount;
723 bool ParentImpl::sShutdownObserverRegistered = false;
725 bool ParentImpl::sShutdownHasStarted = false;
727 // -----------------------------------------------------------------------------
728 // ChildImpl Static Members
729 // -----------------------------------------------------------------------------
731 ChildImpl::ThreadInfoWrapper ChildImpl::sParentAndContentProcessThreadInfo;
733 bool ChildImpl::sShutdownHasStarted = false;
735 // -----------------------------------------------------------------------------
736 // ParentImpl Implementation
737 // -----------------------------------------------------------------------------
739 // static
740 bool ParentImpl::IsOtherProcessActor(PBackgroundParent* aBackgroundActor) {
741 AssertIsOnBackgroundThread();
742 MOZ_ASSERT(aBackgroundActor);
744 return static_cast<ParentImpl*>(aBackgroundActor)->mIsOtherProcessActor;
747 // static
748 ThreadsafeContentParentHandle* ParentImpl::GetContentParentHandle(
749 PBackgroundParent* aBackgroundActor) {
750 AssertIsOnBackgroundThread();
751 MOZ_ASSERT(aBackgroundActor);
753 return static_cast<ParentImpl*>(aBackgroundActor)->mContent.get();
756 // static
757 uint64_t ParentImpl::GetChildID(PBackgroundParent* aBackgroundActor) {
758 AssertIsOnBackgroundThread();
759 MOZ_ASSERT(aBackgroundActor);
761 auto actor = static_cast<ParentImpl*>(aBackgroundActor);
762 if (actor->mContent) {
763 return actor->mContent->ChildID();
766 return 0;
769 // static
770 void ParentImpl::KillHardAsync(PBackgroundParent* aBackgroundActor,
771 const nsACString& aReason) {
772 AssertIsInMainProcess();
773 AssertIsOnBackgroundThread();
774 MOZ_ASSERT(aBackgroundActor);
775 MOZ_ASSERT(BackgroundParent::IsOtherProcessActor(aBackgroundActor));
777 RefPtr<ThreadsafeContentParentHandle> handle =
778 GetContentParentHandle(aBackgroundActor);
779 MOZ_ASSERT(handle);
781 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(
782 NS_NewRunnableFunction(
783 "ParentImpl::KillHardAsync",
784 [handle = std::move(handle), reason = nsCString{aReason}]() {
785 mozilla::AssertIsOnMainThread();
787 if (RefPtr<ContentParent> contentParent =
788 handle->GetContentParent()) {
789 contentParent->KillHard(reason.get());
792 NS_DISPATCH_NORMAL));
794 // After we've scheduled killing of the remote process, also ensure we induce
795 // a connection error in the IPC channel to immediately stop all IPC
796 // communication on this channel.
797 if (aBackgroundActor->CanSend()) {
798 aBackgroundActor->GetIPCChannel()->InduceConnectionError();
802 // static
803 bool ParentImpl::AllocStarter(ContentParent* aContent,
804 Endpoint<PBackgroundStarterParent>&& aEndpoint,
805 bool aCrossProcess) {
806 AssertIsInMainProcess();
807 AssertIsOnMainThread();
809 MOZ_ASSERT(aEndpoint.IsValid());
811 if (!sBackgroundThread && !CreateBackgroundThread()) {
812 NS_WARNING("Failed to create background thread!");
813 return false;
816 sLiveActorCount++;
818 RefPtr<BackgroundStarterParent> actor = new BackgroundStarterParent(
819 aContent ? aContent->ThreadsafeHandle() : nullptr, aCrossProcess);
821 if (NS_FAILED(sBackgroundThread->Dispatch(NS_NewRunnableFunction(
822 "BackgroundStarterParent::ConnectActorRunnable",
823 [actor = std::move(actor), endpoint = std::move(aEndpoint),
824 liveActorArray = sLiveActorsForBackgroundThread]() mutable {
825 MOZ_ASSERT(endpoint.IsValid());
826 MOZ_ALWAYS_TRUE(endpoint.Bind(actor));
827 actor->SetLiveActorArray(liveActorArray);
828 })))) {
829 NS_WARNING("Failed to dispatch connect runnable!");
831 MOZ_ASSERT(sLiveActorCount);
832 sLiveActorCount--;
835 return true;
838 // static
839 bool ParentImpl::CreateBackgroundThread() {
840 AssertIsInMainProcess();
841 AssertIsOnMainThread();
842 MOZ_ASSERT(!sBackgroundThread);
843 MOZ_ASSERT(!sLiveActorsForBackgroundThread);
845 if (sShutdownHasStarted) {
846 NS_WARNING(
847 "Trying to create background thread after shutdown has "
848 "already begun!");
849 return false;
852 nsCOMPtr<nsITimer> newShutdownTimer;
854 if (!sShutdownTimer) {
855 newShutdownTimer = NS_NewTimer();
856 if (!newShutdownTimer) {
857 return false;
861 if (!sShutdownObserverRegistered) {
862 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
863 if (NS_WARN_IF(!obs)) {
864 return false;
867 nsCOMPtr<nsIObserver> observer = new ShutdownObserver();
869 nsresult rv = obs->AddObserver(
870 observer, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, false);
871 if (NS_WARN_IF(NS_FAILED(rv))) {
872 return false;
875 sShutdownObserverRegistered = true;
878 nsCOMPtr<nsIThread> thread;
879 if (NS_FAILED(NS_NewNamedThread(
880 "IPDL Background", getter_AddRefs(thread),
881 NS_NewRunnableFunction(
882 "Background::ParentImpl::CreateBackgroundThreadRunnable", []() {
883 DebugOnly<PRThread*> oldBackgroundThread =
884 sBackgroundPRThread.exchange(PR_GetCurrentThread());
886 MOZ_ASSERT_IF(oldBackgroundThread,
887 PR_GetCurrentThread() != oldBackgroundThread);
888 })))) {
889 NS_WARNING("NS_NewNamedThread failed!");
890 return false;
893 sBackgroundThread = thread.forget();
895 sLiveActorsForBackgroundThread = new nsTArray<IToplevelProtocol*>(1);
897 if (!sShutdownTimer) {
898 MOZ_ASSERT(newShutdownTimer);
899 sShutdownTimer = newShutdownTimer;
902 return true;
905 // static
906 void ParentImpl::ShutdownBackgroundThread() {
907 AssertIsInMainProcess();
908 AssertIsOnMainThread();
909 MOZ_ASSERT(sShutdownHasStarted);
910 MOZ_ASSERT_IF(!sBackgroundThread, !sLiveActorCount);
911 MOZ_ASSERT_IF(sBackgroundThread, sShutdownTimer);
913 nsCOMPtr<nsITimer> shutdownTimer = sShutdownTimer.get();
914 sShutdownTimer = nullptr;
916 if (sBackgroundThread) {
917 nsCOMPtr<nsIThread> thread = sBackgroundThread.get();
918 sBackgroundThread = nullptr;
920 UniquePtr<nsTArray<IToplevelProtocol*>> liveActors(
921 sLiveActorsForBackgroundThread);
922 sLiveActorsForBackgroundThread = nullptr;
924 MOZ_ASSERT_IF(!sShutdownHasStarted, !sLiveActorCount);
926 if (sLiveActorCount) {
927 // We need to spin the event loop while we wait for all the actors to be
928 // cleaned up. We also set a timeout to force-kill any hanging actors.
929 TimerCallbackClosure closure(thread, liveActors.get());
931 MOZ_ALWAYS_SUCCEEDS(shutdownTimer->InitWithNamedFuncCallback(
932 &ShutdownTimerCallback, &closure, kShutdownTimerDelayMS,
933 nsITimer::TYPE_ONE_SHOT, "ParentImpl::ShutdownTimerCallback"));
935 SpinEventLoopUntil("ParentImpl::ShutdownBackgroundThread"_ns,
936 [&]() { return !sLiveActorCount; });
938 MOZ_ASSERT(liveActors->IsEmpty());
940 MOZ_ALWAYS_SUCCEEDS(shutdownTimer->Cancel());
943 // Dispatch this runnable to unregister the PR thread from the profiler.
944 MOZ_ALWAYS_SUCCEEDS(thread->Dispatch(NS_NewRunnableFunction(
945 "Background::ParentImpl::ShutdownBackgroundThreadRunnable", []() {
946 // It is possible that another background thread was created while
947 // this thread was shutting down. In that case we can't assert
948 // anything about sBackgroundPRThread and we should not modify it
949 // here.
950 sBackgroundPRThread.compareExchange(PR_GetCurrentThread(), nullptr);
951 })));
953 MOZ_ALWAYS_SUCCEEDS(thread->Shutdown());
957 // static
958 void ParentImpl::ShutdownTimerCallback(nsITimer* aTimer, void* aClosure) {
959 AssertIsInMainProcess();
960 AssertIsOnMainThread();
961 MOZ_ASSERT(sShutdownHasStarted);
962 MOZ_ASSERT(sLiveActorCount);
964 auto closure = static_cast<TimerCallbackClosure*>(aClosure);
965 MOZ_ASSERT(closure);
967 // Don't let the stack unwind until the ForceCloseBackgroundActorsRunnable has
968 // finished.
969 sLiveActorCount++;
971 InvokeAsync(
972 closure->mThread, __func__,
973 [liveActors = closure->mLiveActors]() {
974 MOZ_ASSERT(liveActors);
976 if (!liveActors->IsEmpty()) {
977 // Copy the array since calling Close() could mutate the
978 // actual array.
979 nsTArray<IToplevelProtocol*> actorsToClose(liveActors->Clone());
980 for (IToplevelProtocol* actor : actorsToClose) {
981 actor->Close();
984 return GenericPromise::CreateAndResolve(true, __func__);
986 ->Then(GetCurrentSerialEventTarget(), __func__, []() {
987 MOZ_ASSERT(sLiveActorCount);
988 sLiveActorCount--;
992 void ParentImpl::Destroy() {
993 // May be called on any thread!
995 AssertIsInMainProcess();
997 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(
998 NewNonOwningRunnableMethod("ParentImpl::MainThreadActorDestroy", this,
999 &ParentImpl::MainThreadActorDestroy)));
1002 void ParentImpl::MainThreadActorDestroy() {
1003 AssertIsInMainProcess();
1004 AssertIsOnMainThread();
1005 MOZ_ASSERT_IF(!mIsOtherProcessActor, !mContent);
1007 MOZ_ASSERT(sLiveActorCount);
1008 sLiveActorCount--;
1010 // This may be the last reference!
1011 Release();
1014 void ParentImpl::ActorDestroy(ActorDestroyReason aWhy) {
1015 AssertIsInMainProcess();
1016 AssertIsOnBackgroundThread();
1017 MOZ_ASSERT(!mActorDestroyed);
1018 MOZ_ASSERT_IF(mIsOtherProcessActor, mLiveActorArray);
1020 BackgroundParentImpl::ActorDestroy(aWhy);
1022 mActorDestroyed = true;
1024 if (mLiveActorArray) {
1025 MOZ_ALWAYS_TRUE(mLiveActorArray->RemoveElement(this));
1026 mLiveActorArray = nullptr;
1029 // This is tricky. We should be able to call Destroy() here directly because
1030 // we're not going to touch 'this' or our MessageChannel any longer on this
1031 // thread. Destroy() dispatches the MainThreadActorDestroy runnable and when
1032 // it runs it will destroy 'this' and our associated MessageChannel. However,
1033 // IPDL is about to call MessageChannel::Clear() on this thread! To avoid
1034 // racing with the main thread we must ensure that the MessageChannel lives
1035 // long enough to be cleared in this call stack.
1037 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(NewNonOwningRunnableMethod(
1038 "ParentImpl::Destroy", this, &ParentImpl::Destroy)));
1041 NS_IMPL_ISUPPORTS(ParentImpl::ShutdownObserver, nsIObserver)
1043 NS_IMETHODIMP
1044 ParentImpl::ShutdownObserver::Observe(nsISupports* aSubject, const char* aTopic,
1045 const char16_t* aData) {
1046 AssertIsInMainProcess();
1047 AssertIsOnMainThread();
1048 MOZ_ASSERT(!sShutdownHasStarted);
1049 MOZ_ASSERT(!strcmp(aTopic, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID));
1051 sShutdownHasStarted = true;
1053 // Do this first before calling (and spinning the event loop in)
1054 // ShutdownBackgroundThread().
1055 ChildImpl::Shutdown();
1057 ShutdownBackgroundThread();
1059 return NS_OK;
1062 BackgroundStarterParent::BackgroundStarterParent(
1063 ThreadsafeContentParentHandle* aContent, bool aCrossProcess)
1064 : mCrossProcess(aCrossProcess), mContent(aContent) {
1065 AssertIsOnMainThread();
1066 AssertIsInMainProcess();
1067 MOZ_ASSERT_IF(!mCrossProcess, !mContent);
1068 MOZ_ASSERT_IF(!mCrossProcess, XRE_IsParentProcess());
1071 void BackgroundStarterParent::SetLiveActorArray(
1072 nsTArray<IToplevelProtocol*>* aLiveActorArray) {
1073 AssertIsInMainProcess();
1074 AssertIsOnBackgroundThread();
1075 MOZ_ASSERT(aLiveActorArray);
1076 MOZ_ASSERT(!aLiveActorArray->Contains(this));
1077 MOZ_ASSERT(!mLiveActorArray);
1078 MOZ_ASSERT_IF(!mCrossProcess, OtherPid() == base::GetCurrentProcId());
1080 mLiveActorArray = aLiveActorArray;
1081 mLiveActorArray->AppendElement(this);
1084 IPCResult BackgroundStarterParent::RecvInitBackground(
1085 Endpoint<PBackgroundParent>&& aEndpoint) {
1086 AssertIsOnBackgroundThread();
1088 if (!aEndpoint.IsValid()) {
1089 return IPC_FAIL(this,
1090 "Cannot initialize PBackground with invalid endpoint");
1093 ParentImpl* actor = new ParentImpl(mContent, mCrossProcess);
1095 // Take a reference on this thread. If Open() fails then we will release this
1096 // reference in Destroy.
1097 NS_ADDREF(actor);
1099 ParentImpl::sLiveActorCount++;
1101 if (!aEndpoint.Bind(actor)) {
1102 actor->Destroy();
1103 return IPC_OK();
1106 if (mCrossProcess) {
1107 actor->SetLiveActorArray(mLiveActorArray);
1109 return IPC_OK();
1112 void BackgroundStarterParent::ActorDestroy(ActorDestroyReason aReason) {
1113 AssertIsOnBackgroundThread();
1115 if (mLiveActorArray) {
1116 MOZ_ALWAYS_TRUE(mLiveActorArray->RemoveElement(this));
1117 mLiveActorArray = nullptr;
1120 // Make sure to decrement `sLiveActorCount` on the main thread.
1121 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(
1122 NS_NewRunnableFunction("BackgroundStarterParent::MainThreadDestroy",
1123 [] { ParentImpl::sLiveActorCount--; })));
1126 // -----------------------------------------------------------------------------
1127 // ChildImpl Implementation
1128 // -----------------------------------------------------------------------------
1130 // static
1131 void ChildImpl::Startup() {
1132 // This happens on the main thread but before XPCOM has started so we can't
1133 // assert that we're being called on the main thread here.
1135 sParentAndContentProcessThreadInfo.Startup();
1137 nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
1138 MOZ_RELEASE_ASSERT(observerService);
1140 nsCOMPtr<nsIObserver> observer = new ShutdownObserver();
1142 nsresult rv = observerService->AddObserver(
1143 observer, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, false);
1144 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
1146 // Initialize a starter actor to allow starting PBackground within the parent
1147 // process.
1148 if (XRE_IsParentProcess()) {
1149 Endpoint<PBackgroundStarterParent> parent;
1150 Endpoint<PBackgroundStarterChild> child;
1151 MOZ_ALWAYS_SUCCEEDS(PBackgroundStarter::CreateEndpoints(
1152 base::GetCurrentProcId(), base::GetCurrentProcId(), &parent, &child));
1154 MOZ_ALWAYS_TRUE(ParentImpl::AllocStarter(nullptr, std::move(parent),
1155 /* aCrossProcess */ false));
1156 sParentAndContentProcessThreadInfo.InitStarter(std::move(child));
1160 // static
1161 void ChildImpl::Shutdown() {
1162 AssertIsOnMainThread();
1164 sParentAndContentProcessThreadInfo.Shutdown();
1166 sShutdownHasStarted = true;
1169 // static
1170 PBackgroundChild* ChildImpl::GetForCurrentThread() {
1171 MOZ_ASSERT(sParentAndContentProcessThreadInfo.mThreadLocalIndex !=
1172 kBadThreadLocalIndex);
1174 auto threadLocalInfo =
1175 NS_IsMainThread()
1176 ? sParentAndContentProcessThreadInfo.mMainThreadInfo
1177 : static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(
1178 sParentAndContentProcessThreadInfo.mThreadLocalIndex));
1180 if (!threadLocalInfo) {
1181 return nullptr;
1184 return threadLocalInfo->mActor;
1187 /* static */
1188 PBackgroundChild* ChildImpl::GetOrCreateForCurrentThread() {
1189 return sParentAndContentProcessThreadInfo.GetOrCreateForCurrentThread();
1192 // static
1193 void ChildImpl::CloseForCurrentThread() {
1194 MOZ_ASSERT(!NS_IsMainThread(),
1195 "PBackground for the main thread should be shut down via "
1196 "ChildImpl::Shutdown().");
1198 sParentAndContentProcessThreadInfo.CloseForCurrentThread();
1201 // static
1202 BackgroundChildImpl::ThreadLocal* ChildImpl::GetThreadLocalForCurrentThread() {
1203 MOZ_ASSERT(sParentAndContentProcessThreadInfo.mThreadLocalIndex !=
1204 kBadThreadLocalIndex,
1205 "BackgroundChild::Startup() was never called!");
1207 auto threadLocalInfo =
1208 NS_IsMainThread()
1209 ? sParentAndContentProcessThreadInfo.mMainThreadInfo
1210 : static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(
1211 sParentAndContentProcessThreadInfo.mThreadLocalIndex));
1213 if (!threadLocalInfo) {
1214 return nullptr;
1217 if (!threadLocalInfo->mConsumerThreadLocal) {
1218 threadLocalInfo->mConsumerThreadLocal =
1219 MakeUnique<BackgroundChildImpl::ThreadLocal>();
1222 return threadLocalInfo->mConsumerThreadLocal.get();
1225 // static
1226 void ChildImpl::InitContentStarter(mozilla::dom::ContentChild* aContent) {
1227 sParentAndContentProcessThreadInfo.InitStarter(aContent);
1230 // static
1231 void ChildImpl::ThreadLocalDestructor(void* aThreadLocal) {
1232 auto threadLocalInfo = static_cast<ThreadLocalInfo*>(aThreadLocal);
1234 if (threadLocalInfo) {
1235 MOZ_ASSERT(threadLocalInfo->mClosed);
1237 if (threadLocalInfo->mActor) {
1238 threadLocalInfo->mActor->Close();
1239 threadLocalInfo->mActor->AssertActorDestroyed();
1242 delete threadLocalInfo;
1246 void ChildImpl::ActorDestroy(ActorDestroyReason aWhy) {
1247 AssertIsOnOwningThread();
1249 #ifdef DEBUG
1250 MOZ_ASSERT(!mActorDestroyed);
1251 mActorDestroyed = true;
1252 #endif
1254 BackgroundChildImpl::ActorDestroy(aWhy);
1257 NS_IMPL_ISUPPORTS(ChildImpl::ShutdownObserver, nsIObserver)
1259 NS_IMETHODIMP
1260 ChildImpl::ShutdownObserver::Observe(nsISupports* aSubject, const char* aTopic,
1261 const char16_t* aData) {
1262 AssertIsOnMainThread();
1263 MOZ_ASSERT(!strcmp(aTopic, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID));
1265 ChildImpl::Shutdown();
1267 return NS_OK;