michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "base/process_util.h" michael@0: #include "mozilla/Assertions.h" michael@0: #include "mozilla/Atomics.h" michael@0: #include "mozilla/ClearOnShutdown.h" michael@0: #include "mozilla/DebugOnly.h" michael@0: #include "mozilla/Services.h" michael@0: #include "mozilla/StaticPtr.h" michael@0: #include "mozilla/unused.h" michael@0: #include "mozilla/dom/ContentChild.h" michael@0: #include "mozilla/dom/ContentParent.h" michael@0: #include "mozilla/ipc/ProtocolTypes.h" michael@0: #include "BackgroundChild.h" michael@0: #include "BackgroundChildImpl.h" michael@0: #include "BackgroundParent.h" michael@0: #include "BackgroundParentImpl.h" michael@0: #include "GeckoProfiler.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsIEventTarget.h" michael@0: #include "nsIIPCBackgroundChildCreateCallback.h" michael@0: #include "nsIObserver.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsIRunnable.h" michael@0: #include "nsISupportsImpl.h" michael@0: #include "nsIThread.h" michael@0: #include "nsITimer.h" michael@0: #include "nsTArray.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsTraceRefcnt.h" michael@0: #include "nsXULAppAPI.h" michael@0: #include "nsXPCOMPrivate.h" michael@0: #include "prthread.h" michael@0: michael@0: #ifdef RELEASE_BUILD michael@0: #define THREADSAFETY_ASSERT MOZ_ASSERT michael@0: #else michael@0: #define THREADSAFETY_ASSERT MOZ_RELEASE_ASSERT michael@0: #endif michael@0: michael@0: #define CRASH_IN_CHILD_PROCESS(_msg) \ michael@0: do { \ michael@0: if (IsMainProcess()) { \ michael@0: MOZ_ASSERT(false, _msg); \ michael@0: } else { \ michael@0: MOZ_CRASH(_msg); \ michael@0: } \ michael@0: } \ michael@0: while (0) michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::ipc; michael@0: michael@0: using mozilla::dom::ContentChild; michael@0: using mozilla::dom::ContentParent; michael@0: michael@0: namespace { michael@0: michael@0: // ----------------------------------------------------------------------------- michael@0: // Utility Functions michael@0: // ----------------------------------------------------------------------------- michael@0: michael@0: bool michael@0: IsMainProcess() michael@0: { michael@0: static const bool isMainProcess = michael@0: XRE_GetProcessType() == GeckoProcessType_Default; michael@0: return isMainProcess; michael@0: } michael@0: michael@0: bool michael@0: IsChildProcess() michael@0: { michael@0: return !IsMainProcess(); michael@0: } michael@0: michael@0: void michael@0: AssertIsInMainProcess() michael@0: { michael@0: MOZ_ASSERT(IsMainProcess()); michael@0: } michael@0: michael@0: void michael@0: AssertIsInChildProcess() michael@0: { michael@0: MOZ_ASSERT(IsChildProcess()); michael@0: } michael@0: michael@0: void michael@0: AssertIsOnMainThread() michael@0: { michael@0: THREADSAFETY_ASSERT(NS_IsMainThread()); michael@0: } michael@0: michael@0: // ----------------------------------------------------------------------------- michael@0: // ParentImpl Declaration michael@0: // ----------------------------------------------------------------------------- michael@0: michael@0: class ParentImpl MOZ_FINAL : public BackgroundParentImpl michael@0: { michael@0: friend class mozilla::ipc::BackgroundParent; michael@0: michael@0: public: michael@0: class CreateCallback; michael@0: michael@0: private: michael@0: class ShutdownObserver; michael@0: class RequestMessageLoopRunnable; michael@0: class ShutdownBackgroundThreadRunnable; michael@0: class ForceCloseBackgroundActorsRunnable; michael@0: class CreateCallbackRunnable; michael@0: class ConnectActorRunnable; michael@0: michael@0: struct MOZ_STACK_CLASS TimerCallbackClosure michael@0: { michael@0: nsIThread* mThread; michael@0: nsTArray* mLiveActors; michael@0: michael@0: TimerCallbackClosure(nsIThread* aThread, nsTArray* aLiveActors) michael@0: : mThread(aThread), mLiveActors(aLiveActors) michael@0: { michael@0: AssertIsInMainProcess(); michael@0: AssertIsOnMainThread(); michael@0: MOZ_ASSERT(aThread); michael@0: MOZ_ASSERT(aLiveActors); michael@0: } michael@0: }; michael@0: michael@0: // A handle that is invalid on any platform. michael@0: static const ProcessHandle kInvalidProcessHandle; michael@0: michael@0: // The length of time we will wait at shutdown for all actors to clean michael@0: // themselves up before forcing them to be destroyed. michael@0: static const uint32_t kShutdownTimerDelayMS = 10000; michael@0: michael@0: // This is only modified on the main thread. It is null if the thread does not michael@0: // exist or is shutting down. michael@0: static StaticRefPtr sBackgroundThread; michael@0: michael@0: // This is created and destroyed on the main thread but only modified on the michael@0: // background thread. It is specific to each instance of sBackgroundThread. michael@0: static nsTArray* sLiveActorsForBackgroundThread; michael@0: michael@0: // This is only modified on the main thread. michael@0: static StaticRefPtr sShutdownTimer; michael@0: michael@0: // This exists so that that [Assert]IsOnBackgroundThread() can continue to michael@0: // work during shutdown. michael@0: static Atomic sBackgroundPRThread; michael@0: michael@0: // This is only modified on the main thread. It is null if the thread does not michael@0: // exist or is shutting down. michael@0: static MessageLoop* sBackgroundThreadMessageLoop; michael@0: michael@0: // This is only modified on the main thread. It maintains a count of live michael@0: // actors so that the background thread can be shut down when it is no longer michael@0: // needed. michael@0: static uint64_t sLiveActorCount; michael@0: michael@0: // This is only modified on the main thread. It is true after the shutdown michael@0: // observer is registered and is never unset thereafter. michael@0: static bool sShutdownObserverRegistered; michael@0: michael@0: // This is only modified on the main thread. It prevents us from trying to michael@0: // create the background thread after application shutdown has started. michael@0: static bool sShutdownHasStarted; michael@0: michael@0: // This is only modified on the main thread. It is a FIFO queue for callbacks michael@0: // waiting for the background thread to be created. michael@0: static StaticAutoPtr>> sPendingCallbacks; michael@0: michael@0: // Only touched on the main thread, null if this is a same-process actor. michael@0: nsRefPtr mContent; michael@0: michael@0: // mTransport is "owned" by this object but it must only be released on the michael@0: // IPC thread. It's left as a raw pointer here to prevent accidentally michael@0: // deleting it on the wrong thread. Only non-null for other-process actors. michael@0: Transport* mTransport; michael@0: michael@0: // Set when the actor is opened successfully and used to handle shutdown michael@0: // hangs. Only touched on the background thread. michael@0: nsTArray* mLiveActorArray; michael@0: michael@0: // Set at construction to indicate whether this parent actor corresponds to a michael@0: // child actor in another process or to a child actor from a different thread michael@0: // in the same process. michael@0: const bool mIsOtherProcessActor; michael@0: michael@0: // Set after ActorDestroy has been called. Only touched on the background michael@0: // thread. michael@0: bool mActorDestroyed; michael@0: michael@0: public: michael@0: static bool michael@0: CreateActorForSameProcess(CreateCallback* aCallback); michael@0: michael@0: static bool michael@0: IsOnBackgroundThread() michael@0: { michael@0: return PR_GetCurrentThread() == sBackgroundPRThread; michael@0: } michael@0: michael@0: static void michael@0: AssertIsOnBackgroundThread() michael@0: { michael@0: THREADSAFETY_ASSERT(IsOnBackgroundThread()); michael@0: } michael@0: michael@0: NS_INLINE_DECL_REFCOUNTING(ParentImpl) michael@0: michael@0: void michael@0: Destroy(); michael@0: michael@0: private: michael@0: // Forwarded from BackgroundParent. michael@0: static bool michael@0: IsOtherProcessActor(PBackgroundParent* aBackgroundActor); michael@0: michael@0: // Forwarded from BackgroundParent. michael@0: static already_AddRefed michael@0: GetContentParent(PBackgroundParent* aBackgroundActor); michael@0: michael@0: // Forwarded from BackgroundParent. michael@0: static PBackgroundParent* michael@0: Alloc(ContentParent* aContent, michael@0: Transport* aTransport, michael@0: ProcessId aOtherProcess); michael@0: michael@0: static bool michael@0: CreateBackgroundThread(); michael@0: michael@0: static void michael@0: ShutdownBackgroundThread(); michael@0: michael@0: static void michael@0: ShutdownTimerCallback(nsITimer* aTimer, void* aClosure); michael@0: michael@0: // For same-process actors. michael@0: ParentImpl() michael@0: : mTransport(nullptr), mLiveActorArray(nullptr), mIsOtherProcessActor(false), michael@0: mActorDestroyed(false) michael@0: { michael@0: AssertIsInMainProcess(); michael@0: AssertIsOnMainThread(); michael@0: michael@0: SetOtherProcess(kInvalidProcessHandle); michael@0: } michael@0: michael@0: // For other-process actors. michael@0: ParentImpl(ContentParent* aContent, Transport* aTransport) michael@0: : mContent(aContent), mTransport(aTransport), mLiveActorArray(nullptr), michael@0: mIsOtherProcessActor(true), mActorDestroyed(false) michael@0: { michael@0: AssertIsInMainProcess(); michael@0: AssertIsOnMainThread(); michael@0: MOZ_ASSERT(aContent); michael@0: MOZ_ASSERT(aTransport); michael@0: } michael@0: michael@0: ~ParentImpl() michael@0: { michael@0: AssertIsInMainProcess(); michael@0: AssertIsOnMainThread(); michael@0: MOZ_ASSERT(!mContent); michael@0: MOZ_ASSERT(!mTransport); michael@0: } michael@0: michael@0: void michael@0: MainThreadActorDestroy(); michael@0: michael@0: void michael@0: SetLiveActorArray(nsTArray* aLiveActorArray) michael@0: { michael@0: AssertIsInMainProcess(); michael@0: AssertIsOnBackgroundThread(); michael@0: MOZ_ASSERT(aLiveActorArray); michael@0: MOZ_ASSERT(!aLiveActorArray->Contains(this)); michael@0: MOZ_ASSERT(!mLiveActorArray); michael@0: MOZ_ASSERT(mIsOtherProcessActor); michael@0: michael@0: mLiveActorArray = aLiveActorArray; michael@0: mLiveActorArray->AppendElement(this); michael@0: } michael@0: michael@0: // These methods are only called by IPDL. michael@0: virtual IToplevelProtocol* michael@0: CloneToplevel(const InfallibleTArray& aFds, michael@0: ProcessHandle aPeerProcess, michael@0: ProtocolCloneContext* aCtx) MOZ_OVERRIDE; michael@0: michael@0: virtual void michael@0: ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; michael@0: }; michael@0: michael@0: // ----------------------------------------------------------------------------- michael@0: // ChildImpl Declaration michael@0: // ----------------------------------------------------------------------------- michael@0: michael@0: class ChildImpl MOZ_FINAL : public BackgroundChildImpl michael@0: { michael@0: friend class mozilla::ipc::BackgroundChild; michael@0: friend class mozilla::ipc::BackgroundChildImpl; michael@0: michael@0: typedef base::ProcessId ProcessId; michael@0: typedef mozilla::ipc::Transport Transport; michael@0: michael@0: class ShutdownObserver; michael@0: class CreateActorRunnable; michael@0: class ParentCreateCallback; michael@0: class CreateCallbackRunnable; michael@0: class OpenChildProcessActorRunnable; michael@0: class OpenMainProcessActorRunnable; michael@0: michael@0: // A thread-local index that is not valid. michael@0: static const unsigned int kBadThreadLocalIndex = michael@0: static_cast(-1); michael@0: michael@0: // This is only modified on the main thread. It is the thread-local index that michael@0: // we use to store the BackgroundChild for each thread. michael@0: static unsigned int sThreadLocalIndex; michael@0: michael@0: struct ThreadLocalInfo michael@0: { michael@0: ThreadLocalInfo(nsIIPCBackgroundChildCreateCallback* aCallback) michael@0: { michael@0: mCallbacks.AppendElement(aCallback); michael@0: } michael@0: michael@0: nsRefPtr mActor; michael@0: nsTArray> mCallbacks; michael@0: nsAutoPtr mConsumerThreadLocal; michael@0: }; michael@0: michael@0: // This is only modified on the main thread. It is a FIFO queue for actors michael@0: // that are in the process of construction. michael@0: static StaticAutoPtr>> sPendingTargets; michael@0: michael@0: // This is only modified on the main thread. It prevents us from trying to michael@0: // create the background thread after application shutdown has started. michael@0: static bool sShutdownHasStarted; michael@0: michael@0: #ifdef RELEASE_BUILD michael@0: DebugOnly mBoundThread; michael@0: #else michael@0: nsIThread* mBoundThread; michael@0: #endif michael@0: michael@0: public: michael@0: static bool michael@0: OpenProtocolOnMainThread(nsIEventTarget* aEventTarget); michael@0: michael@0: static void michael@0: Shutdown(); michael@0: michael@0: void michael@0: AssertIsOnBoundThread() michael@0: { michael@0: THREADSAFETY_ASSERT(mBoundThread); michael@0: michael@0: #ifdef RELEASE_BUILD michael@0: DebugOnly current; michael@0: #else michael@0: bool current; michael@0: #endif michael@0: THREADSAFETY_ASSERT( michael@0: NS_SUCCEEDED(mBoundThread->IsOnCurrentThread(¤t))); michael@0: THREADSAFETY_ASSERT(current); michael@0: } michael@0: michael@0: ChildImpl() michael@0: : mBoundThread(nullptr) michael@0: { michael@0: AssertIsOnMainThread(); michael@0: } michael@0: michael@0: NS_INLINE_DECL_REFCOUNTING(ChildImpl) michael@0: michael@0: private: michael@0: // Forwarded from BackgroundChild. michael@0: static void michael@0: Startup(); michael@0: michael@0: // Forwarded from BackgroundChild. michael@0: static PBackgroundChild* michael@0: Alloc(Transport* aTransport, ProcessId aOtherProcess); michael@0: michael@0: // Forwarded from BackgroundChild. michael@0: static PBackgroundChild* michael@0: GetForCurrentThread(); michael@0: michael@0: // Forwarded from BackgroundChild. michael@0: static bool michael@0: GetOrCreateForCurrentThread(nsIIPCBackgroundChildCreateCallback* aCallback); michael@0: michael@0: // Forwarded from BackgroundChildImpl. michael@0: static BackgroundChildImpl::ThreadLocal* michael@0: GetThreadLocalForCurrentThread(); michael@0: michael@0: static void michael@0: ThreadLocalDestructor(void* aThreadLocal) michael@0: { michael@0: auto threadLocalInfo = static_cast(aThreadLocal); michael@0: michael@0: if (threadLocalInfo) { michael@0: if (threadLocalInfo->mActor) { michael@0: threadLocalInfo->mActor->Close(); michael@0: } michael@0: delete threadLocalInfo; michael@0: } michael@0: } michael@0: michael@0: static void michael@0: DispatchFailureCallback(nsIEventTarget* aEventTarget); michael@0: michael@0: // This class is reference counted. michael@0: ~ChildImpl() michael@0: { } michael@0: michael@0: void michael@0: SetBoundThread() michael@0: { michael@0: THREADSAFETY_ASSERT(!mBoundThread); michael@0: michael@0: #if defined(DEBUG) || !defined(RELEASE_BUILD) michael@0: mBoundThread = NS_GetCurrentThread(); michael@0: #endif michael@0: michael@0: THREADSAFETY_ASSERT(mBoundThread); michael@0: } michael@0: michael@0: // Only called by IPDL. michael@0: virtual void michael@0: ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; michael@0: }; michael@0: michael@0: // ----------------------------------------------------------------------------- michael@0: // ParentImpl Helper Declarations michael@0: // ----------------------------------------------------------------------------- michael@0: michael@0: class ParentImpl::ShutdownObserver MOZ_FINAL : public nsIObserver michael@0: { michael@0: public: michael@0: ShutdownObserver() michael@0: { michael@0: AssertIsOnMainThread(); michael@0: } michael@0: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIOBSERVER michael@0: michael@0: private: michael@0: ~ShutdownObserver() michael@0: { michael@0: AssertIsOnMainThread(); michael@0: } michael@0: }; michael@0: michael@0: class ParentImpl::RequestMessageLoopRunnable MOZ_FINAL : michael@0: public nsRunnable michael@0: { michael@0: nsCOMPtr mTargetThread; michael@0: MessageLoop* mMessageLoop; michael@0: michael@0: public: michael@0: RequestMessageLoopRunnable(nsIThread* aTargetThread) michael@0: : mTargetThread(aTargetThread), mMessageLoop(nullptr) michael@0: { michael@0: AssertIsInMainProcess(); michael@0: AssertIsOnMainThread(); michael@0: MOZ_ASSERT(aTargetThread); michael@0: } michael@0: michael@0: NS_DECL_ISUPPORTS_INHERITED michael@0: michael@0: private: michael@0: ~RequestMessageLoopRunnable() michael@0: { } michael@0: michael@0: NS_DECL_NSIRUNNABLE michael@0: }; michael@0: michael@0: class ParentImpl::ShutdownBackgroundThreadRunnable MOZ_FINAL : public nsRunnable michael@0: { michael@0: public: michael@0: ShutdownBackgroundThreadRunnable() michael@0: { michael@0: AssertIsInMainProcess(); michael@0: AssertIsOnMainThread(); michael@0: } michael@0: michael@0: NS_DECL_ISUPPORTS_INHERITED michael@0: michael@0: private: michael@0: ~ShutdownBackgroundThreadRunnable() michael@0: { } michael@0: michael@0: NS_DECL_NSIRUNNABLE michael@0: }; michael@0: michael@0: class ParentImpl::ForceCloseBackgroundActorsRunnable MOZ_FINAL : public nsRunnable michael@0: { michael@0: nsTArray* mActorArray; michael@0: michael@0: public: michael@0: ForceCloseBackgroundActorsRunnable(nsTArray* aActorArray) michael@0: : mActorArray(aActorArray) michael@0: { michael@0: AssertIsInMainProcess(); michael@0: AssertIsOnMainThread(); michael@0: MOZ_ASSERT(aActorArray); michael@0: } michael@0: michael@0: NS_DECL_ISUPPORTS_INHERITED michael@0: michael@0: private: michael@0: ~ForceCloseBackgroundActorsRunnable() michael@0: { } michael@0: michael@0: NS_DECL_NSIRUNNABLE michael@0: }; michael@0: michael@0: class ParentImpl::CreateCallbackRunnable MOZ_FINAL : public nsRunnable michael@0: { michael@0: nsRefPtr mCallback; michael@0: michael@0: public: michael@0: CreateCallbackRunnable(CreateCallback* aCallback) michael@0: : mCallback(aCallback) michael@0: { michael@0: AssertIsInMainProcess(); michael@0: AssertIsOnMainThread(); michael@0: MOZ_ASSERT(aCallback); michael@0: } michael@0: michael@0: NS_DECL_ISUPPORTS_INHERITED michael@0: michael@0: private: michael@0: ~CreateCallbackRunnable() michael@0: { } michael@0: michael@0: NS_DECL_NSIRUNNABLE michael@0: }; michael@0: michael@0: class ParentImpl::ConnectActorRunnable MOZ_FINAL : public nsRunnable michael@0: { michael@0: nsRefPtr mActor; michael@0: Transport* mTransport; michael@0: ProcessHandle mProcessHandle; michael@0: nsTArray* mLiveActorArray; michael@0: michael@0: public: michael@0: ConnectActorRunnable(ParentImpl* aActor, michael@0: Transport* aTransport, michael@0: ProcessHandle aProcessHandle, michael@0: nsTArray* aLiveActorArray) michael@0: : mActor(aActor), mTransport(aTransport), mProcessHandle(aProcessHandle), michael@0: mLiveActorArray(aLiveActorArray) michael@0: { michael@0: AssertIsInMainProcess(); michael@0: AssertIsOnMainThread(); michael@0: MOZ_ASSERT(aActor); michael@0: MOZ_ASSERT(aTransport); michael@0: MOZ_ASSERT(aLiveActorArray); michael@0: } michael@0: michael@0: NS_DECL_ISUPPORTS_INHERITED michael@0: michael@0: private: michael@0: ~ConnectActorRunnable() michael@0: { michael@0: AssertIsInMainProcess(); michael@0: } michael@0: michael@0: NS_DECL_NSIRUNNABLE michael@0: }; michael@0: michael@0: class NS_NO_VTABLE ParentImpl::CreateCallback michael@0: { michael@0: public: michael@0: NS_INLINE_DECL_REFCOUNTING(CreateCallback) michael@0: michael@0: virtual void michael@0: Success(already_AddRefed aActor, MessageLoop* aMessageLoop) = 0; michael@0: michael@0: virtual void michael@0: Failure() = 0; michael@0: michael@0: protected: michael@0: virtual ~CreateCallback() michael@0: { } michael@0: }; michael@0: michael@0: // ----------------------------------------------------------------------------- michael@0: // ChildImpl Helper Declarations michael@0: // ----------------------------------------------------------------------------- michael@0: michael@0: class ChildImpl::ShutdownObserver MOZ_FINAL : public nsIObserver michael@0: { michael@0: public: michael@0: ShutdownObserver() michael@0: { michael@0: AssertIsOnMainThread(); michael@0: } michael@0: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIOBSERVER michael@0: michael@0: private: michael@0: ~ShutdownObserver() michael@0: { michael@0: AssertIsOnMainThread(); michael@0: } michael@0: }; michael@0: michael@0: class ChildImpl::CreateActorRunnable MOZ_FINAL : public nsRunnable michael@0: { michael@0: nsCOMPtr mEventTarget; michael@0: michael@0: public: michael@0: CreateActorRunnable() michael@0: : mEventTarget(NS_GetCurrentThread()) michael@0: { michael@0: MOZ_ASSERT(mEventTarget); michael@0: } michael@0: michael@0: NS_DECL_ISUPPORTS_INHERITED michael@0: michael@0: private: michael@0: ~CreateActorRunnable() michael@0: { } michael@0: michael@0: NS_DECL_NSIRUNNABLE michael@0: }; michael@0: michael@0: class ChildImpl::ParentCreateCallback MOZ_FINAL : michael@0: public ParentImpl::CreateCallback michael@0: { michael@0: nsCOMPtr mEventTarget; michael@0: michael@0: public: michael@0: ParentCreateCallback(nsIEventTarget* aEventTarget) michael@0: : mEventTarget(aEventTarget) michael@0: { michael@0: AssertIsInMainProcess(); michael@0: AssertIsOnMainThread(); michael@0: MOZ_ASSERT(aEventTarget); michael@0: } michael@0: michael@0: private: michael@0: ~ParentCreateCallback() michael@0: { } michael@0: michael@0: virtual void michael@0: Success(already_AddRefed aActor, MessageLoop* aMessageLoop) michael@0: MOZ_OVERRIDE; michael@0: michael@0: virtual void michael@0: Failure() MOZ_OVERRIDE; michael@0: }; michael@0: michael@0: class ChildImpl::CreateCallbackRunnable : public nsRunnable michael@0: { michael@0: protected: michael@0: nsRefPtr mActor; michael@0: michael@0: public: michael@0: CreateCallbackRunnable(already_AddRefed&& aActor) michael@0: : mActor(aActor) michael@0: { michael@0: // May be created on any thread! michael@0: michael@0: MOZ_ASSERT(mActor); michael@0: } michael@0: michael@0: CreateCallbackRunnable() michael@0: { michael@0: // May be created on any thread! michael@0: } michael@0: michael@0: NS_DECL_ISUPPORTS_INHERITED michael@0: michael@0: protected: michael@0: virtual ~CreateCallbackRunnable(); michael@0: michael@0: static already_AddRefed michael@0: GetNextCallback(); michael@0: michael@0: NS_DECL_NSIRUNNABLE michael@0: }; michael@0: michael@0: class ChildImpl::OpenChildProcessActorRunnable MOZ_FINAL : michael@0: public ChildImpl::CreateCallbackRunnable michael@0: { michael@0: nsAutoPtr mTransport; michael@0: ProcessHandle mProcessHandle; michael@0: michael@0: public: michael@0: OpenChildProcessActorRunnable(already_AddRefed&& aActor, michael@0: Transport* aTransport, michael@0: ProcessHandle aProcessHandle) michael@0: : CreateCallbackRunnable(Move(aActor)), mTransport(aTransport), michael@0: mProcessHandle(aProcessHandle) michael@0: { michael@0: AssertIsOnMainThread(); michael@0: MOZ_ASSERT(aTransport); michael@0: } michael@0: michael@0: NS_DECL_ISUPPORTS_INHERITED michael@0: michael@0: private: michael@0: ~OpenChildProcessActorRunnable() michael@0: { michael@0: if (mTransport) { michael@0: CRASH_IN_CHILD_PROCESS("Leaking transport!"); michael@0: unused << mTransport.forget(); michael@0: } michael@0: } michael@0: michael@0: NS_DECL_NSIRUNNABLE michael@0: }; michael@0: michael@0: class ChildImpl::OpenMainProcessActorRunnable MOZ_FINAL : michael@0: public ChildImpl::CreateCallbackRunnable michael@0: { michael@0: nsRefPtr mParentActor; michael@0: MessageLoop* mParentMessageLoop; michael@0: michael@0: public: michael@0: OpenMainProcessActorRunnable(already_AddRefed&& aChildActor, michael@0: already_AddRefed aParentActor, michael@0: MessageLoop* aParentMessageLoop) michael@0: : CreateCallbackRunnable(Move(aChildActor)), mParentActor(aParentActor), michael@0: mParentMessageLoop(aParentMessageLoop) michael@0: { michael@0: AssertIsOnMainThread(); michael@0: MOZ_ASSERT(mParentActor); michael@0: MOZ_ASSERT(aParentMessageLoop); michael@0: } michael@0: michael@0: NS_DECL_ISUPPORTS_INHERITED michael@0: michael@0: private: michael@0: ~OpenMainProcessActorRunnable() michael@0: { } michael@0: michael@0: NS_DECL_NSIRUNNABLE michael@0: }; michael@0: michael@0: } // anonymous namespace michael@0: michael@0: namespace mozilla { michael@0: namespace ipc { michael@0: michael@0: bool michael@0: IsOnBackgroundThread() michael@0: { michael@0: return ParentImpl::IsOnBackgroundThread(); michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: michael@0: void michael@0: AssertIsOnBackgroundThread() michael@0: { michael@0: ParentImpl::AssertIsOnBackgroundThread(); michael@0: } michael@0: michael@0: #endif // DEBUG michael@0: michael@0: } // namespace ipc michael@0: } // namespace mozilla michael@0: michael@0: // ----------------------------------------------------------------------------- michael@0: // BackgroundParent Public Methods michael@0: // ----------------------------------------------------------------------------- michael@0: michael@0: // static michael@0: bool michael@0: BackgroundParent::IsOtherProcessActor(PBackgroundParent* aBackgroundActor) michael@0: { michael@0: return ParentImpl::IsOtherProcessActor(aBackgroundActor); michael@0: } michael@0: michael@0: // static michael@0: already_AddRefed michael@0: BackgroundParent::GetContentParent(PBackgroundParent* aBackgroundActor) michael@0: { michael@0: return ParentImpl::GetContentParent(aBackgroundActor); michael@0: } michael@0: michael@0: // static michael@0: PBackgroundParent* michael@0: BackgroundParent::Alloc(ContentParent* aContent, michael@0: Transport* aTransport, michael@0: ProcessId aOtherProcess) michael@0: { michael@0: return ParentImpl::Alloc(aContent, aTransport, aOtherProcess); michael@0: } michael@0: michael@0: // ----------------------------------------------------------------------------- michael@0: // BackgroundChild Public Methods michael@0: // ----------------------------------------------------------------------------- michael@0: michael@0: // static michael@0: void michael@0: BackgroundChild::Startup() michael@0: { michael@0: ChildImpl::Startup(); michael@0: } michael@0: michael@0: // static michael@0: PBackgroundChild* michael@0: BackgroundChild::Alloc(Transport* aTransport, ProcessId aOtherProcess) michael@0: { michael@0: return ChildImpl::Alloc(aTransport, aOtherProcess); michael@0: } michael@0: michael@0: // static michael@0: PBackgroundChild* michael@0: BackgroundChild::GetForCurrentThread() michael@0: { michael@0: return ChildImpl::GetForCurrentThread(); michael@0: } michael@0: michael@0: // static michael@0: bool michael@0: BackgroundChild::GetOrCreateForCurrentThread( michael@0: nsIIPCBackgroundChildCreateCallback* aCallback) michael@0: { michael@0: return ChildImpl::GetOrCreateForCurrentThread(aCallback); michael@0: } michael@0: michael@0: // ----------------------------------------------------------------------------- michael@0: // BackgroundChildImpl Public Methods michael@0: // ----------------------------------------------------------------------------- michael@0: michael@0: // static michael@0: BackgroundChildImpl::ThreadLocal* michael@0: BackgroundChildImpl::GetThreadLocalForCurrentThread() michael@0: { michael@0: return ChildImpl::GetThreadLocalForCurrentThread(); michael@0: } michael@0: michael@0: // ----------------------------------------------------------------------------- michael@0: // ParentImpl Static Members michael@0: // ----------------------------------------------------------------------------- michael@0: michael@0: const ParentImpl::ProcessHandle ParentImpl::kInvalidProcessHandle = michael@0: #ifdef XP_WIN michael@0: ProcessHandle(INVALID_HANDLE_VALUE); michael@0: #else michael@0: ProcessHandle(-1); michael@0: #endif michael@0: michael@0: StaticRefPtr ParentImpl::sBackgroundThread; michael@0: michael@0: nsTArray* ParentImpl::sLiveActorsForBackgroundThread; michael@0: michael@0: StaticRefPtr ParentImpl::sShutdownTimer; michael@0: michael@0: Atomic ParentImpl::sBackgroundPRThread; michael@0: michael@0: MessageLoop* ParentImpl::sBackgroundThreadMessageLoop = nullptr; michael@0: michael@0: uint64_t ParentImpl::sLiveActorCount = 0; michael@0: michael@0: bool ParentImpl::sShutdownObserverRegistered = false; michael@0: michael@0: bool ParentImpl::sShutdownHasStarted = false; michael@0: michael@0: StaticAutoPtr>> michael@0: ParentImpl::sPendingCallbacks; michael@0: michael@0: // ----------------------------------------------------------------------------- michael@0: // ChildImpl Static Members michael@0: // ----------------------------------------------------------------------------- michael@0: michael@0: unsigned int ChildImpl::sThreadLocalIndex = kBadThreadLocalIndex; michael@0: michael@0: StaticAutoPtr>> ChildImpl::sPendingTargets; michael@0: michael@0: bool ChildImpl::sShutdownHasStarted = false; michael@0: michael@0: // ----------------------------------------------------------------------------- michael@0: // ParentImpl Implementation michael@0: // ----------------------------------------------------------------------------- michael@0: michael@0: // static michael@0: bool michael@0: ParentImpl::IsOtherProcessActor(PBackgroundParent* aBackgroundActor) michael@0: { michael@0: AssertIsOnBackgroundThread(); michael@0: MOZ_ASSERT(aBackgroundActor); michael@0: michael@0: return static_cast(aBackgroundActor)->mIsOtherProcessActor; michael@0: } michael@0: michael@0: // static michael@0: already_AddRefed michael@0: ParentImpl::GetContentParent(PBackgroundParent* aBackgroundActor) michael@0: { michael@0: AssertIsOnBackgroundThread(); michael@0: MOZ_ASSERT(aBackgroundActor); michael@0: michael@0: auto actor = static_cast(aBackgroundActor); michael@0: if (actor->mActorDestroyed) { michael@0: MOZ_ASSERT(false, "GetContentParent called after ActorDestroy was called!"); michael@0: return nullptr; michael@0: } michael@0: michael@0: if (actor->mContent) { michael@0: // We need to hand out a reference to our ContentParent but we also need to michael@0: // keep the one we have. We can't call AddRef here because ContentParent is michael@0: // not threadsafe so instead we dispatch a runnable to the main thread to do michael@0: // it for us. This is safe since we are guaranteed that our AddRef runnable michael@0: // will run before the reference we hand out can be released, and the michael@0: // ContentParent can't die as long as the existing reference is maintained. michael@0: nsCOMPtr runnable = michael@0: NS_NewNonOwningRunnableMethod(actor->mContent, &ContentParent::AddRef); michael@0: MOZ_ASSERT(runnable); michael@0: michael@0: MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable))); michael@0: } michael@0: michael@0: return actor->mContent.get(); michael@0: } michael@0: michael@0: // static michael@0: PBackgroundParent* michael@0: ParentImpl::Alloc(ContentParent* aContent, michael@0: Transport* aTransport, michael@0: ProcessId aOtherProcess) michael@0: { michael@0: AssertIsInMainProcess(); michael@0: AssertIsOnMainThread(); michael@0: MOZ_ASSERT(aTransport); michael@0: michael@0: ProcessHandle processHandle; michael@0: if (!base::OpenProcessHandle(aOtherProcess, &processHandle)) { michael@0: // Process has already died? michael@0: return nullptr; michael@0: } michael@0: michael@0: if (!sBackgroundThread && !CreateBackgroundThread()) { michael@0: NS_WARNING("Failed to create background thread!"); michael@0: return nullptr; michael@0: } michael@0: michael@0: MOZ_ASSERT(sLiveActorsForBackgroundThread); michael@0: michael@0: sLiveActorCount++; michael@0: michael@0: nsRefPtr actor = new ParentImpl(aContent, aTransport); michael@0: michael@0: nsCOMPtr connectRunnable = michael@0: new ConnectActorRunnable(actor, aTransport, processHandle, michael@0: sLiveActorsForBackgroundThread); michael@0: michael@0: if (NS_FAILED(sBackgroundThread->Dispatch(connectRunnable, michael@0: NS_DISPATCH_NORMAL))) { michael@0: NS_WARNING("Failed to dispatch connect runnable!"); michael@0: michael@0: MOZ_ASSERT(sLiveActorCount); michael@0: sLiveActorCount--; michael@0: michael@0: if (!sLiveActorCount) { michael@0: ShutdownBackgroundThread(); michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: return actor; michael@0: } michael@0: michael@0: // static michael@0: bool michael@0: ParentImpl::CreateActorForSameProcess(CreateCallback* aCallback) michael@0: { michael@0: AssertIsInMainProcess(); michael@0: AssertIsOnMainThread(); michael@0: MOZ_ASSERT(aCallback); michael@0: michael@0: if (!sBackgroundThread && !CreateBackgroundThread()) { michael@0: NS_WARNING("Failed to create background thread!"); michael@0: return false; michael@0: } michael@0: michael@0: MOZ_ASSERT(!sShutdownHasStarted); michael@0: michael@0: sLiveActorCount++; michael@0: michael@0: if (sBackgroundThreadMessageLoop) { michael@0: nsCOMPtr callbackRunnable = michael@0: new CreateCallbackRunnable(aCallback); michael@0: MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(callbackRunnable))); michael@0: return true; michael@0: } michael@0: michael@0: if (!sPendingCallbacks) { michael@0: sPendingCallbacks = new nsTArray>(); michael@0: } michael@0: michael@0: sPendingCallbacks->AppendElement(aCallback); michael@0: return true; michael@0: } michael@0: michael@0: // static michael@0: bool michael@0: ParentImpl::CreateBackgroundThread() michael@0: { michael@0: AssertIsInMainProcess(); michael@0: AssertIsOnMainThread(); michael@0: MOZ_ASSERT(!sBackgroundThread); michael@0: MOZ_ASSERT(!sLiveActorsForBackgroundThread); michael@0: michael@0: if (sShutdownHasStarted) { michael@0: NS_WARNING("Trying to create background thread after shutdown has " michael@0: "already begun!"); michael@0: return false; michael@0: } michael@0: michael@0: nsCOMPtr newShutdownTimer; michael@0: michael@0: if (!sShutdownTimer) { michael@0: nsresult rv; michael@0: newShutdownTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: if (!sShutdownObserverRegistered) { michael@0: nsCOMPtr obs = services::GetObserverService(); michael@0: if (NS_WARN_IF(!obs)) { michael@0: return false; michael@0: } michael@0: michael@0: nsCOMPtr observer = new ShutdownObserver(); michael@0: michael@0: nsresult rv = michael@0: obs->AddObserver(observer, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, false); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) { michael@0: return false; michael@0: } michael@0: michael@0: sShutdownObserverRegistered = true; michael@0: } michael@0: michael@0: nsCOMPtr thread; michael@0: if (NS_FAILED(NS_NewNamedThread("IPDL Background", getter_AddRefs(thread)))) { michael@0: NS_WARNING("NS_NewNamedThread failed!"); michael@0: return false; michael@0: } michael@0: michael@0: nsCOMPtr messageLoopRunnable = michael@0: new RequestMessageLoopRunnable(thread); michael@0: if (NS_FAILED(thread->Dispatch(messageLoopRunnable, NS_DISPATCH_NORMAL))) { michael@0: NS_WARNING("Failed to dispatch RequestMessageLoopRunnable!"); michael@0: return false; michael@0: } michael@0: michael@0: sBackgroundThread = thread; michael@0: sLiveActorsForBackgroundThread = new nsTArray(1); michael@0: michael@0: if (!sShutdownTimer) { michael@0: MOZ_ASSERT(newShutdownTimer); michael@0: sShutdownTimer = newShutdownTimer; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: // static michael@0: void michael@0: ParentImpl::ShutdownBackgroundThread() michael@0: { michael@0: AssertIsInMainProcess(); michael@0: AssertIsOnMainThread(); michael@0: MOZ_ASSERT_IF(!sBackgroundThread, !sBackgroundThreadMessageLoop); michael@0: MOZ_ASSERT_IF(!sShutdownHasStarted, !sLiveActorCount); michael@0: MOZ_ASSERT_IF(!sBackgroundThread, !sLiveActorCount); michael@0: MOZ_ASSERT_IF(sBackgroundThread, sShutdownTimer); michael@0: michael@0: if (sPendingCallbacks) { michael@0: if (!sPendingCallbacks->IsEmpty()) { michael@0: nsTArray> callbacks; michael@0: sPendingCallbacks->SwapElements(callbacks); michael@0: michael@0: for (uint32_t index = 0; index < callbacks.Length(); index++) { michael@0: nsRefPtr callback; michael@0: callbacks[index].swap(callback); michael@0: MOZ_ASSERT(callback); michael@0: michael@0: callback->Failure(); michael@0: } michael@0: } michael@0: michael@0: if (sShutdownHasStarted) { michael@0: sPendingCallbacks = nullptr; michael@0: } michael@0: } michael@0: michael@0: nsCOMPtr shutdownTimer; michael@0: if (sShutdownHasStarted) { michael@0: shutdownTimer = sShutdownTimer.get(); michael@0: sShutdownTimer = nullptr; michael@0: } michael@0: michael@0: if (sBackgroundThread) { michael@0: nsCOMPtr thread = sBackgroundThread.get(); michael@0: nsAutoPtr> liveActors(sLiveActorsForBackgroundThread); michael@0: michael@0: sBackgroundThread = nullptr; michael@0: sLiveActorsForBackgroundThread = nullptr; michael@0: sBackgroundThreadMessageLoop = nullptr; michael@0: michael@0: MOZ_ASSERT_IF(!sShutdownHasStarted, !sLiveActorCount); michael@0: michael@0: if (sShutdownHasStarted) { michael@0: // If this is final shutdown then we need to spin the event loop while we michael@0: // wait for all the actors to be cleaned up. We also set a timeout to michael@0: // force-kill any hanging actors. michael@0: michael@0: if (sLiveActorCount) { michael@0: TimerCallbackClosure closure(thread, liveActors); michael@0: michael@0: MOZ_ALWAYS_TRUE(NS_SUCCEEDED( michael@0: shutdownTimer->InitWithFuncCallback(&ShutdownTimerCallback, &closure, michael@0: kShutdownTimerDelayMS, michael@0: nsITimer::TYPE_ONE_SHOT))); michael@0: michael@0: nsIThread* currentThread = NS_GetCurrentThread(); michael@0: MOZ_ASSERT(currentThread); michael@0: michael@0: while (sLiveActorCount) { michael@0: NS_ProcessNextEvent(currentThread); michael@0: } michael@0: michael@0: MOZ_ASSERT(liveActors->IsEmpty()); michael@0: michael@0: MOZ_ALWAYS_TRUE(NS_SUCCEEDED(shutdownTimer->Cancel())); michael@0: } michael@0: } michael@0: michael@0: // Dispatch this runnable to unregister the thread from the profiler. michael@0: nsCOMPtr shutdownRunnable = michael@0: new ShutdownBackgroundThreadRunnable(); michael@0: MOZ_ALWAYS_TRUE(NS_SUCCEEDED(thread->Dispatch(shutdownRunnable, michael@0: NS_DISPATCH_NORMAL))); michael@0: michael@0: MOZ_ALWAYS_TRUE(NS_SUCCEEDED(thread->Shutdown())); michael@0: } michael@0: } michael@0: michael@0: // static michael@0: void michael@0: ParentImpl::ShutdownTimerCallback(nsITimer* aTimer, void* aClosure) michael@0: { michael@0: AssertIsInMainProcess(); michael@0: AssertIsOnMainThread(); michael@0: MOZ_ASSERT(sShutdownHasStarted); michael@0: MOZ_ASSERT(sLiveActorCount); michael@0: michael@0: auto closure = static_cast(aClosure); michael@0: MOZ_ASSERT(closure); michael@0: michael@0: // Don't let the stack unwind until the ForceCloseBackgroundActorsRunnable has michael@0: // finished. michael@0: sLiveActorCount++; michael@0: michael@0: nsCOMPtr forceCloseRunnable = michael@0: new ForceCloseBackgroundActorsRunnable(closure->mLiveActors); michael@0: MOZ_ALWAYS_TRUE(NS_SUCCEEDED(closure->mThread->Dispatch(forceCloseRunnable, michael@0: NS_DISPATCH_NORMAL))); michael@0: } michael@0: michael@0: void michael@0: ParentImpl::Destroy() michael@0: { michael@0: // May be called on any thread! michael@0: michael@0: AssertIsInMainProcess(); michael@0: michael@0: nsCOMPtr destroyRunnable = michael@0: NS_NewNonOwningRunnableMethod(this, &ParentImpl::MainThreadActorDestroy); michael@0: MOZ_ASSERT(destroyRunnable); michael@0: michael@0: MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(destroyRunnable))); michael@0: } michael@0: michael@0: void michael@0: ParentImpl::MainThreadActorDestroy() michael@0: { michael@0: AssertIsInMainProcess(); michael@0: AssertIsOnMainThread(); michael@0: MOZ_ASSERT_IF(mIsOtherProcessActor, mContent); michael@0: MOZ_ASSERT_IF(!mIsOtherProcessActor, !mContent); michael@0: MOZ_ASSERT_IF(mIsOtherProcessActor, mTransport); michael@0: MOZ_ASSERT_IF(!mIsOtherProcessActor, !mTransport); michael@0: michael@0: if (mTransport) { michael@0: XRE_GetIOMessageLoop()->PostTask(FROM_HERE, michael@0: new DeleteTask(mTransport)); michael@0: mTransport = nullptr; michael@0: } michael@0: michael@0: ProcessHandle otherProcess = OtherProcess(); michael@0: if (otherProcess != kInvalidProcessHandle) { michael@0: base::CloseProcessHandle(otherProcess); michael@0: #ifdef DEBUG michael@0: SetOtherProcess(kInvalidProcessHandle); michael@0: #endif michael@0: } michael@0: michael@0: mContent = nullptr; michael@0: michael@0: MOZ_ASSERT(sLiveActorCount); michael@0: sLiveActorCount--; michael@0: michael@0: if (!sLiveActorCount) { michael@0: ShutdownBackgroundThread(); michael@0: } michael@0: michael@0: // This may be the last reference! michael@0: Release(); michael@0: } michael@0: michael@0: IToplevelProtocol* michael@0: ParentImpl::CloneToplevel(const InfallibleTArray& aFds, michael@0: ProcessHandle aPeerProcess, michael@0: ProtocolCloneContext* aCtx) michael@0: { michael@0: AssertIsInMainProcess(); michael@0: AssertIsOnMainThread(); michael@0: michael@0: const ProtocolId protocolId = GetProtocolId(); michael@0: michael@0: for (unsigned int i = 0; i < aFds.Length(); i++) { michael@0: if (static_cast(aFds[i].protocolId()) != protocolId) { michael@0: continue; michael@0: } michael@0: michael@0: Transport* transport = OpenDescriptor(aFds[i].fd(), michael@0: Transport::MODE_SERVER); michael@0: if (!transport) { michael@0: NS_WARNING("Failed to open transport!"); michael@0: break; michael@0: } michael@0: michael@0: PBackgroundParent* clonedActor = michael@0: Alloc(mContent, transport, base::GetProcId(aPeerProcess)); michael@0: MOZ_ASSERT(clonedActor); michael@0: michael@0: clonedActor->CloneManagees(this, aCtx); michael@0: clonedActor->SetTransport(transport); michael@0: michael@0: return clonedActor; michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: void michael@0: ParentImpl::ActorDestroy(ActorDestroyReason aWhy) michael@0: { michael@0: AssertIsInMainProcess(); michael@0: AssertIsOnBackgroundThread(); michael@0: MOZ_ASSERT(!mActorDestroyed); michael@0: MOZ_ASSERT_IF(mIsOtherProcessActor, mLiveActorArray); michael@0: michael@0: BackgroundParentImpl::ActorDestroy(aWhy); michael@0: michael@0: mActorDestroyed = true; michael@0: michael@0: if (mLiveActorArray) { michael@0: MOZ_ALWAYS_TRUE(mLiveActorArray->RemoveElement(this)); michael@0: mLiveActorArray = nullptr; michael@0: } michael@0: michael@0: // This is tricky. We should be able to call Destroy() here directly because michael@0: // we're not going to touch 'this' or our MessageChannel any longer on this michael@0: // thread. Destroy() dispatches the MainThreadActorDestroy runnable and when michael@0: // it runs it will destroy 'this' and our associated MessageChannel. However, michael@0: // IPDL is about to call MessageChannel::Clear() on this thread! To avoid michael@0: // racing with the main thread we must ensure that the MessageChannel lives michael@0: // long enough to be cleared in this call stack. michael@0: nsCOMPtr destroyRunnable = michael@0: NS_NewNonOwningRunnableMethod(this, &ParentImpl::Destroy); michael@0: MOZ_ASSERT(destroyRunnable); michael@0: michael@0: MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(destroyRunnable))); michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(ParentImpl::ShutdownObserver, nsIObserver) michael@0: michael@0: NS_IMETHODIMP michael@0: ParentImpl::ShutdownObserver::Observe(nsISupports* aSubject, michael@0: const char* aTopic, michael@0: const char16_t* aData) michael@0: { michael@0: AssertIsInMainProcess(); michael@0: AssertIsOnMainThread(); michael@0: MOZ_ASSERT(!sShutdownHasStarted); michael@0: MOZ_ASSERT(!strcmp(aTopic, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID)); michael@0: michael@0: sShutdownHasStarted = true; michael@0: michael@0: // Do this first before calling (and spinning the event loop in) michael@0: // ShutdownBackgroundThread(). michael@0: ChildImpl::Shutdown(); michael@0: michael@0: ShutdownBackgroundThread(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS_INHERITED0(ParentImpl::RequestMessageLoopRunnable, michael@0: nsRunnable) michael@0: michael@0: NS_IMETHODIMP michael@0: ParentImpl::RequestMessageLoopRunnable::Run() michael@0: { michael@0: AssertIsInMainProcess(); michael@0: MOZ_ASSERT(mTargetThread); michael@0: michael@0: if (NS_IsMainThread()) { michael@0: MOZ_ASSERT(mMessageLoop); michael@0: michael@0: if (!sBackgroundThread || michael@0: !SameCOMIdentity(mTargetThread.get(), sBackgroundThread.get())) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: MOZ_ASSERT(!sBackgroundThreadMessageLoop); michael@0: sBackgroundThreadMessageLoop = mMessageLoop; michael@0: michael@0: if (sPendingCallbacks && !sPendingCallbacks->IsEmpty()) { michael@0: nsTArray> callbacks; michael@0: sPendingCallbacks->SwapElements(callbacks); michael@0: michael@0: for (uint32_t index = 0; index < callbacks.Length(); index++) { michael@0: MOZ_ASSERT(callbacks[index]); michael@0: michael@0: nsCOMPtr callbackRunnable = michael@0: new CreateCallbackRunnable(callbacks[index]); michael@0: if (NS_FAILED(NS_DispatchToCurrentThread(callbackRunnable))) { michael@0: NS_WARNING("Failed to dispatch callback runnable!"); michael@0: } michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: char stackBaseGuess; michael@0: profiler_register_thread("IPDL Background", &stackBaseGuess); michael@0: michael@0: #ifdef DEBUG michael@0: { michael@0: bool correctThread; michael@0: MOZ_ASSERT(NS_SUCCEEDED(mTargetThread->IsOnCurrentThread(&correctThread))); michael@0: MOZ_ASSERT(correctThread); michael@0: } michael@0: #endif michael@0: michael@0: DebugOnly oldBackgroundThread = michael@0: sBackgroundPRThread.exchange(PR_GetCurrentThread()); michael@0: michael@0: MOZ_ASSERT_IF(oldBackgroundThread, michael@0: PR_GetCurrentThread() != oldBackgroundThread); michael@0: michael@0: MOZ_ASSERT(!mMessageLoop); michael@0: michael@0: mMessageLoop = MessageLoop::current(); michael@0: MOZ_ASSERT(mMessageLoop); michael@0: michael@0: if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) { michael@0: NS_WARNING("Failed to dispatch RequestMessageLoopRunnable to main thread!"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS_INHERITED0(ParentImpl::ShutdownBackgroundThreadRunnable, michael@0: nsRunnable) michael@0: michael@0: NS_IMETHODIMP michael@0: ParentImpl::ShutdownBackgroundThreadRunnable::Run() michael@0: { michael@0: AssertIsInMainProcess(); michael@0: michael@0: // It is possible that another background thread was created while this thread michael@0: // was shutting down. In that case we can't assert anything about michael@0: // sBackgroundPRThread and we should not modify it here. michael@0: sBackgroundPRThread.compareExchange(PR_GetCurrentThread(), nullptr); michael@0: michael@0: profiler_unregister_thread(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS_INHERITED0(ParentImpl::ForceCloseBackgroundActorsRunnable, michael@0: nsRunnable) michael@0: michael@0: NS_IMETHODIMP michael@0: ParentImpl::ForceCloseBackgroundActorsRunnable::Run() michael@0: { michael@0: AssertIsInMainProcess(); michael@0: MOZ_ASSERT(mActorArray); michael@0: michael@0: if (NS_IsMainThread()) { michael@0: MOZ_ASSERT(sLiveActorCount); michael@0: sLiveActorCount--; michael@0: return NS_OK; michael@0: } michael@0: michael@0: AssertIsOnBackgroundThread(); michael@0: michael@0: if (!mActorArray->IsEmpty()) { michael@0: // Copy the array since calling Close() could mutate the actual array. michael@0: nsTArray actorsToClose(*mActorArray); michael@0: michael@0: for (uint32_t index = 0; index < actorsToClose.Length(); index++) { michael@0: actorsToClose[index]->Close(); michael@0: } michael@0: } michael@0: michael@0: MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(this))); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS_INHERITED0(ParentImpl::CreateCallbackRunnable, nsRunnable) michael@0: michael@0: NS_IMETHODIMP michael@0: ParentImpl::CreateCallbackRunnable::Run() michael@0: { michael@0: AssertIsInMainProcess(); michael@0: AssertIsOnMainThread(); michael@0: MOZ_ASSERT(sBackgroundThreadMessageLoop); michael@0: MOZ_ASSERT(mCallback); michael@0: michael@0: nsRefPtr callback; michael@0: mCallback.swap(callback); michael@0: michael@0: nsRefPtr actor = new ParentImpl(); michael@0: michael@0: callback->Success(actor.forget(), sBackgroundThreadMessageLoop); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS_INHERITED0(ParentImpl::ConnectActorRunnable, nsRunnable) michael@0: michael@0: NS_IMETHODIMP michael@0: ParentImpl::ConnectActorRunnable::Run() michael@0: { michael@0: AssertIsInMainProcess(); michael@0: AssertIsOnBackgroundThread(); michael@0: michael@0: // Transfer ownership to this thread. If Open() fails then we will release michael@0: // this reference in Destroy. michael@0: ParentImpl* actor; michael@0: mActor.forget(&actor); michael@0: michael@0: if (!actor->Open(mTransport, mProcessHandle, XRE_GetIOMessageLoop(), michael@0: ParentSide)) { michael@0: actor->Destroy(); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: actor->SetLiveActorArray(mLiveActorArray); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // ----------------------------------------------------------------------------- michael@0: // ChildImpl Implementation michael@0: // ----------------------------------------------------------------------------- michael@0: michael@0: // static michael@0: void michael@0: ChildImpl::Startup() michael@0: { michael@0: // This happens on the main thread but before XPCOM has started so we can't michael@0: // assert that we're being called on the main thread here. michael@0: michael@0: MOZ_ASSERT(sThreadLocalIndex == kBadThreadLocalIndex, michael@0: "BackgroundChild::Startup() called more than once!"); michael@0: michael@0: PRStatus status = michael@0: PR_NewThreadPrivateIndex(&sThreadLocalIndex, ThreadLocalDestructor); michael@0: MOZ_RELEASE_ASSERT(status == PR_SUCCESS, "PR_NewThreadPrivateIndex failed!"); michael@0: michael@0: MOZ_ASSERT(sThreadLocalIndex != kBadThreadLocalIndex); michael@0: michael@0: nsCOMPtr observerService = services::GetObserverService(); michael@0: MOZ_RELEASE_ASSERT(observerService); michael@0: michael@0: nsCOMPtr observer = new ShutdownObserver(); michael@0: michael@0: nsresult rv = michael@0: observerService->AddObserver(observer, michael@0: NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, michael@0: false); michael@0: MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv)); michael@0: } michael@0: michael@0: // static michael@0: void michael@0: ChildImpl::Shutdown() michael@0: { michael@0: AssertIsOnMainThread(); michael@0: michael@0: if (sShutdownHasStarted) { michael@0: MOZ_ASSERT_IF(sThreadLocalIndex != kBadThreadLocalIndex, michael@0: !PR_GetThreadPrivate(sThreadLocalIndex)); michael@0: return; michael@0: } michael@0: michael@0: sShutdownHasStarted = true; michael@0: michael@0: MOZ_ASSERT(sThreadLocalIndex != kBadThreadLocalIndex); michael@0: michael@0: DebugOnly status = PR_SetThreadPrivate(sThreadLocalIndex, nullptr); michael@0: MOZ_ASSERT(status == PR_SUCCESS); michael@0: } michael@0: michael@0: // static michael@0: PBackgroundChild* michael@0: ChildImpl::Alloc(Transport* aTransport, ProcessId aOtherProcess) michael@0: { michael@0: AssertIsInChildProcess(); michael@0: AssertIsOnMainThread(); michael@0: MOZ_ASSERT(aTransport); michael@0: MOZ_ASSERT(sPendingTargets); michael@0: MOZ_ASSERT(!sPendingTargets->IsEmpty()); michael@0: michael@0: nsCOMPtr eventTarget; michael@0: sPendingTargets->ElementAt(0).swap(eventTarget); michael@0: michael@0: sPendingTargets->RemoveElementAt(0); michael@0: michael@0: ProcessHandle processHandle; michael@0: if (!base::OpenProcessHandle(aOtherProcess, &processHandle)) { michael@0: MOZ_CRASH("Failed to open process handle!"); michael@0: } michael@0: michael@0: nsRefPtr actor = new ChildImpl(); michael@0: michael@0: ChildImpl* weakActor = actor; michael@0: michael@0: nsCOMPtr openRunnable = michael@0: new OpenChildProcessActorRunnable(actor.forget(), aTransport, michael@0: processHandle); michael@0: if (NS_FAILED(eventTarget->Dispatch(openRunnable, NS_DISPATCH_NORMAL))) { michael@0: MOZ_CRASH("Failed to dispatch OpenActorRunnable!"); michael@0: } michael@0: michael@0: // This value is only checked against null to determine success/failure, so michael@0: // there is no need to worry about the reference count here. michael@0: return weakActor; michael@0: } michael@0: michael@0: // static michael@0: PBackgroundChild* michael@0: ChildImpl::GetForCurrentThread() michael@0: { michael@0: MOZ_ASSERT(sThreadLocalIndex != kBadThreadLocalIndex); michael@0: michael@0: auto threadLocalInfo = michael@0: static_cast(PR_GetThreadPrivate(sThreadLocalIndex)); michael@0: michael@0: return threadLocalInfo ? threadLocalInfo->mActor : nullptr; michael@0: } michael@0: michael@0: // static michael@0: bool michael@0: ChildImpl::GetOrCreateForCurrentThread( michael@0: nsIIPCBackgroundChildCreateCallback* aCallback) michael@0: { michael@0: MOZ_ASSERT(aCallback); michael@0: MOZ_ASSERT(sThreadLocalIndex != kBadThreadLocalIndex, michael@0: "BackgroundChild::Startup() was never called!"); michael@0: michael@0: bool created = false; michael@0: michael@0: auto threadLocalInfo = michael@0: static_cast(PR_GetThreadPrivate(sThreadLocalIndex)); michael@0: michael@0: if (threadLocalInfo) { michael@0: threadLocalInfo->mCallbacks.AppendElement(aCallback); michael@0: } else { michael@0: nsAutoPtr newInfo(new ThreadLocalInfo(aCallback)); michael@0: michael@0: if (PR_SetThreadPrivate(sThreadLocalIndex, newInfo) != PR_SUCCESS) { michael@0: CRASH_IN_CHILD_PROCESS("PR_SetThreadPrivate failed!"); michael@0: return false; michael@0: } michael@0: michael@0: created = true; michael@0: threadLocalInfo = newInfo.forget(); michael@0: } michael@0: michael@0: if (threadLocalInfo->mActor) { michael@0: nsRefPtr actor = threadLocalInfo->mActor; michael@0: michael@0: nsCOMPtr runnable = new CreateCallbackRunnable(actor.forget()); michael@0: MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(runnable))); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: if (!created) { michael@0: // We have already started the sequence for opening the actor so there's michael@0: // nothing else we need to do here. This callback will be called after the michael@0: // first callback in CreateCallbackRunnable::Run(). michael@0: return true; michael@0: } michael@0: michael@0: if (NS_IsMainThread()) { michael@0: if (NS_WARN_IF(!OpenProtocolOnMainThread(NS_GetCurrentThread()))) { michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: nsRefPtr runnable = new CreateActorRunnable(); michael@0: if (NS_FAILED(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))) { michael@0: CRASH_IN_CHILD_PROCESS("Failed to dispatch to main thread!"); michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: // static michael@0: BackgroundChildImpl::ThreadLocal* michael@0: ChildImpl::GetThreadLocalForCurrentThread() michael@0: { michael@0: MOZ_ASSERT(sThreadLocalIndex != kBadThreadLocalIndex, michael@0: "BackgroundChild::Startup() was never called!"); michael@0: michael@0: auto threadLocalInfo = michael@0: static_cast(PR_GetThreadPrivate(sThreadLocalIndex)); michael@0: michael@0: if (!threadLocalInfo) { michael@0: return nullptr; michael@0: } michael@0: michael@0: if (!threadLocalInfo->mConsumerThreadLocal) { michael@0: threadLocalInfo->mConsumerThreadLocal = michael@0: new BackgroundChildImpl::ThreadLocal(); michael@0: } michael@0: michael@0: return threadLocalInfo->mConsumerThreadLocal; michael@0: } michael@0: michael@0: ChildImpl::CreateCallbackRunnable::~CreateCallbackRunnable() michael@0: { michael@0: if (mActor) { michael@0: CRASH_IN_CHILD_PROCESS("Leaking actor!"); michael@0: unused << mActor.forget(); michael@0: } michael@0: } michael@0: michael@0: // static michael@0: already_AddRefed michael@0: ChildImpl::CreateCallbackRunnable::GetNextCallback() michael@0: { michael@0: // May run on any thread! michael@0: michael@0: auto threadLocalInfo = michael@0: static_cast(PR_GetThreadPrivate(sThreadLocalIndex)); michael@0: MOZ_ASSERT(threadLocalInfo); michael@0: michael@0: if (threadLocalInfo->mCallbacks.IsEmpty()) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsCOMPtr callback; michael@0: threadLocalInfo->mCallbacks[0].swap(callback); michael@0: michael@0: threadLocalInfo->mCallbacks.RemoveElementAt(0); michael@0: michael@0: return callback.forget(); michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS_INHERITED0(ChildImpl::CreateCallbackRunnable, nsRunnable) michael@0: michael@0: NS_IMETHODIMP michael@0: ChildImpl::CreateCallbackRunnable::Run() michael@0: { michael@0: // May run on any thread! michael@0: michael@0: nsRefPtr actor; michael@0: mActor.swap(actor); michael@0: michael@0: nsCOMPtr callback = GetNextCallback(); michael@0: while (callback) { michael@0: if (actor) { michael@0: callback->ActorCreated(actor); michael@0: } else { michael@0: callback->ActorFailed(); michael@0: } michael@0: michael@0: callback = GetNextCallback(); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS_INHERITED0(ChildImpl::OpenChildProcessActorRunnable, michael@0: ChildImpl::CreateCallbackRunnable) michael@0: michael@0: NS_IMETHODIMP michael@0: ChildImpl::OpenChildProcessActorRunnable::Run() michael@0: { michael@0: // May be run on any thread! michael@0: michael@0: AssertIsInChildProcess(); michael@0: MOZ_ASSERT(mActor); michael@0: MOZ_ASSERT(mTransport); michael@0: michael@0: nsCOMPtr callback = GetNextCallback(); michael@0: MOZ_ASSERT(callback, michael@0: "There should be at least one callback when first creating the " michael@0: "actor!"); michael@0: michael@0: nsRefPtr strongActor; michael@0: mActor.swap(strongActor); michael@0: michael@0: if (!strongActor->Open(mTransport.forget(), mProcessHandle, michael@0: XRE_GetIOMessageLoop(), ChildSide)) { michael@0: CRASH_IN_CHILD_PROCESS("Failed to open ChildImpl!"); michael@0: michael@0: while (callback) { michael@0: callback->ActorFailed(); michael@0: callback = GetNextCallback(); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Now that Open() has succeeded transfer the ownership of the actor to IPDL. michael@0: auto threadLocalInfo = michael@0: static_cast(PR_GetThreadPrivate(sThreadLocalIndex)); michael@0: michael@0: MOZ_ASSERT(threadLocalInfo); michael@0: MOZ_ASSERT(!threadLocalInfo->mActor); michael@0: michael@0: nsRefPtr& actor = threadLocalInfo->mActor; michael@0: strongActor.swap(actor); michael@0: michael@0: actor->SetBoundThread(); michael@0: michael@0: while (callback) { michael@0: callback->ActorCreated(actor); michael@0: callback = GetNextCallback(); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS_INHERITED0(ChildImpl::OpenMainProcessActorRunnable, michael@0: ChildImpl::CreateCallbackRunnable) michael@0: michael@0: NS_IMETHODIMP michael@0: ChildImpl::OpenMainProcessActorRunnable::Run() michael@0: { michael@0: // May run on any thread! michael@0: michael@0: AssertIsInMainProcess(); michael@0: MOZ_ASSERT(mActor); michael@0: MOZ_ASSERT(mParentActor); michael@0: MOZ_ASSERT(mParentMessageLoop); michael@0: michael@0: nsCOMPtr callback = GetNextCallback(); michael@0: MOZ_ASSERT(callback, michael@0: "There should be at least one callback when first creating the " michael@0: "actor!"); michael@0: michael@0: nsRefPtr strongChildActor; michael@0: mActor.swap(strongChildActor); michael@0: michael@0: nsRefPtr parentActor; michael@0: mParentActor.swap(parentActor); michael@0: michael@0: MessageChannel* parentChannel = parentActor->GetIPCChannel(); michael@0: MOZ_ASSERT(parentChannel); michael@0: michael@0: if (!strongChildActor->Open(parentChannel, mParentMessageLoop, ChildSide)) { michael@0: NS_WARNING("Failed to open ChildImpl!"); michael@0: michael@0: parentActor->Destroy(); michael@0: michael@0: while (callback) { michael@0: callback->ActorFailed(); michael@0: callback = GetNextCallback(); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Now that Open() has succeeded transfer the ownership of the actors to IPDL. michael@0: unused << parentActor.forget(); michael@0: michael@0: auto threadLocalInfo = michael@0: static_cast(PR_GetThreadPrivate(sThreadLocalIndex)); michael@0: michael@0: MOZ_ASSERT(threadLocalInfo); michael@0: MOZ_ASSERT(!threadLocalInfo->mActor); michael@0: michael@0: nsRefPtr& childActor = threadLocalInfo->mActor; michael@0: strongChildActor.swap(childActor); michael@0: michael@0: childActor->SetBoundThread(); michael@0: michael@0: while (callback) { michael@0: callback->ActorCreated(childActor); michael@0: callback = GetNextCallback(); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS_INHERITED0(ChildImpl::CreateActorRunnable, nsRunnable) michael@0: michael@0: NS_IMETHODIMP michael@0: ChildImpl::CreateActorRunnable::Run() michael@0: { michael@0: AssertIsOnMainThread(); michael@0: michael@0: if (!OpenProtocolOnMainThread(mEventTarget)) { michael@0: NS_WARNING("OpenProtocolOnMainThread failed!"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: ChildImpl::ParentCreateCallback::Success( michael@0: already_AddRefed aParentActor, michael@0: MessageLoop* aParentMessageLoop) michael@0: { michael@0: AssertIsInMainProcess(); michael@0: AssertIsOnMainThread(); michael@0: michael@0: nsRefPtr parentActor = aParentActor; michael@0: MOZ_ASSERT(parentActor); michael@0: MOZ_ASSERT(aParentMessageLoop); michael@0: MOZ_ASSERT(mEventTarget); michael@0: michael@0: nsRefPtr childActor = new ChildImpl(); michael@0: michael@0: nsCOMPtr target; michael@0: mEventTarget.swap(target); michael@0: michael@0: nsCOMPtr openRunnable = michael@0: new OpenMainProcessActorRunnable(childActor.forget(), parentActor.forget(), michael@0: aParentMessageLoop); michael@0: if (NS_FAILED(target->Dispatch(openRunnable, NS_DISPATCH_NORMAL))) { michael@0: NS_WARNING("Failed to dispatch open runnable!"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: ChildImpl::ParentCreateCallback::Failure() michael@0: { michael@0: AssertIsInMainProcess(); michael@0: AssertIsOnMainThread(); michael@0: MOZ_ASSERT(mEventTarget); michael@0: michael@0: nsCOMPtr target; michael@0: mEventTarget.swap(target); michael@0: michael@0: DispatchFailureCallback(target); michael@0: } michael@0: michael@0: // static michael@0: bool michael@0: ChildImpl::OpenProtocolOnMainThread(nsIEventTarget* aEventTarget) michael@0: { michael@0: AssertIsOnMainThread(); michael@0: MOZ_ASSERT(aEventTarget); michael@0: michael@0: if (sShutdownHasStarted) { michael@0: MOZ_CRASH("Called BackgroundChild::GetOrCreateForCurrentThread after " michael@0: "shutdown has started!"); michael@0: } michael@0: michael@0: if (IsMainProcess()) { michael@0: nsRefPtr parentCallback = michael@0: new ParentCreateCallback(aEventTarget); michael@0: michael@0: if (!ParentImpl::CreateActorForSameProcess(parentCallback)) { michael@0: NS_WARNING("BackgroundParent::CreateActor() failed!"); michael@0: DispatchFailureCallback(aEventTarget); michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: ContentChild* content = ContentChild::GetSingleton(); michael@0: MOZ_ASSERT(content); michael@0: michael@0: if (!PBackground::Open(content)) { michael@0: MOZ_CRASH("Failed to create top level actor!"); michael@0: return false; michael@0: } michael@0: michael@0: if (!sPendingTargets) { michael@0: sPendingTargets = new nsTArray>(1); michael@0: ClearOnShutdown(&sPendingTargets); michael@0: } michael@0: michael@0: sPendingTargets->AppendElement(aEventTarget); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: // static michael@0: void michael@0: ChildImpl::DispatchFailureCallback(nsIEventTarget* aEventTarget) michael@0: { michael@0: MOZ_ASSERT(aEventTarget); michael@0: michael@0: nsCOMPtr callbackRunnable = new CreateCallbackRunnable(); michael@0: if (NS_FAILED(aEventTarget->Dispatch(callbackRunnable, NS_DISPATCH_NORMAL))) { michael@0: NS_WARNING("Failed to dispatch CreateCallbackRunnable!"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: ChildImpl::ActorDestroy(ActorDestroyReason aWhy) michael@0: { michael@0: AssertIsOnBoundThread(); michael@0: michael@0: BackgroundChildImpl::ActorDestroy(aWhy); michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(ChildImpl::ShutdownObserver, nsIObserver) michael@0: michael@0: NS_IMETHODIMP michael@0: ChildImpl::ShutdownObserver::Observe(nsISupports* aSubject, michael@0: const char* aTopic, michael@0: const char16_t* aData) michael@0: { michael@0: AssertIsOnMainThread(); michael@0: MOZ_ASSERT(!strcmp(aTopic, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID)); michael@0: michael@0: ChildImpl::Shutdown(); michael@0: michael@0: return NS_OK; michael@0: }