diff -r 000000000000 -r 6474c204b198 ipc/glue/BackgroundImpl.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ipc/glue/BackgroundImpl.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,1963 @@ +/* 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; + friend class mozilla::ipc::BackgroundChildImpl; + + 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; + nsAutoPtr mConsumerThreadLocal; + }; + + // 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); + + // Forwarded from BackgroundChildImpl. + static BackgroundChildImpl::ThreadLocal* + GetThreadLocalForCurrentThread(); + + 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); +} + +// ----------------------------------------------------------------------------- +// BackgroundChildImpl Public Methods +// ----------------------------------------------------------------------------- + +// static +BackgroundChildImpl::ThreadLocal* +BackgroundChildImpl::GetThreadLocalForCurrentThread() +{ + return ChildImpl::GetThreadLocalForCurrentThread(); +} + +// ----------------------------------------------------------------------------- +// 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_ISUPPORTS(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; +} + +// static +BackgroundChildImpl::ThreadLocal* +ChildImpl::GetThreadLocalForCurrentThread() +{ + MOZ_ASSERT(sThreadLocalIndex != kBadThreadLocalIndex, + "BackgroundChild::Startup() was never called!"); + + auto threadLocalInfo = + static_cast(PR_GetThreadPrivate(sThreadLocalIndex)); + + if (!threadLocalInfo) { + return nullptr; + } + + if (!threadLocalInfo->mConsumerThreadLocal) { + threadLocalInfo->mConsumerThreadLocal = + new BackgroundChildImpl::ThreadLocal(); + } + + return threadLocalInfo->mConsumerThreadLocal; +} + +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_ISUPPORTS(ChildImpl::ShutdownObserver, nsIObserver) + +NS_IMETHODIMP +ChildImpl::ShutdownObserver::Observe(nsISupports* aSubject, + const char* aTopic, + const char16_t* aData) +{ + AssertIsOnMainThread(); + MOZ_ASSERT(!strcmp(aTopic, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID)); + + ChildImpl::Shutdown(); + + return NS_OK; +}