/* 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 "base/process_util.h" #include "mozilla/Assertions.h" #include "mozilla/Atomics.h" #include "mozilla/ClearOnShutdown.h" #include "mozilla/DebugOnly.h" #include "mozilla/Services.h" #include "mozilla/StaticPtr.h" #include "mozilla/unused.h" #include "mozilla/dom/ContentChild.h" #include "mozilla/dom/ContentParent.h" #include "mozilla/ipc/ProtocolTypes.h" #include "BackgroundChild.h" #include "BackgroundChildImpl.h" #include "BackgroundParent.h" #include "BackgroundParentImpl.h" #include "GeckoProfiler.h" #include "nsAutoPtr.h" #include "nsCOMPtr.h" #include "nsIEventTarget.h" #include "nsIIPCBackgroundChildCreateCallback.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" #ifdef RELEASE_BUILD #define THREADSAFETY_ASSERT MOZ_ASSERT #else #define THREADSAFETY_ASSERT MOZ_RELEASE_ASSERT #endif #define CRASH_IN_CHILD_PROCESS(_msg) \ do { \ if (IsMainProcess()) { \ MOZ_ASSERT(false, _msg); \ } else { \ MOZ_CRASH(_msg); \ } \ } \ while (0) using namespace mozilla; using namespace mozilla::ipc; using mozilla::dom::ContentChild; using mozilla::dom::ContentParent; namespace { // ----------------------------------------------------------------------------- // Utility Functions // ----------------------------------------------------------------------------- bool IsMainProcess() { static const bool isMainProcess = XRE_GetProcessType() == GeckoProcessType_Default; return isMainProcess; } bool IsChildProcess() { return !IsMainProcess(); } void AssertIsInMainProcess() { MOZ_ASSERT(IsMainProcess()); } void AssertIsInChildProcess() { MOZ_ASSERT(IsChildProcess()); } void AssertIsOnMainThread() { THREADSAFETY_ASSERT(NS_IsMainThread()); } // ----------------------------------------------------------------------------- // ParentImpl Declaration // ----------------------------------------------------------------------------- class ParentImpl MOZ_FINAL : public BackgroundParentImpl { friend class mozilla::ipc::BackgroundParent; public: class CreateCallback; private: class ShutdownObserver; class RequestMessageLoopRunnable; class ShutdownBackgroundThreadRunnable; class ForceCloseBackgroundActorsRunnable; class CreateCallbackRunnable; class ConnectActorRunnable; struct MOZ_STACK_CLASS TimerCallbackClosure { nsIThread* mThread; nsTArray* mLiveActors; TimerCallbackClosure(nsIThread* aThread, nsTArray* aLiveActors) : mThread(aThread), mLiveActors(aLiveActors) { AssertIsInMainProcess(); AssertIsOnMainThread(); MOZ_ASSERT(aThread); MOZ_ASSERT(aLiveActors); } }; // A handle that is invalid on any platform. static const ProcessHandle kInvalidProcessHandle; // 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 is null if the thread does not // exist or is shutting down. static MessageLoop* sBackgroundThreadMessageLoop; // 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; // This is only modified on the main thread. It is a FIFO queue for callbacks // waiting for the background thread to be created. static StaticAutoPtr>> sPendingCallbacks; // Only touched on the main thread, null if this is a same-process actor. nsRefPtr mContent; // mTransport is "owned" by this object but it must only be released on the // IPC thread. It's left as a raw pointer here to prevent accidentally // deleting it on the wrong thread. Only non-null for other-process actors. Transport* mTransport; // 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 bool CreateActorForSameProcess(CreateCallback* aCallback); static bool IsOnBackgroundThread() { return PR_GetCurrentThread() == sBackgroundPRThread; } static void AssertIsOnBackgroundThread() { THREADSAFETY_ASSERT(IsOnBackgroundThread()); } NS_INLINE_DECL_REFCOUNTING(ParentImpl) void Destroy(); private: // Forwarded from BackgroundParent. static bool IsOtherProcessActor(PBackgroundParent* aBackgroundActor); // Forwarded from BackgroundParent. static already_AddRefed GetContentParent(PBackgroundParent* aBackgroundActor); // Forwarded from BackgroundParent. static PBackgroundParent* Alloc(ContentParent* aContent, Transport* aTransport, ProcessId aOtherProcess); static bool CreateBackgroundThread(); static void ShutdownBackgroundThread(); static void ShutdownTimerCallback(nsITimer* aTimer, void* aClosure); // For same-process actors. ParentImpl() : mTransport(nullptr), mLiveActorArray(nullptr), mIsOtherProcessActor(false), mActorDestroyed(false) { AssertIsInMainProcess(); AssertIsOnMainThread(); SetOtherProcess(kInvalidProcessHandle); } // For other-process actors. ParentImpl(ContentParent* aContent, Transport* aTransport) : mContent(aContent), mTransport(aTransport), mLiveActorArray(nullptr), mIsOtherProcessActor(true), mActorDestroyed(false) { AssertIsInMainProcess(); AssertIsOnMainThread(); MOZ_ASSERT(aContent); MOZ_ASSERT(aTransport); } ~ParentImpl() { AssertIsInMainProcess(); AssertIsOnMainThread(); MOZ_ASSERT(!mContent); MOZ_ASSERT(!mTransport); } void MainThreadActorDestroy(); void SetLiveActorArray(nsTArray* aLiveActorArray) { AssertIsInMainProcess(); 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 IToplevelProtocol* CloneToplevel(const InfallibleTArray& aFds, ProcessHandle aPeerProcess, ProtocolCloneContext* aCtx) MOZ_OVERRIDE; virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; }; // ----------------------------------------------------------------------------- // ChildImpl Declaration // ----------------------------------------------------------------------------- class ChildImpl MOZ_FINAL : public BackgroundChildImpl { friend class mozilla::ipc::BackgroundChild; typedef base::ProcessId ProcessId; typedef mozilla::ipc::Transport Transport; class ShutdownObserver; class CreateActorRunnable; class ParentCreateCallback; class CreateCallbackRunnable; class OpenChildProcessActorRunnable; class OpenMainProcessActorRunnable; // A thread-local index that is not valid. static const unsigned int kBadThreadLocalIndex = static_cast(-1); // This is only modified on the main thread. It is the thread-local index that // we use to store the BackgroundChild for each thread. static unsigned int sThreadLocalIndex; struct ThreadLocalInfo { ThreadLocalInfo(nsIIPCBackgroundChildCreateCallback* aCallback) { mCallbacks.AppendElement(aCallback); } nsRefPtr mActor; nsTArray> mCallbacks; }; // This is only modified on the main thread. It is a FIFO queue for actors // that are in the process of construction. static StaticAutoPtr>> sPendingTargets; // 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; #ifdef RELEASE_BUILD DebugOnly mBoundThread; #else nsIThread* mBoundThread; #endif public: static bool OpenProtocolOnMainThread(nsIEventTarget* aEventTarget); static void Shutdown(); void AssertIsOnBoundThread() { THREADSAFETY_ASSERT(mBoundThread); #ifdef RELEASE_BUILD DebugOnly current; #else bool current; #endif THREADSAFETY_ASSERT( NS_SUCCEEDED(mBoundThread->IsOnCurrentThread(¤t))); THREADSAFETY_ASSERT(current); } ChildImpl() : mBoundThread(nullptr) { AssertIsOnMainThread(); } NS_INLINE_DECL_REFCOUNTING(ChildImpl) private: // Forwarded from BackgroundChild. static void Startup(); // Forwarded from BackgroundChild. static PBackgroundChild* Alloc(Transport* aTransport, ProcessId aOtherProcess); // Forwarded from BackgroundChild. static PBackgroundChild* GetForCurrentThread(); // Forwarded from BackgroundChild. static bool GetOrCreateForCurrentThread(nsIIPCBackgroundChildCreateCallback* aCallback); static void ThreadLocalDestructor(void* aThreadLocal) { auto threadLocalInfo = static_cast(aThreadLocal); if (threadLocalInfo) { if (threadLocalInfo->mActor) { threadLocalInfo->mActor->Close(); } delete threadLocalInfo; } } static void DispatchFailureCallback(nsIEventTarget* aEventTarget); // This class is reference counted. ~ChildImpl() { } void SetBoundThread() { THREADSAFETY_ASSERT(!mBoundThread); #if defined(DEBUG) || !defined(RELEASE_BUILD) mBoundThread = NS_GetCurrentThread(); #endif THREADSAFETY_ASSERT(mBoundThread); } // Only called by IPDL. virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; }; // ----------------------------------------------------------------------------- // ParentImpl Helper Declarations // ----------------------------------------------------------------------------- class ParentImpl::ShutdownObserver MOZ_FINAL : public nsIObserver { public: ShutdownObserver() { AssertIsOnMainThread(); } NS_DECL_ISUPPORTS NS_DECL_NSIOBSERVER private: ~ShutdownObserver() { AssertIsOnMainThread(); } }; class ParentImpl::RequestMessageLoopRunnable MOZ_FINAL : public nsRunnable { nsCOMPtr mTargetThread; MessageLoop* mMessageLoop; public: RequestMessageLoopRunnable(nsIThread* aTargetThread) : mTargetThread(aTargetThread), mMessageLoop(nullptr) { AssertIsInMainProcess(); AssertIsOnMainThread(); MOZ_ASSERT(aTargetThread); } NS_DECL_ISUPPORTS_INHERITED private: ~RequestMessageLoopRunnable() { } NS_DECL_NSIRUNNABLE }; class ParentImpl::ShutdownBackgroundThreadRunnable MOZ_FINAL : public nsRunnable { public: ShutdownBackgroundThreadRunnable() { AssertIsInMainProcess(); AssertIsOnMainThread(); } NS_DECL_ISUPPORTS_INHERITED private: ~ShutdownBackgroundThreadRunnable() { } NS_DECL_NSIRUNNABLE }; class ParentImpl::ForceCloseBackgroundActorsRunnable MOZ_FINAL : public nsRunnable { nsTArray* mActorArray; public: ForceCloseBackgroundActorsRunnable(nsTArray* aActorArray) : mActorArray(aActorArray) { AssertIsInMainProcess(); AssertIsOnMainThread(); MOZ_ASSERT(aActorArray); } NS_DECL_ISUPPORTS_INHERITED private: ~ForceCloseBackgroundActorsRunnable() { } NS_DECL_NSIRUNNABLE }; class ParentImpl::CreateCallbackRunnable MOZ_FINAL : public nsRunnable { nsRefPtr mCallback; public: CreateCallbackRunnable(CreateCallback* aCallback) : mCallback(aCallback) { AssertIsInMainProcess(); AssertIsOnMainThread(); MOZ_ASSERT(aCallback); } NS_DECL_ISUPPORTS_INHERITED private: ~CreateCallbackRunnable() { } NS_DECL_NSIRUNNABLE }; class ParentImpl::ConnectActorRunnable MOZ_FINAL : public nsRunnable { nsRefPtr mActor; Transport* mTransport; ProcessHandle mProcessHandle; nsTArray* mLiveActorArray; public: ConnectActorRunnable(ParentImpl* aActor, Transport* aTransport, ProcessHandle aProcessHandle, nsTArray* aLiveActorArray) : mActor(aActor), mTransport(aTransport), mProcessHandle(aProcessHandle), mLiveActorArray(aLiveActorArray) { AssertIsInMainProcess(); AssertIsOnMainThread(); MOZ_ASSERT(aActor); MOZ_ASSERT(aTransport); MOZ_ASSERT(aLiveActorArray); } NS_DECL_ISUPPORTS_INHERITED private: ~ConnectActorRunnable() { AssertIsInMainProcess(); } NS_DECL_NSIRUNNABLE }; class NS_NO_VTABLE ParentImpl::CreateCallback { public: NS_INLINE_DECL_REFCOUNTING(CreateCallback) virtual void Success(already_AddRefed aActor, MessageLoop* aMessageLoop) = 0; virtual void Failure() = 0; protected: virtual ~CreateCallback() { } }; // ----------------------------------------------------------------------------- // ChildImpl Helper Declarations // ----------------------------------------------------------------------------- class ChildImpl::ShutdownObserver MOZ_FINAL : public nsIObserver { public: ShutdownObserver() { AssertIsOnMainThread(); } NS_DECL_ISUPPORTS NS_DECL_NSIOBSERVER private: ~ShutdownObserver() { AssertIsOnMainThread(); } }; class ChildImpl::CreateActorRunnable MOZ_FINAL : public nsRunnable { nsCOMPtr mEventTarget; public: CreateActorRunnable() : mEventTarget(NS_GetCurrentThread()) { MOZ_ASSERT(mEventTarget); } NS_DECL_ISUPPORTS_INHERITED private: ~CreateActorRunnable() { } NS_DECL_NSIRUNNABLE }; class ChildImpl::ParentCreateCallback MOZ_FINAL : public ParentImpl::CreateCallback { nsCOMPtr mEventTarget; public: ParentCreateCallback(nsIEventTarget* aEventTarget) : mEventTarget(aEventTarget) { AssertIsInMainProcess(); AssertIsOnMainThread(); MOZ_ASSERT(aEventTarget); } private: ~ParentCreateCallback() { } virtual void Success(already_AddRefed aActor, MessageLoop* aMessageLoop) MOZ_OVERRIDE; virtual void Failure() MOZ_OVERRIDE; }; class ChildImpl::CreateCallbackRunnable : public nsRunnable { protected: nsRefPtr mActor; public: CreateCallbackRunnable(already_AddRefed&& aActor) : mActor(aActor) { // May be created on any thread! MOZ_ASSERT(mActor); } CreateCallbackRunnable() { // May be created on any thread! } NS_DECL_ISUPPORTS_INHERITED protected: virtual ~CreateCallbackRunnable(); static already_AddRefed GetNextCallback(); NS_DECL_NSIRUNNABLE }; class ChildImpl::OpenChildProcessActorRunnable MOZ_FINAL : public ChildImpl::CreateCallbackRunnable { nsAutoPtr mTransport; ProcessHandle mProcessHandle; public: OpenChildProcessActorRunnable(already_AddRefed&& aActor, Transport* aTransport, ProcessHandle aProcessHandle) : CreateCallbackRunnable(Move(aActor)), mTransport(aTransport), mProcessHandle(aProcessHandle) { AssertIsOnMainThread(); MOZ_ASSERT(aTransport); } NS_DECL_ISUPPORTS_INHERITED private: ~OpenChildProcessActorRunnable() { if (mTransport) { CRASH_IN_CHILD_PROCESS("Leaking transport!"); unused << mTransport.forget(); } } NS_DECL_NSIRUNNABLE }; class ChildImpl::OpenMainProcessActorRunnable MOZ_FINAL : public ChildImpl::CreateCallbackRunnable { nsRefPtr mParentActor; MessageLoop* mParentMessageLoop; public: OpenMainProcessActorRunnable(already_AddRefed&& aChildActor, already_AddRefed aParentActor, MessageLoop* aParentMessageLoop) : CreateCallbackRunnable(Move(aChildActor)), mParentActor(aParentActor), mParentMessageLoop(aParentMessageLoop) { AssertIsOnMainThread(); MOZ_ASSERT(mParentActor); MOZ_ASSERT(aParentMessageLoop); } NS_DECL_ISUPPORTS_INHERITED private: ~OpenMainProcessActorRunnable() { } NS_DECL_NSIRUNNABLE }; } // anonymous 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 PBackgroundParent* BackgroundParent::Alloc(ContentParent* aContent, Transport* aTransport, ProcessId aOtherProcess) { return ParentImpl::Alloc(aContent, aTransport, aOtherProcess); } // ----------------------------------------------------------------------------- // BackgroundChild Public Methods // ----------------------------------------------------------------------------- // static void BackgroundChild::Startup() { ChildImpl::Startup(); } // static PBackgroundChild* BackgroundChild::Alloc(Transport* aTransport, ProcessId aOtherProcess) { return ChildImpl::Alloc(aTransport, aOtherProcess); } // static PBackgroundChild* BackgroundChild::GetForCurrentThread() { return ChildImpl::GetForCurrentThread(); } // static bool BackgroundChild::GetOrCreateForCurrentThread( nsIIPCBackgroundChildCreateCallback* aCallback) { return ChildImpl::GetOrCreateForCurrentThread(aCallback); } // ----------------------------------------------------------------------------- // ParentImpl Static Members // ----------------------------------------------------------------------------- const ParentImpl::ProcessHandle ParentImpl::kInvalidProcessHandle = #ifdef XP_WIN ProcessHandle(INVALID_HANDLE_VALUE); #else ProcessHandle(-1); #endif StaticRefPtr ParentImpl::sBackgroundThread; nsTArray* ParentImpl::sLiveActorsForBackgroundThread; StaticRefPtr ParentImpl::sShutdownTimer; Atomic ParentImpl::sBackgroundPRThread; MessageLoop* ParentImpl::sBackgroundThreadMessageLoop = nullptr; uint64_t ParentImpl::sLiveActorCount = 0; bool ParentImpl::sShutdownObserverRegistered = false; bool ParentImpl::sShutdownHasStarted = false; StaticAutoPtr>> ParentImpl::sPendingCallbacks; // ----------------------------------------------------------------------------- // ChildImpl Static Members // ----------------------------------------------------------------------------- unsigned int ChildImpl::sThreadLocalIndex = kBadThreadLocalIndex; StaticAutoPtr>> ChildImpl::sPendingTargets; 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. nsCOMPtr runnable = NS_NewNonOwningRunnableMethod(actor->mContent, &ContentParent::AddRef); MOZ_ASSERT(runnable); MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable))); } return actor->mContent.get(); } // static PBackgroundParent* ParentImpl::Alloc(ContentParent* aContent, Transport* aTransport, ProcessId aOtherProcess) { AssertIsInMainProcess(); AssertIsOnMainThread(); MOZ_ASSERT(aTransport); ProcessHandle processHandle; if (!base::OpenProcessHandle(aOtherProcess, &processHandle)) { // Process has already died? return nullptr; } if (!sBackgroundThread && !CreateBackgroundThread()) { NS_WARNING("Failed to create background thread!"); return nullptr; } MOZ_ASSERT(sLiveActorsForBackgroundThread); sLiveActorCount++; nsRefPtr actor = new ParentImpl(aContent, aTransport); nsCOMPtr connectRunnable = new ConnectActorRunnable(actor, aTransport, processHandle, sLiveActorsForBackgroundThread); if (NS_FAILED(sBackgroundThread->Dispatch(connectRunnable, NS_DISPATCH_NORMAL))) { NS_WARNING("Failed to dispatch connect runnable!"); MOZ_ASSERT(sLiveActorCount); sLiveActorCount--; if (!sLiveActorCount) { ShutdownBackgroundThread(); } return nullptr; } return actor; } // static bool ParentImpl::CreateActorForSameProcess(CreateCallback* aCallback) { AssertIsInMainProcess(); AssertIsOnMainThread(); MOZ_ASSERT(aCallback); if (!sBackgroundThread && !CreateBackgroundThread()) { NS_WARNING("Failed to create background thread!"); return false; } MOZ_ASSERT(!sShutdownHasStarted); sLiveActorCount++; if (sBackgroundThreadMessageLoop) { nsCOMPtr callbackRunnable = new CreateCallbackRunnable(aCallback); MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(callbackRunnable))); return true; } if (!sPendingCallbacks) { sPendingCallbacks = new nsTArray>(); } sPendingCallbacks->AppendElement(aCallback); return true; } // static bool ParentImpl::CreateBackgroundThread() { AssertIsInMainProcess(); 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) { nsresult rv; newShutdownTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv); if (NS_WARN_IF(NS_FAILED(rv))) { 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_WARNING("NS_NewNamedThread failed!"); return false; } nsCOMPtr messageLoopRunnable = new RequestMessageLoopRunnable(thread); if (NS_FAILED(thread->Dispatch(messageLoopRunnable, NS_DISPATCH_NORMAL))) { NS_WARNING("Failed to dispatch RequestMessageLoopRunnable!"); return false; } sBackgroundThread = thread; sLiveActorsForBackgroundThread = new nsTArray(1); if (!sShutdownTimer) { MOZ_ASSERT(newShutdownTimer); sShutdownTimer = newShutdownTimer; } return true; } // static void ParentImpl::ShutdownBackgroundThread() { AssertIsInMainProcess(); AssertIsOnMainThread(); MOZ_ASSERT_IF(!sBackgroundThread, !sBackgroundThreadMessageLoop); MOZ_ASSERT_IF(!sShutdownHasStarted, !sLiveActorCount); MOZ_ASSERT_IF(!sBackgroundThread, !sLiveActorCount); MOZ_ASSERT_IF(sBackgroundThread, sShutdownTimer); if (sPendingCallbacks) { if (!sPendingCallbacks->IsEmpty()) { nsTArray> callbacks; sPendingCallbacks->SwapElements(callbacks); for (uint32_t index = 0; index < callbacks.Length(); index++) { nsRefPtr callback; callbacks[index].swap(callback); MOZ_ASSERT(callback); callback->Failure(); } } if (sShutdownHasStarted) { sPendingCallbacks = nullptr; } } nsCOMPtr shutdownTimer; if (sShutdownHasStarted) { shutdownTimer = sShutdownTimer.get(); sShutdownTimer = nullptr; } if (sBackgroundThread) { nsCOMPtr thread = sBackgroundThread.get(); nsAutoPtr> liveActors(sLiveActorsForBackgroundThread); sBackgroundThread = nullptr; sLiveActorsForBackgroundThread = nullptr; sBackgroundThreadMessageLoop = nullptr; MOZ_ASSERT_IF(!sShutdownHasStarted, !sLiveActorCount); if (sShutdownHasStarted) { // If this is final shutdown then 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. if (sLiveActorCount) { TimerCallbackClosure closure(thread, liveActors); MOZ_ALWAYS_TRUE(NS_SUCCEEDED( shutdownTimer->InitWithFuncCallback(&ShutdownTimerCallback, &closure, kShutdownTimerDelayMS, nsITimer::TYPE_ONE_SHOT))); nsIThread* currentThread = NS_GetCurrentThread(); MOZ_ASSERT(currentThread); while (sLiveActorCount) { NS_ProcessNextEvent(currentThread); } MOZ_ASSERT(liveActors->IsEmpty()); MOZ_ALWAYS_TRUE(NS_SUCCEEDED(shutdownTimer->Cancel())); } } // Dispatch this runnable to unregister the thread from the profiler. nsCOMPtr shutdownRunnable = new ShutdownBackgroundThreadRunnable(); MOZ_ALWAYS_TRUE(NS_SUCCEEDED(thread->Dispatch(shutdownRunnable, NS_DISPATCH_NORMAL))); MOZ_ALWAYS_TRUE(NS_SUCCEEDED(thread->Shutdown())); } } // static void ParentImpl::ShutdownTimerCallback(nsITimer* aTimer, void* aClosure) { AssertIsInMainProcess(); 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++; nsCOMPtr forceCloseRunnable = new ForceCloseBackgroundActorsRunnable(closure->mLiveActors); MOZ_ALWAYS_TRUE(NS_SUCCEEDED(closure->mThread->Dispatch(forceCloseRunnable, NS_DISPATCH_NORMAL))); } void ParentImpl::Destroy() { // May be called on any thread! AssertIsInMainProcess(); nsCOMPtr destroyRunnable = NS_NewNonOwningRunnableMethod(this, &ParentImpl::MainThreadActorDestroy); MOZ_ASSERT(destroyRunnable); MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(destroyRunnable))); } void ParentImpl::MainThreadActorDestroy() { AssertIsInMainProcess(); AssertIsOnMainThread(); MOZ_ASSERT_IF(mIsOtherProcessActor, mContent); MOZ_ASSERT_IF(!mIsOtherProcessActor, !mContent); MOZ_ASSERT_IF(mIsOtherProcessActor, mTransport); MOZ_ASSERT_IF(!mIsOtherProcessActor, !mTransport); if (mTransport) { XRE_GetIOMessageLoop()->PostTask(FROM_HERE, new DeleteTask(mTransport)); mTransport = nullptr; } ProcessHandle otherProcess = OtherProcess(); if (otherProcess != kInvalidProcessHandle) { base::CloseProcessHandle(otherProcess); #ifdef DEBUG SetOtherProcess(kInvalidProcessHandle); #endif } mContent = nullptr; MOZ_ASSERT(sLiveActorCount); sLiveActorCount--; if (!sLiveActorCount) { ShutdownBackgroundThread(); } // This may be the last reference! Release(); } IToplevelProtocol* ParentImpl::CloneToplevel(const InfallibleTArray& aFds, ProcessHandle aPeerProcess, ProtocolCloneContext* aCtx) { AssertIsInMainProcess(); AssertIsOnMainThread(); const ProtocolId protocolId = GetProtocolId(); for (unsigned int i = 0; i < aFds.Length(); i++) { if (static_cast(aFds[i].protocolId()) != protocolId) { continue; } Transport* transport = OpenDescriptor(aFds[i].fd(), Transport::MODE_SERVER); if (!transport) { NS_WARNING("Failed to open transport!"); break; } PBackgroundParent* clonedActor = Alloc(mContent, transport, base::GetProcId(aPeerProcess)); MOZ_ASSERT(clonedActor); clonedActor->CloneManagees(this, aCtx); clonedActor->SetTransport(transport); return clonedActor; } return nullptr; } void ParentImpl::ActorDestroy(ActorDestroyReason aWhy) { AssertIsInMainProcess(); 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. nsCOMPtr destroyRunnable = NS_NewNonOwningRunnableMethod(this, &ParentImpl::Destroy); MOZ_ASSERT(destroyRunnable); MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(destroyRunnable))); } NS_IMPL_ISUPPORTS1(ParentImpl::ShutdownObserver, nsIObserver) NS_IMETHODIMP ParentImpl::ShutdownObserver::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) { AssertIsInMainProcess(); 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; } NS_IMPL_ISUPPORTS_INHERITED0(ParentImpl::RequestMessageLoopRunnable, nsRunnable) NS_IMETHODIMP ParentImpl::RequestMessageLoopRunnable::Run() { AssertIsInMainProcess(); MOZ_ASSERT(mTargetThread); if (NS_IsMainThread()) { MOZ_ASSERT(mMessageLoop); if (!sBackgroundThread || !SameCOMIdentity(mTargetThread.get(), sBackgroundThread.get())) { return NS_OK; } MOZ_ASSERT(!sBackgroundThreadMessageLoop); sBackgroundThreadMessageLoop = mMessageLoop; if (sPendingCallbacks && !sPendingCallbacks->IsEmpty()) { nsTArray> callbacks; sPendingCallbacks->SwapElements(callbacks); for (uint32_t index = 0; index < callbacks.Length(); index++) { MOZ_ASSERT(callbacks[index]); nsCOMPtr callbackRunnable = new CreateCallbackRunnable(callbacks[index]); if (NS_FAILED(NS_DispatchToCurrentThread(callbackRunnable))) { NS_WARNING("Failed to dispatch callback runnable!"); } } } return NS_OK; } char stackBaseGuess; profiler_register_thread("IPDL Background", &stackBaseGuess); #ifdef DEBUG { bool correctThread; MOZ_ASSERT(NS_SUCCEEDED(mTargetThread->IsOnCurrentThread(&correctThread))); MOZ_ASSERT(correctThread); } #endif DebugOnly oldBackgroundThread = sBackgroundPRThread.exchange(PR_GetCurrentThread()); MOZ_ASSERT_IF(oldBackgroundThread, PR_GetCurrentThread() != oldBackgroundThread); MOZ_ASSERT(!mMessageLoop); mMessageLoop = MessageLoop::current(); MOZ_ASSERT(mMessageLoop); if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) { NS_WARNING("Failed to dispatch RequestMessageLoopRunnable to main thread!"); return NS_ERROR_FAILURE; } return NS_OK; } NS_IMPL_ISUPPORTS_INHERITED0(ParentImpl::ShutdownBackgroundThreadRunnable, nsRunnable) NS_IMETHODIMP ParentImpl::ShutdownBackgroundThreadRunnable::Run() { AssertIsInMainProcess(); // 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); profiler_unregister_thread(); return NS_OK; } NS_IMPL_ISUPPORTS_INHERITED0(ParentImpl::ForceCloseBackgroundActorsRunnable, nsRunnable) NS_IMETHODIMP ParentImpl::ForceCloseBackgroundActorsRunnable::Run() { AssertIsInMainProcess(); MOZ_ASSERT(mActorArray); if (NS_IsMainThread()) { MOZ_ASSERT(sLiveActorCount); sLiveActorCount--; return NS_OK; } AssertIsOnBackgroundThread(); if (!mActorArray->IsEmpty()) { // Copy the array since calling Close() could mutate the actual array. nsTArray actorsToClose(*mActorArray); for (uint32_t index = 0; index < actorsToClose.Length(); index++) { actorsToClose[index]->Close(); } } MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(this))); return NS_OK; } NS_IMPL_ISUPPORTS_INHERITED0(ParentImpl::CreateCallbackRunnable, nsRunnable) NS_IMETHODIMP ParentImpl::CreateCallbackRunnable::Run() { AssertIsInMainProcess(); AssertIsOnMainThread(); MOZ_ASSERT(sBackgroundThreadMessageLoop); MOZ_ASSERT(mCallback); nsRefPtr callback; mCallback.swap(callback); nsRefPtr actor = new ParentImpl(); callback->Success(actor.forget(), sBackgroundThreadMessageLoop); return NS_OK; } NS_IMPL_ISUPPORTS_INHERITED0(ParentImpl::ConnectActorRunnable, nsRunnable) NS_IMETHODIMP ParentImpl::ConnectActorRunnable::Run() { AssertIsInMainProcess(); AssertIsOnBackgroundThread(); // Transfer ownership to this thread. If Open() fails then we will release // this reference in Destroy. ParentImpl* actor; mActor.forget(&actor); if (!actor->Open(mTransport, mProcessHandle, XRE_GetIOMessageLoop(), ParentSide)) { actor->Destroy(); return NS_ERROR_FAILURE; } actor->SetLiveActorArray(mLiveActorArray); 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. MOZ_ASSERT(sThreadLocalIndex == kBadThreadLocalIndex, "BackgroundChild::Startup() called more than once!"); PRStatus status = PR_NewThreadPrivateIndex(&sThreadLocalIndex, ThreadLocalDestructor); MOZ_RELEASE_ASSERT(status == PR_SUCCESS, "PR_NewThreadPrivateIndex failed!"); MOZ_ASSERT(sThreadLocalIndex != kBadThreadLocalIndex); 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(); if (sShutdownHasStarted) { MOZ_ASSERT_IF(sThreadLocalIndex != kBadThreadLocalIndex, !PR_GetThreadPrivate(sThreadLocalIndex)); return; } sShutdownHasStarted = true; MOZ_ASSERT(sThreadLocalIndex != kBadThreadLocalIndex); DebugOnly status = PR_SetThreadPrivate(sThreadLocalIndex, nullptr); MOZ_ASSERT(status == PR_SUCCESS); } // static PBackgroundChild* ChildImpl::Alloc(Transport* aTransport, ProcessId aOtherProcess) { AssertIsInChildProcess(); AssertIsOnMainThread(); MOZ_ASSERT(aTransport); MOZ_ASSERT(sPendingTargets); MOZ_ASSERT(!sPendingTargets->IsEmpty()); nsCOMPtr eventTarget; sPendingTargets->ElementAt(0).swap(eventTarget); sPendingTargets->RemoveElementAt(0); ProcessHandle processHandle; if (!base::OpenProcessHandle(aOtherProcess, &processHandle)) { MOZ_CRASH("Failed to open process handle!"); } nsRefPtr actor = new ChildImpl(); ChildImpl* weakActor = actor; nsCOMPtr openRunnable = new OpenChildProcessActorRunnable(actor.forget(), aTransport, processHandle); if (NS_FAILED(eventTarget->Dispatch(openRunnable, NS_DISPATCH_NORMAL))) { MOZ_CRASH("Failed to dispatch OpenActorRunnable!"); } // This value is only checked against null to determine success/failure, so // there is no need to worry about the reference count here. return weakActor; } // static PBackgroundChild* ChildImpl::GetForCurrentThread() { MOZ_ASSERT(sThreadLocalIndex != kBadThreadLocalIndex); auto threadLocalInfo = static_cast(PR_GetThreadPrivate(sThreadLocalIndex)); return threadLocalInfo ? threadLocalInfo->mActor : nullptr; } // static bool ChildImpl::GetOrCreateForCurrentThread( nsIIPCBackgroundChildCreateCallback* aCallback) { MOZ_ASSERT(aCallback); MOZ_ASSERT(sThreadLocalIndex != kBadThreadLocalIndex, "BackgroundChild::Startup() was never called!"); bool created = false; auto threadLocalInfo = static_cast(PR_GetThreadPrivate(sThreadLocalIndex)); if (threadLocalInfo) { threadLocalInfo->mCallbacks.AppendElement(aCallback); } else { nsAutoPtr newInfo(new ThreadLocalInfo(aCallback)); if (PR_SetThreadPrivate(sThreadLocalIndex, newInfo) != PR_SUCCESS) { CRASH_IN_CHILD_PROCESS("PR_SetThreadPrivate failed!"); return false; } created = true; threadLocalInfo = newInfo.forget(); } if (threadLocalInfo->mActor) { nsRefPtr actor = threadLocalInfo->mActor; nsCOMPtr runnable = new CreateCallbackRunnable(actor.forget()); MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(runnable))); return true; } if (!created) { // We have already started the sequence for opening the actor so there's // nothing else we need to do here. This callback will be called after the // first callback in CreateCallbackRunnable::Run(). return true; } if (NS_IsMainThread()) { if (NS_WARN_IF(!OpenProtocolOnMainThread(NS_GetCurrentThread()))) { return false; } return true; } nsRefPtr runnable = new CreateActorRunnable(); if (NS_FAILED(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))) { CRASH_IN_CHILD_PROCESS("Failed to dispatch to main thread!"); return false; } return true; } ChildImpl::CreateCallbackRunnable::~CreateCallbackRunnable() { if (mActor) { CRASH_IN_CHILD_PROCESS("Leaking actor!"); unused << mActor.forget(); } } // static already_AddRefed ChildImpl::CreateCallbackRunnable::GetNextCallback() { // May run on any thread! auto threadLocalInfo = static_cast(PR_GetThreadPrivate(sThreadLocalIndex)); MOZ_ASSERT(threadLocalInfo); if (threadLocalInfo->mCallbacks.IsEmpty()) { return nullptr; } nsCOMPtr callback; threadLocalInfo->mCallbacks[0].swap(callback); threadLocalInfo->mCallbacks.RemoveElementAt(0); return callback.forget(); } NS_IMPL_ISUPPORTS_INHERITED0(ChildImpl::CreateCallbackRunnable, nsRunnable) NS_IMETHODIMP ChildImpl::CreateCallbackRunnable::Run() { // May run on any thread! nsRefPtr actor; mActor.swap(actor); nsCOMPtr callback = GetNextCallback(); while (callback) { if (actor) { callback->ActorCreated(actor); } else { callback->ActorFailed(); } callback = GetNextCallback(); } return NS_OK; } NS_IMPL_ISUPPORTS_INHERITED0(ChildImpl::OpenChildProcessActorRunnable, ChildImpl::CreateCallbackRunnable) NS_IMETHODIMP ChildImpl::OpenChildProcessActorRunnable::Run() { // May be run on any thread! AssertIsInChildProcess(); MOZ_ASSERT(mActor); MOZ_ASSERT(mTransport); nsCOMPtr callback = GetNextCallback(); MOZ_ASSERT(callback, "There should be at least one callback when first creating the " "actor!"); nsRefPtr strongActor; mActor.swap(strongActor); if (!strongActor->Open(mTransport.forget(), mProcessHandle, XRE_GetIOMessageLoop(), ChildSide)) { CRASH_IN_CHILD_PROCESS("Failed to open ChildImpl!"); while (callback) { callback->ActorFailed(); callback = GetNextCallback(); } return NS_OK; } // Now that Open() has succeeded transfer the ownership of the actor to IPDL. auto threadLocalInfo = static_cast(PR_GetThreadPrivate(sThreadLocalIndex)); MOZ_ASSERT(threadLocalInfo); MOZ_ASSERT(!threadLocalInfo->mActor); nsRefPtr& actor = threadLocalInfo->mActor; strongActor.swap(actor); actor->SetBoundThread(); while (callback) { callback->ActorCreated(actor); callback = GetNextCallback(); } return NS_OK; } NS_IMPL_ISUPPORTS_INHERITED0(ChildImpl::OpenMainProcessActorRunnable, ChildImpl::CreateCallbackRunnable) NS_IMETHODIMP ChildImpl::OpenMainProcessActorRunnable::Run() { // May run on any thread! AssertIsInMainProcess(); MOZ_ASSERT(mActor); MOZ_ASSERT(mParentActor); MOZ_ASSERT(mParentMessageLoop); nsCOMPtr callback = GetNextCallback(); MOZ_ASSERT(callback, "There should be at least one callback when first creating the " "actor!"); nsRefPtr strongChildActor; mActor.swap(strongChildActor); nsRefPtr parentActor; mParentActor.swap(parentActor); MessageChannel* parentChannel = parentActor->GetIPCChannel(); MOZ_ASSERT(parentChannel); if (!strongChildActor->Open(parentChannel, mParentMessageLoop, ChildSide)) { NS_WARNING("Failed to open ChildImpl!"); parentActor->Destroy(); while (callback) { callback->ActorFailed(); callback = GetNextCallback(); } return NS_OK; } // Now that Open() has succeeded transfer the ownership of the actors to IPDL. unused << parentActor.forget(); auto threadLocalInfo = static_cast(PR_GetThreadPrivate(sThreadLocalIndex)); MOZ_ASSERT(threadLocalInfo); MOZ_ASSERT(!threadLocalInfo->mActor); nsRefPtr& childActor = threadLocalInfo->mActor; strongChildActor.swap(childActor); childActor->SetBoundThread(); while (callback) { callback->ActorCreated(childActor); callback = GetNextCallback(); } return NS_OK; } NS_IMPL_ISUPPORTS_INHERITED0(ChildImpl::CreateActorRunnable, nsRunnable) NS_IMETHODIMP ChildImpl::CreateActorRunnable::Run() { AssertIsOnMainThread(); if (!OpenProtocolOnMainThread(mEventTarget)) { NS_WARNING("OpenProtocolOnMainThread failed!"); return NS_ERROR_FAILURE; } return NS_OK; } void ChildImpl::ParentCreateCallback::Success( already_AddRefed aParentActor, MessageLoop* aParentMessageLoop) { AssertIsInMainProcess(); AssertIsOnMainThread(); nsRefPtr parentActor = aParentActor; MOZ_ASSERT(parentActor); MOZ_ASSERT(aParentMessageLoop); MOZ_ASSERT(mEventTarget); nsRefPtr childActor = new ChildImpl(); nsCOMPtr target; mEventTarget.swap(target); nsCOMPtr openRunnable = new OpenMainProcessActorRunnable(childActor.forget(), parentActor.forget(), aParentMessageLoop); if (NS_FAILED(target->Dispatch(openRunnable, NS_DISPATCH_NORMAL))) { NS_WARNING("Failed to dispatch open runnable!"); } } void ChildImpl::ParentCreateCallback::Failure() { AssertIsInMainProcess(); AssertIsOnMainThread(); MOZ_ASSERT(mEventTarget); nsCOMPtr target; mEventTarget.swap(target); DispatchFailureCallback(target); } // static bool ChildImpl::OpenProtocolOnMainThread(nsIEventTarget* aEventTarget) { AssertIsOnMainThread(); MOZ_ASSERT(aEventTarget); if (sShutdownHasStarted) { MOZ_CRASH("Called BackgroundChild::GetOrCreateForCurrentThread after " "shutdown has started!"); } if (IsMainProcess()) { nsRefPtr parentCallback = new ParentCreateCallback(aEventTarget); if (!ParentImpl::CreateActorForSameProcess(parentCallback)) { NS_WARNING("BackgroundParent::CreateActor() failed!"); DispatchFailureCallback(aEventTarget); return false; } return true; } ContentChild* content = ContentChild::GetSingleton(); MOZ_ASSERT(content); if (!PBackground::Open(content)) { MOZ_CRASH("Failed to create top level actor!"); return false; } if (!sPendingTargets) { sPendingTargets = new nsTArray>(1); ClearOnShutdown(&sPendingTargets); } sPendingTargets->AppendElement(aEventTarget); return true; } // static void ChildImpl::DispatchFailureCallback(nsIEventTarget* aEventTarget) { MOZ_ASSERT(aEventTarget); nsCOMPtr callbackRunnable = new CreateCallbackRunnable(); if (NS_FAILED(aEventTarget->Dispatch(callbackRunnable, NS_DISPATCH_NORMAL))) { NS_WARNING("Failed to dispatch CreateCallbackRunnable!"); } } void ChildImpl::ActorDestroy(ActorDestroyReason aWhy) { AssertIsOnBoundThread(); BackgroundChildImpl::ActorDestroy(aWhy); } NS_IMPL_ISUPPORTS1(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; }