/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "BackgroundChild.h" #include "BackgroundParent.h" #include "BackgroundChildImpl.h" #include "BackgroundParentImpl.h" #include "base/process_util.h" #include "base/task.h" #include "FileDescriptor.h" #include "GeckoProfiler.h" #include "InputStreamUtils.h" #include "mozilla/Assertions.h" #include "mozilla/Atomics.h" #include "mozilla/ClearOnShutdown.h" #include "mozilla/DebugOnly.h" #include "mozilla/MozPromise.h" #include "mozilla/Services.h" #include "mozilla/SpinEventLoopUntil.h" #include "mozilla/StaticPtr.h" #include "mozilla/Unused.h" #include "mozilla/dom/ContentChild.h" #include "mozilla/dom/ContentParent.h" #include "mozilla/dom/File.h" #include "mozilla/dom/WorkerPrivate.h" #include "mozilla/dom/WorkerRef.h" #include "mozilla/ipc/Endpoint.h" #include "mozilla/ipc/ProtocolTypes.h" #include "mozilla/net/SocketProcessChild.h" #include "mozilla/net/SocketProcessBridgeChild.h" #include "nsCOMPtr.h" #include "nsIEventTarget.h" #include "nsIObserver.h" #include "nsIObserverService.h" #include "nsIRunnable.h" #include "nsISupportsImpl.h" #include "nsIThread.h" #include "nsITimer.h" #include "nsTArray.h" #include "nsThreadUtils.h" #include "nsTraceRefcnt.h" #include "nsXULAppAPI.h" #include "nsXPCOMPrivate.h" #include "prthread.h" #include #ifdef RELEASE_OR_BETA # define THREADSAFETY_ASSERT MOZ_ASSERT #else # define THREADSAFETY_ASSERT MOZ_RELEASE_ASSERT #endif #define CRASH_IN_CHILD_PROCESS(_msg) \ do { \ if (XRE_IsParentProcess()) { \ MOZ_ASSERT(false, _msg); \ } else { \ MOZ_CRASH(_msg); \ } \ } while (0) using namespace mozilla; using namespace mozilla::dom; using namespace mozilla::ipc; using namespace mozilla::net; namespace { class ChildImpl; // ----------------------------------------------------------------------------- // Utility Functions // ----------------------------------------------------------------------------- void AssertIsInMainProcess() { MOZ_ASSERT(XRE_IsParentProcess()); } void AssertIsInMainOrSocketProcess() { MOZ_ASSERT(XRE_IsParentProcess() || XRE_IsSocketProcess()); } void AssertIsOnMainThread() { THREADSAFETY_ASSERT(NS_IsMainThread()); } void AssertIsNotOnMainThread() { THREADSAFETY_ASSERT(!NS_IsMainThread()); } // ----------------------------------------------------------------------------- // ParentImpl Declaration // ----------------------------------------------------------------------------- class ParentImpl final : public BackgroundParentImpl { friend class mozilla::ipc::BackgroundParent; private: class ShutdownObserver; class CreateActorHelper; struct MOZ_STACK_CLASS TimerCallbackClosure { nsIThread* mThread; nsTArray* mLiveActors; TimerCallbackClosure(nsIThread* aThread, nsTArray* aLiveActors) : mThread(aThread), mLiveActors(aLiveActors) { AssertIsInMainOrSocketProcess(); AssertIsOnMainThread(); MOZ_ASSERT(aThread); MOZ_ASSERT(aLiveActors); } }; // The length of time we will wait at shutdown for all actors to clean // themselves up before forcing them to be destroyed. static const uint32_t kShutdownTimerDelayMS = 10000; // This is only modified on the main thread. It is null if the thread does not // exist or is shutting down. static StaticRefPtr sBackgroundThread; // This is created and destroyed on the main thread but only modified on the // background thread. It is specific to each instance of sBackgroundThread. static nsTArray* sLiveActorsForBackgroundThread; // This is only modified on the main thread. static StaticRefPtr sShutdownTimer; // This exists so that that [Assert]IsOnBackgroundThread() can continue to // work during shutdown. static Atomic sBackgroundPRThread; // This is only modified on the main thread. It maintains a count of live // actors so that the background thread can be shut down when it is no longer // needed. static uint64_t sLiveActorCount; // This is only modified on the main thread. It is true after the shutdown // observer is registered and is never unset thereafter. static bool sShutdownObserverRegistered; // This is only modified on the main thread. It prevents us from trying to // create the background thread after application shutdown has started. static bool sShutdownHasStarted; // Only touched on the main thread, null if this is a same-process actor. RefPtr mContent; // Set when the actor is opened successfully and used to handle shutdown // hangs. Only touched on the background thread. nsTArray* mLiveActorArray; // Set at construction to indicate whether this parent actor corresponds to a // child actor in another process or to a child actor from a different thread // in the same process. const bool mIsOtherProcessActor; // Set after ActorDestroy has been called. Only touched on the background // thread. bool mActorDestroyed; public: static already_AddRefed CreateActorForSameProcess( nsIEventTarget* aMainEventTarget); static bool IsOnBackgroundThread() { return PR_GetCurrentThread() == sBackgroundPRThread; } static void AssertIsOnBackgroundThread() { THREADSAFETY_ASSERT(IsOnBackgroundThread()); } // `ParentImpl` instances are created and need to be deleted on the main // thread, despite IPC controlling them on a background thread. Use // `_WITH_DELETE_ON_MAIN_THREAD` to force destruction to occur on the desired // thread. NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_DELETE_ON_MAIN_THREAD(ParentImpl, override) void Destroy(); private: // Forwarded from BackgroundParent. static bool IsOtherProcessActor(PBackgroundParent* aBackgroundActor); // Forwarded from BackgroundParent. static already_AddRefed GetContentParent( PBackgroundParent* aBackgroundActor); // Forwarded from BackgroundParent. static intptr_t GetRawContentParentForComparison( PBackgroundParent* aBackgroundActor); // Forwarded from BackgroundParent. static uint64_t GetChildID(PBackgroundParent* aBackgroundActor); // Forwarded from BackgroundParent. static bool GetLiveActorArray(PBackgroundParent* aBackgroundActor, nsTArray& aLiveActorArray); // Forwarded from BackgroundParent. static bool Alloc(ContentParent* aContent, Endpoint&& aEndpoint); static bool CreateBackgroundThread(); static void ShutdownBackgroundThread(); static void ShutdownTimerCallback(nsITimer* aTimer, void* aClosure); // For same-process actors. ParentImpl() : mLiveActorArray(nullptr), mIsOtherProcessActor(false), mActorDestroyed(false) { AssertIsInMainProcess(); AssertIsOnMainThread(); } // For other-process actors. // NOTE: ParentImpl could be used in 3 cases below. // 1. Between parent process and content process. // 2. Between socket process and content process. // 3. Between parent process and socket process. // |mContent| should be not null for case 1. For case 2 and 3, it's null. explicit ParentImpl(ContentParent* aContent) : mContent(aContent), mLiveActorArray(nullptr), mIsOtherProcessActor(true), mActorDestroyed(false) { MOZ_ASSERT(XRE_IsParentProcess() || XRE_IsSocketProcess()); AssertIsOnMainThread(); } ~ParentImpl() { AssertIsInMainOrSocketProcess(); AssertIsOnMainThread(); MOZ_ASSERT(!mContent); } void MainThreadActorDestroy(); void SetLiveActorArray(nsTArray* aLiveActorArray) { AssertIsInMainOrSocketProcess(); AssertIsOnBackgroundThread(); MOZ_ASSERT(aLiveActorArray); MOZ_ASSERT(!aLiveActorArray->Contains(this)); MOZ_ASSERT(!mLiveActorArray); MOZ_ASSERT(mIsOtherProcessActor); mLiveActorArray = aLiveActorArray; mLiveActorArray->AppendElement(this); } // These methods are only called by IPDL. virtual void ActorDestroy(ActorDestroyReason aWhy) override; }; // ----------------------------------------------------------------------------- // ChildImpl Declaration // ----------------------------------------------------------------------------- class ChildImpl final : public BackgroundChildImpl { friend class mozilla::ipc::BackgroundChild; friend class mozilla::ipc::BackgroundChildImpl; typedef base::ProcessId ProcessId; typedef mozilla::ipc::Transport Transport; class ShutdownObserver; public: class SendInitBackgroundRunnable; struct ThreadLocalInfo { ThreadLocalInfo() #ifdef DEBUG : mClosed(false) #endif { } RefPtr mActor; RefPtr mSendInitBackgroundRunnable; UniquePtr mConsumerThreadLocal; #ifdef DEBUG bool mClosed; #endif }; private: // A thread-local index that is not valid. static constexpr unsigned int kBadThreadLocalIndex = static_cast(-1); // ThreadInfoWrapper encapsulates ThreadLocalInfo and ThreadLocalIndex and // also provides some common functions for creating PBackground IPC actor. class ThreadInfoWrapper final { friend class ChildImpl; public: using ActorCreateFunc = void (*)(ThreadLocalInfo*, unsigned int, nsIEventTarget*, ChildImpl**); constexpr explicit ThreadInfoWrapper(ActorCreateFunc aFunc) : mThreadLocalIndex(kBadThreadLocalIndex), mMainThreadInfo(nullptr), mCreateActorFunc(aFunc) {} void Startup() { MOZ_ASSERT(mThreadLocalIndex == kBadThreadLocalIndex, "ThreadInfoWrapper::Startup() called more than once!"); PRStatus status = PR_NewThreadPrivateIndex(&mThreadLocalIndex, ThreadLocalDestructor); MOZ_RELEASE_ASSERT(status == PR_SUCCESS, "PR_NewThreadPrivateIndex failed!"); MOZ_ASSERT(mThreadLocalIndex != kBadThreadLocalIndex); } void Shutdown() { if (sShutdownHasStarted) { MOZ_ASSERT_IF(mThreadLocalIndex != kBadThreadLocalIndex, !PR_GetThreadPrivate(mThreadLocalIndex)); return; } if (mThreadLocalIndex == kBadThreadLocalIndex) { return; } ThreadLocalInfo* threadLocalInfo; #ifdef DEBUG threadLocalInfo = static_cast(PR_GetThreadPrivate(mThreadLocalIndex)); MOZ_ASSERT(!threadLocalInfo); #endif threadLocalInfo = mMainThreadInfo; if (threadLocalInfo) { #ifdef DEBUG MOZ_ASSERT(!threadLocalInfo->mClosed); threadLocalInfo->mClosed = true; #endif ThreadLocalDestructor(threadLocalInfo); mMainThreadInfo = nullptr; } } void CloseForCurrentThread() { MOZ_ASSERT(!NS_IsMainThread()); if (mThreadLocalIndex == kBadThreadLocalIndex) { return; } auto threadLocalInfo = static_cast(PR_GetThreadPrivate(mThreadLocalIndex)); if (!threadLocalInfo) { return; } #ifdef DEBUG MOZ_ASSERT(!threadLocalInfo->mClosed); threadLocalInfo->mClosed = true; #endif // Clearing the thread local will synchronously close the actor. DebugOnly status = PR_SetThreadPrivate(mThreadLocalIndex, nullptr); MOZ_ASSERT(status == PR_SUCCESS); } PBackgroundChild* GetOrCreateForCurrentThread( nsIEventTarget* aMainEventTarget) { MOZ_ASSERT_IF(NS_IsMainThread(), !aMainEventTarget); // Processes can be told to do final CC's during shutdown even though // they never finished starting (and thus call this), because they // hadn't gotten far enough to call Startup() before shutdown began. if (mThreadLocalIndex == kBadThreadLocalIndex) { NS_ERROR( "BackgroundChild::Startup() was never called"); return nullptr; } if (NS_IsMainThread() && ChildImpl::sShutdownHasStarted) { return nullptr; } auto threadLocalInfo = NS_IsMainThread() ? mMainThreadInfo : static_cast( PR_GetThreadPrivate(mThreadLocalIndex)); if (!threadLocalInfo) { auto newInfo = MakeUnique(); if (NS_IsMainThread()) { mMainThreadInfo = newInfo.get(); } else { if (PR_SetThreadPrivate(mThreadLocalIndex, newInfo.get()) != PR_SUCCESS) { CRASH_IN_CHILD_PROCESS("PR_SetThreadPrivate failed!"); return nullptr; } } threadLocalInfo = newInfo.release(); } PBackgroundChild* bgChild = GetFromThreadInfo(aMainEventTarget, threadLocalInfo); if (bgChild) { return bgChild; } RefPtr actor; mCreateActorFunc(threadLocalInfo, mThreadLocalIndex, aMainEventTarget, getter_AddRefs(actor)); return actor; } private: // This is only modified on the main thread. It is the thread-local index // that we use to store the BackgroundChild for each thread. unsigned int mThreadLocalIndex; // On the main thread, we store TLS in this global instead of in // mThreadLocalIndex. That way, cooperative main threads all share the same // thread info. ThreadLocalInfo* mMainThreadInfo; ActorCreateFunc mCreateActorFunc; }; // For PBackground between parent and content process. static ThreadInfoWrapper sParentAndContentProcessThreadInfo; // For PBackground between socket and content process. static ThreadInfoWrapper sSocketAndContentProcessThreadInfo; // For PBackground between socket and parent process. static ThreadInfoWrapper sSocketAndParentProcessThreadInfo; // This is only modified on the main thread. It prevents us from trying to // create the background thread after application shutdown has started. static bool sShutdownHasStarted; #if defined(DEBUG) || !defined(RELEASE_OR_BETA) nsISerialEventTarget* mOwningEventTarget; #endif #ifdef DEBUG bool mActorWasAlive; bool mActorDestroyed; #endif public: static void Shutdown(); void AssertIsOnOwningThread() { THREADSAFETY_ASSERT(mOwningEventTarget); #ifdef RELEASE_OR_BETA DebugOnly current; #else bool current; #endif THREADSAFETY_ASSERT( NS_SUCCEEDED(mOwningEventTarget->IsOnCurrentThread(¤t))); THREADSAFETY_ASSERT(current); } void AssertActorDestroyed() { MOZ_ASSERT(mActorDestroyed, "ChildImpl::ActorDestroy not called in time"); } explicit ChildImpl() #if defined(DEBUG) || !defined(RELEASE_OR_BETA) : mOwningEventTarget(GetCurrentSerialEventTarget()) #endif #ifdef DEBUG , mActorWasAlive(false), mActorDestroyed(false) #endif { AssertIsOnOwningThread(); } void SetActorAlive() { AssertIsOnOwningThread(); MOZ_ASSERT(!mActorWasAlive); MOZ_ASSERT(!mActorDestroyed); #ifdef DEBUG mActorWasAlive = true; #endif } NS_INLINE_DECL_REFCOUNTING(ChildImpl, override) private: // Forwarded from BackgroundChild. static void Startup(); // Forwarded from BackgroundChild. static PBackgroundChild* GetForCurrentThread(); // Helper function for getting PBackgroundChild from thread info. static PBackgroundChild* GetFromThreadInfo(nsIEventTarget* aMainEventTarget, ThreadLocalInfo* aThreadLocalInfo); // Forwarded from BackgroundChild. static PBackgroundChild* GetOrCreateForCurrentThread( nsIEventTarget* aMainEventTarget); // Forwarded from BackgroundChild. static PBackgroundChild* GetOrCreateSocketActorForCurrentThread( nsIEventTarget* aMainEventTarget); // Forwarded from BackgroundChild. static PBackgroundChild* GetOrCreateForSocketParentBridgeForCurrentThread( nsIEventTarget* aMainEventTarget); static void CloseForCurrentThread(); // Forwarded from BackgroundChildImpl. static BackgroundChildImpl::ThreadLocal* GetThreadLocalForCurrentThread(); static void ThreadLocalDestructor(void* aThreadLocal); // This class is reference counted. ~ChildImpl() { MOZ_ASSERT_IF(mActorWasAlive, mActorDestroyed); } // Only called by IPDL. virtual void ActorDestroy(ActorDestroyReason aWhy) override; }; // ----------------------------------------------------------------------------- // ParentImpl Helper Declarations // ----------------------------------------------------------------------------- class ParentImpl::ShutdownObserver final : public nsIObserver { public: ShutdownObserver() { AssertIsOnMainThread(); } NS_DECL_ISUPPORTS NS_DECL_NSIOBSERVER private: ~ShutdownObserver() { AssertIsOnMainThread(); } }; class ParentImpl::CreateActorHelper final : public Runnable { mozilla::Monitor mMonitor; RefPtr mParentActor; nsCOMPtr mThread; nsresult mMainThreadResultCode; bool mWaiting; public: explicit CreateActorHelper() : Runnable("Background::ParentImpl::CreateActorHelper"), mMonitor("CreateActorHelper::mMonitor"), mMainThreadResultCode(NS_OK), mWaiting(true) { AssertIsInMainOrSocketProcess(); AssertIsNotOnMainThread(); } nsresult BlockAndGetResults(nsIEventTarget* aMainEventTarget, RefPtr& aParentActor, nsCOMPtr& aThread); private: ~CreateActorHelper() { AssertIsInMainOrSocketProcess(); } nsresult RunOnMainThread(); NS_DECL_NSIRUNNABLE }; // ----------------------------------------------------------------------------- // ChildImpl Helper Declarations // ----------------------------------------------------------------------------- class ChildImpl::ShutdownObserver final : public nsIObserver { public: ShutdownObserver() { AssertIsOnMainThread(); } NS_DECL_ISUPPORTS NS_DECL_NSIOBSERVER private: ~ShutdownObserver() { AssertIsOnMainThread(); } }; class ChildImpl::SendInitBackgroundRunnable final : public DiscardableRunnable { nsCOMPtr mOwningEventTarget; RefPtr mWorkerRef; Endpoint mParent; mozilla::Mutex mMutex; bool mSentInitBackground; std::function&& aParent)> mSendInitfunc; unsigned int mThreadLocalIndex; public: static already_AddRefed Create( Endpoint&& aParent, std::function&& aParent)>&& aFunc, unsigned int aThreadLocalIndex); void ClearEventTarget() { mWorkerRef = nullptr; mozilla::MutexAutoLock lock(mMutex); mOwningEventTarget = nullptr; } private: explicit SendInitBackgroundRunnable( Endpoint&& aParent, std::function&& aParent)>&& aFunc, unsigned int aThreadLocalIndex) : DiscardableRunnable( "Background::ChildImpl::SendInitBackgroundRunnable"), mOwningEventTarget(GetCurrentSerialEventTarget()), mParent(std::move(aParent)), mMutex("SendInitBackgroundRunnable::mMutex"), mSentInitBackground(false), mSendInitfunc(std::move(aFunc)), mThreadLocalIndex(aThreadLocalIndex) {} ~SendInitBackgroundRunnable() = default; NS_DECL_NSIRUNNABLE }; } // namespace namespace mozilla { namespace ipc { bool IsOnBackgroundThread() { return ParentImpl::IsOnBackgroundThread(); } #ifdef DEBUG void AssertIsOnBackgroundThread() { ParentImpl::AssertIsOnBackgroundThread(); } #endif // DEBUG } // namespace ipc } // namespace mozilla // ----------------------------------------------------------------------------- // BackgroundParent Public Methods // ----------------------------------------------------------------------------- // static bool BackgroundParent::IsOtherProcessActor( PBackgroundParent* aBackgroundActor) { return ParentImpl::IsOtherProcessActor(aBackgroundActor); } // static already_AddRefed BackgroundParent::GetContentParent( PBackgroundParent* aBackgroundActor) { return ParentImpl::GetContentParent(aBackgroundActor); } // static intptr_t BackgroundParent::GetRawContentParentForComparison( PBackgroundParent* aBackgroundActor) { return ParentImpl::GetRawContentParentForComparison(aBackgroundActor); } // static uint64_t BackgroundParent::GetChildID(PBackgroundParent* aBackgroundActor) { return ParentImpl::GetChildID(aBackgroundActor); } // static bool BackgroundParent::GetLiveActorArray( PBackgroundParent* aBackgroundActor, nsTArray& aLiveActorArray) { return ParentImpl::GetLiveActorArray(aBackgroundActor, aLiveActorArray); } // static bool BackgroundParent::Alloc(ContentParent* aContent, Endpoint&& aEndpoint) { return ParentImpl::Alloc(aContent, std::move(aEndpoint)); } // ----------------------------------------------------------------------------- // BackgroundChild Public Methods // ----------------------------------------------------------------------------- // static void BackgroundChild::Startup() { ChildImpl::Startup(); } // static PBackgroundChild* BackgroundChild::GetForCurrentThread() { return ChildImpl::GetForCurrentThread(); } // static PBackgroundChild* BackgroundChild::GetOrCreateForCurrentThread( nsIEventTarget* aMainEventTarget) { return ChildImpl::GetOrCreateForCurrentThread(aMainEventTarget); } // static PBackgroundChild* BackgroundChild::GetOrCreateSocketActorForCurrentThread( nsIEventTarget* aMainEventTarget) { return ChildImpl::GetOrCreateSocketActorForCurrentThread(aMainEventTarget); } // static PBackgroundChild* BackgroundChild::GetOrCreateForSocketParentBridgeForCurrentThread( nsIEventTarget* aMainEventTarget) { return ChildImpl::GetOrCreateForSocketParentBridgeForCurrentThread( aMainEventTarget); } // static void BackgroundChild::CloseForCurrentThread() { ChildImpl::CloseForCurrentThread(); } // ----------------------------------------------------------------------------- // BackgroundChildImpl Public Methods // ----------------------------------------------------------------------------- // static BackgroundChildImpl::ThreadLocal* BackgroundChildImpl::GetThreadLocalForCurrentThread() { return ChildImpl::GetThreadLocalForCurrentThread(); } // ----------------------------------------------------------------------------- // ParentImpl Static Members // ----------------------------------------------------------------------------- StaticRefPtr ParentImpl::sBackgroundThread; nsTArray* ParentImpl::sLiveActorsForBackgroundThread; StaticRefPtr ParentImpl::sShutdownTimer; Atomic ParentImpl::sBackgroundPRThread; uint64_t ParentImpl::sLiveActorCount = 0; bool ParentImpl::sShutdownObserverRegistered = false; bool ParentImpl::sShutdownHasStarted = false; // ----------------------------------------------------------------------------- // ChildImpl Static Members // ----------------------------------------------------------------------------- static void ParentContentActorCreateFunc( ChildImpl::ThreadLocalInfo* aThreadLocalInfo, unsigned int aThreadLocalIndex, nsIEventTarget* aMainEventTarget, ChildImpl** aOutput) { if (XRE_IsParentProcess()) { RefPtr strongActor = ParentImpl::CreateActorForSameProcess(aMainEventTarget); if (NS_WARN_IF(!strongActor)) { return; } aThreadLocalInfo->mActor = strongActor; strongActor.forget(aOutput); return; } RefPtr content = ContentChild::GetSingleton(); MOZ_ASSERT(content); if (content->IsShuttingDown()) { // The transport for ContentChild is shut down and can't be used to open // PBackground. return; } Endpoint parent; Endpoint child; nsresult rv; rv = PBackground::CreateEndpoints(content->OtherPid(), base::GetCurrentProcId(), &parent, &child); if (NS_FAILED(rv)) { NS_WARNING("Failed to create top level actor!"); return; } RefPtr runnable; if (!NS_IsMainThread()) { runnable = ChildImpl::SendInitBackgroundRunnable::Create( std::move(parent), [](Endpoint&& aParent) { RefPtr content = ContentChild::GetSingleton(); MOZ_ASSERT(content); if (!content->SendInitBackground(std::move(aParent))) { NS_WARNING("Failed to create top level actor!"); } }, aThreadLocalIndex); if (!runnable) { return; } } RefPtr strongActor = new ChildImpl(); if (!child.Bind(strongActor)) { CRASH_IN_CHILD_PROCESS("Failed to bind ChildImpl!"); return; } strongActor->SetActorAlive(); if (NS_IsMainThread()) { if (!content->SendInitBackground(std::move(parent))) { NS_WARNING("Failed to create top level actor!"); return; } } else { if (aMainEventTarget) { MOZ_ALWAYS_SUCCEEDS( aMainEventTarget->Dispatch(runnable, NS_DISPATCH_NORMAL)); } else { MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable)); } aThreadLocalInfo->mSendInitBackgroundRunnable = runnable; } aThreadLocalInfo->mActor = strongActor; strongActor.forget(aOutput); } ChildImpl::ThreadInfoWrapper ChildImpl::sParentAndContentProcessThreadInfo( ParentContentActorCreateFunc); static void SocketContentActorCreateFunc( ChildImpl::ThreadLocalInfo* aThreadLocalInfo, unsigned int aThreadLocalIndex, nsIEventTarget* aMainEventTarget, ChildImpl** aOutput) { RefPtr bridgeChild = SocketProcessBridgeChild::GetSingleton(); if (!bridgeChild || bridgeChild->IsShuttingDown()) { // The transport for SocketProcessBridgeChild is shut down // and can't be used to open PBackground. return; } Endpoint parent; Endpoint child; nsresult rv; rv = PBackground::CreateEndpoints(bridgeChild->SocketProcessPid(), base::GetCurrentProcId(), &parent, &child); if (NS_FAILED(rv)) { NS_WARNING("Failed to create top level actor!"); return; } RefPtr runnable; if (!NS_IsMainThread()) { runnable = ChildImpl::SendInitBackgroundRunnable::Create( std::move(parent), [](Endpoint&& aParent) { RefPtr bridgeChild = SocketProcessBridgeChild::GetSingleton(); if (!bridgeChild->SendInitBackground(std::move(aParent))) { NS_WARNING("Failed to create top level actor!"); } }, aThreadLocalIndex); if (!runnable) { return; } } RefPtr strongActor = new ChildImpl(); if (!child.Bind(strongActor)) { CRASH_IN_CHILD_PROCESS("Failed to bind ChildImpl!"); return; } strongActor->SetActorAlive(); if (NS_IsMainThread()) { if (!bridgeChild->SendInitBackground(std::move(parent))) { NS_WARNING("Failed to create top level actor!"); // Need to close the IPC channel before ChildImpl getting deleted. strongActor->Close(); strongActor->AssertActorDestroyed(); return; } } else { if (aMainEventTarget) { MOZ_ALWAYS_SUCCEEDS( aMainEventTarget->Dispatch(runnable, NS_DISPATCH_NORMAL)); } else { MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable)); } aThreadLocalInfo->mSendInitBackgroundRunnable = runnable; } aThreadLocalInfo->mActor = strongActor; strongActor.forget(aOutput); } ChildImpl::ThreadInfoWrapper ChildImpl::sSocketAndContentProcessThreadInfo( SocketContentActorCreateFunc); static void SocketParentActorCreateFunc( ChildImpl::ThreadLocalInfo* aThreadLocalInfo, unsigned int aThreadLocalIndex, nsIEventTarget* aMainEventTarget, ChildImpl** aOutput) { SocketProcessChild* socketChild = SocketProcessChild::GetSingleton(); if (!socketChild || socketChild->IsShuttingDown()) { return; } Endpoint parent; Endpoint child; nsresult rv; rv = PBackground::CreateEndpoints(socketChild->OtherPid(), base::GetCurrentProcId(), &parent, &child); if (NS_FAILED(rv)) { NS_WARNING("Failed to create top level actor!"); return; } RefPtr runnable; if (!NS_IsMainThread()) { runnable = ChildImpl::SendInitBackgroundRunnable::Create( std::move(parent), [](Endpoint&& aParent) { SocketProcessChild* socketChild = SocketProcessChild::GetSingleton(); MOZ_ASSERT(socketChild); if (!socketChild->SendInitBackground(std::move(aParent))) { MOZ_CRASH("Failed to create top level actor!"); } }, aThreadLocalIndex); if (!runnable) { return; } } RefPtr strongActor = new ChildImpl(); if (!child.Bind(strongActor)) { CRASH_IN_CHILD_PROCESS("Failed to bind ChildImpl!"); return; } strongActor->SetActorAlive(); if (NS_IsMainThread()) { if (!socketChild->SendInitBackground(std::move(parent))) { NS_WARNING("Failed to create top level actor!"); return; } } else { if (aMainEventTarget) { MOZ_ALWAYS_SUCCEEDS( aMainEventTarget->Dispatch(runnable, NS_DISPATCH_NORMAL)); } else { MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable)); } aThreadLocalInfo->mSendInitBackgroundRunnable = runnable; } aThreadLocalInfo->mActor = strongActor; strongActor.forget(aOutput); } ChildImpl::ThreadInfoWrapper ChildImpl::sSocketAndParentProcessThreadInfo( SocketParentActorCreateFunc); bool ChildImpl::sShutdownHasStarted = false; // ----------------------------------------------------------------------------- // ParentImpl Implementation // ----------------------------------------------------------------------------- // static bool ParentImpl::IsOtherProcessActor(PBackgroundParent* aBackgroundActor) { AssertIsOnBackgroundThread(); MOZ_ASSERT(aBackgroundActor); return static_cast(aBackgroundActor)->mIsOtherProcessActor; } // static already_AddRefed ParentImpl::GetContentParent( PBackgroundParent* aBackgroundActor) { AssertIsOnBackgroundThread(); MOZ_ASSERT(aBackgroundActor); auto actor = static_cast(aBackgroundActor); if (actor->mActorDestroyed) { MOZ_ASSERT(false, "GetContentParent called after ActorDestroy was called!"); return nullptr; } if (actor->mContent) { // We need to hand out a reference to our ContentParent but we also need to // keep the one we have. We can't call AddRef here because ContentParent is // not threadsafe so instead we dispatch a runnable to the main thread to do // it for us. This is safe since we are guaranteed that our AddRef runnable // will run before the reference we hand out can be released, and the // ContentParent can't die as long as the existing reference is maintained. MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(NewNonOwningRunnableMethod( "ContentParent::AddRef", actor->mContent, &ContentParent::AddRef))); } return already_AddRefed(actor->mContent.get()); } // static intptr_t ParentImpl::GetRawContentParentForComparison( PBackgroundParent* aBackgroundActor) { AssertIsOnBackgroundThread(); MOZ_ASSERT(aBackgroundActor); auto actor = static_cast(aBackgroundActor); if (actor->mActorDestroyed) { MOZ_ASSERT(false, "GetRawContentParentForComparison called after ActorDestroy was " "called!"); return intptr_t(-1); } return intptr_t(static_cast(actor->mContent.get())); } // static uint64_t ParentImpl::GetChildID(PBackgroundParent* aBackgroundActor) { AssertIsOnBackgroundThread(); MOZ_ASSERT(aBackgroundActor); auto actor = static_cast(aBackgroundActor); if (actor->mActorDestroyed) { MOZ_ASSERT(false, "GetContentParent called after ActorDestroy was called!"); return 0; } if (actor->mContent) { return actor->mContent->ChildID(); } return 0; } // static bool ParentImpl::GetLiveActorArray( PBackgroundParent* aBackgroundActor, nsTArray& aLiveActorArray) { AssertIsOnBackgroundThread(); MOZ_ASSERT(aBackgroundActor); MOZ_ASSERT(aLiveActorArray.IsEmpty()); auto actor = static_cast(aBackgroundActor); if (actor->mActorDestroyed) { MOZ_ASSERT(false, "GetLiveActorArray called after ActorDestroy was called!"); return false; } if (!actor->mLiveActorArray) { return true; } for (ParentImpl* liveActor : *actor->mLiveActorArray) { aLiveActorArray.AppendElement(liveActor); } return true; } // static bool ParentImpl::Alloc(ContentParent* aContent, Endpoint&& aEndpoint) { AssertIsInMainOrSocketProcess(); AssertIsOnMainThread(); MOZ_ASSERT(aEndpoint.IsValid()); if (!sBackgroundThread && !CreateBackgroundThread()) { NS_WARNING("Failed to create background thread!"); return false; } MOZ_ASSERT(sLiveActorsForBackgroundThread); sLiveActorCount++; RefPtr actor = new ParentImpl(aContent); if (NS_FAILED(sBackgroundThread->Dispatch(NS_NewRunnableFunction( "Background::ParentImpl::ConnectActorRunnable", [actor = std::move(actor), endpoint = std::move(aEndpoint), liveActorArray = sLiveActorsForBackgroundThread]() mutable { MOZ_ASSERT(endpoint.IsValid()); MOZ_ASSERT(liveActorArray); // Transfer ownership to this thread. If Open() fails then we will // release this reference in Destroy. ParentImpl* actorTmp; actor.forget(&actorTmp); if (!endpoint.Bind(actorTmp)) { actorTmp->Destroy(); return; } actorTmp->SetLiveActorArray(liveActorArray); })))) { NS_WARNING("Failed to dispatch connect runnable!"); MOZ_ASSERT(sLiveActorCount); sLiveActorCount--; } return true; } // static already_AddRefed ParentImpl::CreateActorForSameProcess( nsIEventTarget* aMainEventTarget) { AssertIsInMainProcess(); RefPtr parentActor; nsCOMPtr backgroundThread; if (NS_IsMainThread()) { if (!sBackgroundThread && !CreateBackgroundThread()) { NS_WARNING("Failed to create background thread!"); return nullptr; } MOZ_ASSERT(!sShutdownHasStarted); sLiveActorCount++; parentActor = new ParentImpl(); backgroundThread = sBackgroundThread.get(); } else { RefPtr helper = new CreateActorHelper(); nsresult rv = helper->BlockAndGetResults(aMainEventTarget, parentActor, backgroundThread); if (NS_WARN_IF(NS_FAILED(rv))) { return nullptr; } } RefPtr childActor = new ChildImpl(); MessageChannel* parentChannel = parentActor->GetIPCChannel(); MOZ_ASSERT(parentChannel); if (!childActor->Open(parentChannel, backgroundThread, ChildSide)) { NS_WARNING("Failed to open ChildImpl!"); // Can't release it here, we will release this reference in Destroy. ParentImpl* actor; parentActor.forget(&actor); actor->Destroy(); return nullptr; } childActor->SetActorAlive(); // Make sure the parent knows it is same process. parentActor->SetOtherProcessId(base::GetCurrentProcId()); // Now that Open() has succeeded transfer the ownership of the actors to IPDL. Unused << parentActor.forget(); return childActor.forget(); } // static bool ParentImpl::CreateBackgroundThread() { AssertIsInMainOrSocketProcess(); AssertIsOnMainThread(); MOZ_ASSERT(!sBackgroundThread); MOZ_ASSERT(!sLiveActorsForBackgroundThread); if (sShutdownHasStarted) { NS_WARNING( "Trying to create background thread after shutdown has " "already begun!"); return false; } nsCOMPtr newShutdownTimer; if (!sShutdownTimer) { newShutdownTimer = NS_NewTimer(); if (!newShutdownTimer) { return false; } } if (!sShutdownObserverRegistered) { nsCOMPtr obs = services::GetObserverService(); if (NS_WARN_IF(!obs)) { return false; } nsCOMPtr observer = new ShutdownObserver(); nsresult rv = obs->AddObserver( observer, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, false); if (NS_WARN_IF(NS_FAILED(rv))) { return false; } sShutdownObserverRegistered = true; } nsCOMPtr thread; if (NS_FAILED(NS_NewNamedThread( "IPDL Background", getter_AddRefs(thread), NS_NewRunnableFunction( "Background::ParentImpl::CreateBackgroundThreadRunnable", []() { DebugOnly oldBackgroundThread = sBackgroundPRThread.exchange(PR_GetCurrentThread()); MOZ_ASSERT_IF(oldBackgroundThread, PR_GetCurrentThread() != oldBackgroundThread); })))) { NS_WARNING("NS_NewNamedThread failed!"); return false; } sBackgroundThread = thread.forget(); sLiveActorsForBackgroundThread = new nsTArray(1); if (!sShutdownTimer) { MOZ_ASSERT(newShutdownTimer); sShutdownTimer = newShutdownTimer; } return true; } // static void ParentImpl::ShutdownBackgroundThread() { AssertIsInMainOrSocketProcess(); AssertIsOnMainThread(); MOZ_ASSERT(sShutdownHasStarted); MOZ_ASSERT_IF(!sBackgroundThread, !sLiveActorCount); MOZ_ASSERT_IF(sBackgroundThread, sShutdownTimer); nsCOMPtr shutdownTimer = sShutdownTimer.get(); sShutdownTimer = nullptr; if (sBackgroundThread) { nsCOMPtr thread = sBackgroundThread.get(); sBackgroundThread = nullptr; UniquePtr> liveActors(sLiveActorsForBackgroundThread); sLiveActorsForBackgroundThread = nullptr; MOZ_ASSERT_IF(!sShutdownHasStarted, !sLiveActorCount); if (sLiveActorCount) { // We need to spin the event loop while we wait for all the actors to be // cleaned up. We also set a timeout to force-kill any hanging actors. TimerCallbackClosure closure(thread, liveActors.get()); MOZ_ALWAYS_SUCCEEDS(shutdownTimer->InitWithNamedFuncCallback( &ShutdownTimerCallback, &closure, kShutdownTimerDelayMS, nsITimer::TYPE_ONE_SHOT, "ParentImpl::ShutdownTimerCallback")); SpinEventLoopUntil([&]() { return !sLiveActorCount; }); MOZ_ASSERT(liveActors->IsEmpty()); MOZ_ALWAYS_SUCCEEDS(shutdownTimer->Cancel()); } // Dispatch this runnable to unregister the PR thread from the profiler. MOZ_ALWAYS_SUCCEEDS(thread->Dispatch(NS_NewRunnableFunction( "Background::ParentImpl::ShutdownBackgroundThreadRunnable", []() { // It is possible that another background thread was created while // this thread was shutting down. In that case we can't assert // anything about sBackgroundPRThread and we should not modify it // here. sBackgroundPRThread.compareExchange(PR_GetCurrentThread(), nullptr); }))); MOZ_ALWAYS_SUCCEEDS(thread->Shutdown()); } } // static void ParentImpl::ShutdownTimerCallback(nsITimer* aTimer, void* aClosure) { AssertIsInMainOrSocketProcess(); AssertIsOnMainThread(); MOZ_ASSERT(sShutdownHasStarted); MOZ_ASSERT(sLiveActorCount); auto closure = static_cast(aClosure); MOZ_ASSERT(closure); // Don't let the stack unwind until the ForceCloseBackgroundActorsRunnable has // finished. sLiveActorCount++; InvokeAsync(closure->mThread, __func__, [liveActors = closure->mLiveActors]() { MOZ_ASSERT(liveActors); if (!liveActors->IsEmpty()) { // Copy the array since calling Close() could mutate the // actual array. nsTArray actorsToClose(liveActors->Clone()); for (ParentImpl* actor : actorsToClose) { actor->Close(); } } return GenericPromise::CreateAndResolve(true, __func__); }) ->Then(GetCurrentSerialEventTarget(), __func__, []() { MOZ_ASSERT(sLiveActorCount); sLiveActorCount--; }); } void ParentImpl::Destroy() { // May be called on any thread! AssertIsInMainOrSocketProcess(); MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread( NewNonOwningRunnableMethod("ParentImpl::MainThreadActorDestroy", this, &ParentImpl::MainThreadActorDestroy))); } void ParentImpl::MainThreadActorDestroy() { AssertIsInMainOrSocketProcess(); AssertIsOnMainThread(); MOZ_ASSERT_IF(!mIsOtherProcessActor, !mContent); mContent = nullptr; MOZ_ASSERT(sLiveActorCount); sLiveActorCount--; // This may be the last reference! Release(); } void ParentImpl::ActorDestroy(ActorDestroyReason aWhy) { AssertIsInMainOrSocketProcess(); AssertIsOnBackgroundThread(); MOZ_ASSERT(!mActorDestroyed); MOZ_ASSERT_IF(mIsOtherProcessActor, mLiveActorArray); BackgroundParentImpl::ActorDestroy(aWhy); mActorDestroyed = true; if (mLiveActorArray) { MOZ_ALWAYS_TRUE(mLiveActorArray->RemoveElement(this)); mLiveActorArray = nullptr; } // This is tricky. We should be able to call Destroy() here directly because // we're not going to touch 'this' or our MessageChannel any longer on this // thread. Destroy() dispatches the MainThreadActorDestroy runnable and when // it runs it will destroy 'this' and our associated MessageChannel. However, // IPDL is about to call MessageChannel::Clear() on this thread! To avoid // racing with the main thread we must ensure that the MessageChannel lives // long enough to be cleared in this call stack. MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(NewNonOwningRunnableMethod( "ParentImpl::Destroy", this, &ParentImpl::Destroy))); } NS_IMPL_ISUPPORTS(ParentImpl::ShutdownObserver, nsIObserver) NS_IMETHODIMP ParentImpl::ShutdownObserver::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) { AssertIsInMainOrSocketProcess(); AssertIsOnMainThread(); MOZ_ASSERT(!sShutdownHasStarted); MOZ_ASSERT(!strcmp(aTopic, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID)); sShutdownHasStarted = true; // Do this first before calling (and spinning the event loop in) // ShutdownBackgroundThread(). ChildImpl::Shutdown(); ShutdownBackgroundThread(); return NS_OK; } nsresult ParentImpl::CreateActorHelper::BlockAndGetResults( nsIEventTarget* aMainEventTarget, RefPtr& aParentActor, nsCOMPtr& aThread) { AssertIsNotOnMainThread(); if (aMainEventTarget) { MOZ_ALWAYS_SUCCEEDS(aMainEventTarget->Dispatch(this, NS_DISPATCH_NORMAL)); } else { MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this)); } mozilla::MonitorAutoLock lock(mMonitor); while (mWaiting) { lock.Wait(); } if (NS_WARN_IF(NS_FAILED(mMainThreadResultCode))) { return mMainThreadResultCode; } aParentActor = std::move(mParentActor); aThread = std::move(mThread); return NS_OK; } nsresult ParentImpl::CreateActorHelper::RunOnMainThread() { AssertIsOnMainThread(); if (!sBackgroundThread && !CreateBackgroundThread()) { NS_WARNING("Failed to create background thread!"); return NS_ERROR_FAILURE; } MOZ_ASSERT(!sShutdownHasStarted); sLiveActorCount++; mParentActor = new ParentImpl(); mThread = sBackgroundThread; return NS_OK; } NS_IMETHODIMP ParentImpl::CreateActorHelper::Run() { AssertIsOnMainThread(); nsresult rv = RunOnMainThread(); if (NS_WARN_IF(NS_FAILED(rv))) { mMainThreadResultCode = rv; } mozilla::MonitorAutoLock lock(mMonitor); MOZ_ASSERT(mWaiting); mWaiting = false; lock.Notify(); return NS_OK; } // ----------------------------------------------------------------------------- // ChildImpl Implementation // ----------------------------------------------------------------------------- // static void ChildImpl::Startup() { // This happens on the main thread but before XPCOM has started so we can't // assert that we're being called on the main thread here. sParentAndContentProcessThreadInfo.Startup(); sSocketAndContentProcessThreadInfo.Startup(); sSocketAndParentProcessThreadInfo.Startup(); nsCOMPtr observerService = services::GetObserverService(); MOZ_RELEASE_ASSERT(observerService); nsCOMPtr observer = new ShutdownObserver(); nsresult rv = observerService->AddObserver( observer, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, false); MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv)); } // static void ChildImpl::Shutdown() { AssertIsOnMainThread(); sParentAndContentProcessThreadInfo.Shutdown(); sSocketAndContentProcessThreadInfo.Shutdown(); sSocketAndParentProcessThreadInfo.Shutdown(); sShutdownHasStarted = true; } // static PBackgroundChild* ChildImpl::GetForCurrentThread() { MOZ_ASSERT(sParentAndContentProcessThreadInfo.mThreadLocalIndex != kBadThreadLocalIndex); auto threadLocalInfo = NS_IsMainThread() ? sParentAndContentProcessThreadInfo.mMainThreadInfo : static_cast(PR_GetThreadPrivate( sParentAndContentProcessThreadInfo.mThreadLocalIndex)); if (!threadLocalInfo) { return nullptr; } return threadLocalInfo->mActor; } /* static */ PBackgroundChild* ChildImpl::GetFromThreadInfo( nsIEventTarget* aMainEventTarget, ThreadLocalInfo* aThreadLocalInfo) { MOZ_ASSERT(aThreadLocalInfo); if (aThreadLocalInfo->mActor) { RefPtr& runnable = aThreadLocalInfo->mSendInitBackgroundRunnable; if (aMainEventTarget && runnable) { // The SendInitBackgroundRunnable was already dispatched to the main // thread to finish initialization of a new background child actor. // However, the caller passed a custom main event target which indicates // that synchronous blocking of the main thread is happening (done by // creating a nested event target and spinning the event loop). // It can happen that the SendInitBackgroundRunnable didn't have a chance // to run before the synchronous blocking has occured. Unblocking of the // main thread can depend on an IPC message received on this thread, so // we have to dispatch the SendInitBackgroundRunnable to the custom main // event target too, otherwise IPC will be only queueing messages on this // thread. The runnable will run twice in the end, but that's a harmless // race between the main and nested event queue of the main thread. // There's a guard in the runnable implementation for calling // SendInitBackground only once. MOZ_ALWAYS_SUCCEEDS( aMainEventTarget->Dispatch(runnable, NS_DISPATCH_NORMAL)); } return aThreadLocalInfo->mActor; } return nullptr; } /* static */ PBackgroundChild* ChildImpl::GetOrCreateForCurrentThread( nsIEventTarget* aMainEventTarget) { return sParentAndContentProcessThreadInfo.GetOrCreateForCurrentThread( aMainEventTarget); } /* static */ PBackgroundChild* ChildImpl::GetOrCreateSocketActorForCurrentThread( nsIEventTarget* aMainEventTarget) { return sSocketAndContentProcessThreadInfo.GetOrCreateForCurrentThread( aMainEventTarget); } /* static */ PBackgroundChild* ChildImpl::GetOrCreateForSocketParentBridgeForCurrentThread( nsIEventTarget* aMainEventTarget) { return sSocketAndParentProcessThreadInfo.GetOrCreateForCurrentThread( aMainEventTarget); } // static void ChildImpl::CloseForCurrentThread() { MOZ_ASSERT(!NS_IsMainThread(), "PBackground for the main thread should be shut down via " "ChildImpl::Shutdown()."); sParentAndContentProcessThreadInfo.CloseForCurrentThread(); sSocketAndContentProcessThreadInfo.CloseForCurrentThread(); sSocketAndParentProcessThreadInfo.CloseForCurrentThread(); } // static BackgroundChildImpl::ThreadLocal* ChildImpl::GetThreadLocalForCurrentThread() { MOZ_ASSERT(sParentAndContentProcessThreadInfo.mThreadLocalIndex != kBadThreadLocalIndex, "BackgroundChild::Startup() was never called!"); auto threadLocalInfo = NS_IsMainThread() ? sParentAndContentProcessThreadInfo.mMainThreadInfo : static_cast(PR_GetThreadPrivate( sParentAndContentProcessThreadInfo.mThreadLocalIndex)); if (!threadLocalInfo) { return nullptr; } if (!threadLocalInfo->mConsumerThreadLocal) { threadLocalInfo->mConsumerThreadLocal = MakeUnique(); } return threadLocalInfo->mConsumerThreadLocal.get(); } // static void ChildImpl::ThreadLocalDestructor(void* aThreadLocal) { auto threadLocalInfo = static_cast(aThreadLocal); if (threadLocalInfo) { MOZ_ASSERT(threadLocalInfo->mClosed); if (threadLocalInfo->mActor) { threadLocalInfo->mActor->Close(); threadLocalInfo->mActor->AssertActorDestroyed(); } if (threadLocalInfo->mSendInitBackgroundRunnable) { threadLocalInfo->mSendInitBackgroundRunnable->ClearEventTarget(); } delete threadLocalInfo; } } void ChildImpl::ActorDestroy(ActorDestroyReason aWhy) { AssertIsOnOwningThread(); #ifdef DEBUG MOZ_ASSERT(!mActorDestroyed); mActorDestroyed = true; #endif BackgroundChildImpl::ActorDestroy(aWhy); } NS_IMPL_ISUPPORTS(ChildImpl::ShutdownObserver, nsIObserver) NS_IMETHODIMP ChildImpl::ShutdownObserver::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) { AssertIsOnMainThread(); MOZ_ASSERT(!strcmp(aTopic, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID)); ChildImpl::Shutdown(); return NS_OK; } // static already_AddRefed ChildImpl::SendInitBackgroundRunnable::Create( Endpoint&& aParent, std::function&& aParent)>&& aFunc, unsigned int aThreadLocalIndex) { MOZ_ASSERT(!NS_IsMainThread()); RefPtr runnable = new SendInitBackgroundRunnable( std::move(aParent), std::move(aFunc), aThreadLocalIndex); WorkerPrivate* workerPrivate = mozilla::dom::GetCurrentThreadWorkerPrivate(); if (!workerPrivate) { return runnable.forget(); } workerPrivate->AssertIsOnWorkerThread(); runnable->mWorkerRef = StrongWorkerRef::Create( workerPrivate, "ChildImpl::SendInitBackgroundRunnable"); if (NS_WARN_IF(!runnable->mWorkerRef)) { return nullptr; } return runnable.forget(); } NS_IMETHODIMP ChildImpl::SendInitBackgroundRunnable::Run() { if (NS_IsMainThread()) { if (mSentInitBackground) { return NS_OK; } mSentInitBackground = true; mSendInitfunc(std::move(mParent)); nsCOMPtr owningEventTarget; { mozilla::MutexAutoLock lock(mMutex); owningEventTarget = mOwningEventTarget; } if (!owningEventTarget) { return NS_OK; } nsresult rv = owningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } return NS_OK; } ClearEventTarget(); auto threadLocalInfo = static_cast(PR_GetThreadPrivate(mThreadLocalIndex)); if (!threadLocalInfo) { return NS_OK; } threadLocalInfo->mSendInitBackgroundRunnable = nullptr; return NS_OK; }