summaryrefslogtreecommitdiffstats
path: root/ipc/glue/BackgroundImpl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ipc/glue/BackgroundImpl.cpp')
-rw-r--r--ipc/glue/BackgroundImpl.cpp2092
1 files changed, 2092 insertions, 0 deletions
diff --git a/ipc/glue/BackgroundImpl.cpp b/ipc/glue/BackgroundImpl.cpp
new file mode 100644
index 000000000..2f8e073f8
--- /dev/null
+++ b/ipc/glue/BackgroundImpl.cpp
@@ -0,0 +1,2092 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "BackgroundChild.h"
+#include "BackgroundParent.h"
+
+#include "BackgroundChildImpl.h"
+#include "BackgroundParentImpl.h"
+#include "base/process_util.h"
+#include "base/task.h"
+#include "FileDescriptor.h"
+#include "GeckoProfiler.h"
+#include "InputStreamUtils.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/Services.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/Unused.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/File.h"
+#include "mozilla/dom/ipc/BlobChild.h"
+#include "mozilla/dom/ipc/BlobParent.h"
+#include "mozilla/dom/ipc/nsIRemoteBlob.h"
+#include "mozilla/ipc/ProtocolTypes.h"
+#include "nsAutoPtr.h"
+#include "nsCOMPtr.h"
+#include "nsIEventTarget.h"
+#include "nsIIPCBackgroundChildCreateCallback.h"
+#include "nsIMutable.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_OR_BETA
+#define THREADSAFETY_ASSERT MOZ_ASSERT
+#else
+#define THREADSAFETY_ASSERT MOZ_RELEASE_ASSERT
+#endif
+
+#define CRASH_IN_CHILD_PROCESS(_msg) \
+ do { \
+ if (XRE_IsParentProcess()) { \
+ MOZ_ASSERT(false, _msg); \
+ } else { \
+ MOZ_CRASH(_msg); \
+ } \
+ } \
+ while (0)
+
+using namespace mozilla;
+using namespace mozilla::dom;
+using namespace mozilla::ipc;
+
+namespace {
+
+// -----------------------------------------------------------------------------
+// Utility Functions
+// -----------------------------------------------------------------------------
+
+
+void
+AssertIsInMainProcess()
+{
+ MOZ_ASSERT(XRE_IsParentProcess());
+}
+
+void
+AssertIsInChildProcess()
+{
+ MOZ_ASSERT(!XRE_IsParentProcess());
+}
+
+void
+AssertIsOnMainThread()
+{
+ THREADSAFETY_ASSERT(NS_IsMainThread());
+}
+
+// -----------------------------------------------------------------------------
+// ParentImpl Declaration
+// -----------------------------------------------------------------------------
+
+class ParentImpl 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<ParentImpl*>* mLiveActors;
+
+ TimerCallbackClosure(nsIThread* aThread, nsTArray<ParentImpl*>* aLiveActors)
+ : mThread(aThread), mLiveActors(aLiveActors)
+ {
+ AssertIsInMainProcess();
+ AssertIsOnMainThread();
+ MOZ_ASSERT(aThread);
+ MOZ_ASSERT(aLiveActors);
+ }
+ };
+
+ // The length of time we will wait at shutdown for all actors to clean
+ // themselves up before forcing them to be destroyed.
+ static const uint32_t kShutdownTimerDelayMS = 10000;
+
+ // This is only modified on the main thread. It is null if the thread does not
+ // exist or is shutting down.
+ static StaticRefPtr<nsIThread> 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<ParentImpl*>* sLiveActorsForBackgroundThread;
+
+ // This is only modified on the main thread.
+ static StaticRefPtr<nsITimer> sShutdownTimer;
+
+ // This exists so that that [Assert]IsOnBackgroundThread() can continue to
+ // work during shutdown.
+ static Atomic<PRThread*> 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<nsTArray<RefPtr<CreateCallback>>> sPendingCallbacks;
+
+ // Only touched on the main thread, null if this is a same-process actor.
+ RefPtr<ContentParent> mContent;
+
+ // Set when the actor is opened successfully and used to handle shutdown
+ // hangs. Only touched on the background thread.
+ nsTArray<ParentImpl*>* 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<ContentParent>
+ GetContentParent(PBackgroundParent* aBackgroundActor);
+
+ // Forwarded from BackgroundParent.
+ static intptr_t
+ GetRawContentParentForComparison(PBackgroundParent* aBackgroundActor);
+
+ // Forwarded from BackgroundParent.
+ static PBackgroundParent*
+ Alloc(ContentParent* aContent,
+ Transport* aTransport,
+ ProcessId aOtherPid);
+
+ static bool
+ CreateBackgroundThread();
+
+ static void
+ ShutdownBackgroundThread();
+
+ static void
+ ShutdownTimerCallback(nsITimer* aTimer, void* aClosure);
+
+ // For same-process actors.
+ ParentImpl()
+ : mLiveActorArray(nullptr), mIsOtherProcessActor(false),
+ mActorDestroyed(false)
+ {
+ AssertIsInMainProcess();
+ AssertIsOnMainThread();
+ }
+
+ // For other-process actors.
+ explicit ParentImpl(ContentParent* aContent)
+ : mContent(aContent), mLiveActorArray(nullptr),
+ mIsOtherProcessActor(true), mActorDestroyed(false)
+ {
+ AssertIsInMainProcess();
+ AssertIsOnMainThread();
+ MOZ_ASSERT(aContent);
+ }
+
+ ~ParentImpl()
+ {
+ AssertIsInMainProcess();
+ AssertIsOnMainThread();
+ MOZ_ASSERT(!mContent);
+ }
+
+ void
+ MainThreadActorDestroy();
+
+ void
+ SetLiveActorArray(nsTArray<ParentImpl*>* 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 void
+ ActorDestroy(ActorDestroyReason aWhy) override;
+};
+
+// -----------------------------------------------------------------------------
+// ChildImpl Declaration
+// -----------------------------------------------------------------------------
+
+class ChildImpl final : public BackgroundChildImpl
+{
+ friend class mozilla::ipc::BackgroundChild;
+ friend class mozilla::ipc::BackgroundChildImpl;
+
+ typedef base::ProcessId ProcessId;
+ typedef mozilla::ipc::Transport Transport;
+
+ class ShutdownObserver;
+ class CreateActorRunnable;
+ class ParentCreateCallback;
+ class AlreadyCreatedCallbackRunnable;
+ class FailedCreateCallbackRunnable;
+ class OpenChildProcessActorRunnable;
+ class OpenMainProcessActorRunnable;
+
+ // A thread-local index that is not valid.
+ static const unsigned int kBadThreadLocalIndex =
+ static_cast<unsigned int>(-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
+ {
+ explicit ThreadLocalInfo(nsIIPCBackgroundChildCreateCallback* aCallback)
+#ifdef DEBUG
+ : mClosed(false)
+#endif
+ {
+ mCallbacks.AppendElement(aCallback);
+ }
+
+ RefPtr<ChildImpl> mActor;
+ nsTArray<nsCOMPtr<nsIIPCBackgroundChildCreateCallback>> mCallbacks;
+ nsAutoPtr<BackgroundChildImpl::ThreadLocal> mConsumerThreadLocal;
+#ifdef DEBUG
+ bool mClosed;
+#endif
+ };
+
+ // This is only modified on the main thread. It is a FIFO queue for actors
+ // that are in the process of construction.
+ static StaticAutoPtr<nsTArray<nsCOMPtr<nsIEventTarget>>> 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;
+
+#if defined(DEBUG) || !defined(RELEASE_OR_BETA)
+ nsIThread* mBoundThread;
+#endif
+
+#ifdef DEBUG
+ bool mActorDestroyed;
+#endif
+
+public:
+ static bool
+ OpenProtocolOnMainThread(nsIEventTarget* aEventTarget);
+
+ static void
+ Shutdown();
+
+ void
+ AssertIsOnBoundThread()
+ {
+ THREADSAFETY_ASSERT(mBoundThread);
+
+#ifdef RELEASE_OR_BETA
+ DebugOnly<bool> current;
+#else
+ bool current;
+#endif
+ THREADSAFETY_ASSERT(
+ NS_SUCCEEDED(mBoundThread->IsOnCurrentThread(&current)));
+ THREADSAFETY_ASSERT(current);
+ }
+
+ void
+ AssertActorDestroyed()
+ {
+ MOZ_ASSERT(mActorDestroyed, "ChildImpl::ActorDestroy not called in time");
+ }
+
+ ChildImpl()
+#if defined(DEBUG) || !defined(RELEASE_OR_BETA)
+ : mBoundThread(nullptr)
+#endif
+#ifdef DEBUG
+ , mActorDestroyed(false)
+#endif
+ {
+ AssertIsOnMainThread();
+ }
+
+ NS_INLINE_DECL_REFCOUNTING(ChildImpl)
+
+private:
+ // Forwarded from BackgroundChild.
+ static void
+ Startup();
+
+ // Forwarded from BackgroundChild.
+ static PBackgroundChild*
+ Alloc(Transport* aTransport, ProcessId aOtherPid);
+
+ // Forwarded from BackgroundChild.
+ static PBackgroundChild*
+ GetForCurrentThread();
+
+ // Forwarded from BackgroundChild.
+ static bool
+ GetOrCreateForCurrentThread(nsIIPCBackgroundChildCreateCallback* aCallback);
+
+ // Forwarded from BackgroundChild.
+ static PBackgroundChild*
+ SynchronouslyCreateForCurrentThread();
+
+ // Forwarded from BackgroundChild.
+ static void
+ CloseForCurrentThread();
+
+ // Forwarded from BackgroundChildImpl.
+ static BackgroundChildImpl::ThreadLocal*
+ GetThreadLocalForCurrentThread();
+
+ static void
+ ThreadLocalDestructor(void* aThreadLocal)
+ {
+ auto threadLocalInfo = static_cast<ThreadLocalInfo*>(aThreadLocal);
+
+ if (threadLocalInfo) {
+ MOZ_ASSERT(threadLocalInfo->mClosed);
+
+ if (threadLocalInfo->mActor) {
+ threadLocalInfo->mActor->Close();
+ threadLocalInfo->mActor->AssertActorDestroyed();
+
+ // Since the actor is created on the main thread it must only
+ // be released on the main thread as well.
+ if (!NS_IsMainThread()) {
+ ChildImpl* actor;
+ threadLocalInfo->mActor.forget(&actor);
+
+ MOZ_ALWAYS_SUCCEEDS(
+ NS_DispatchToMainThread(NewNonOwningRunnableMethod(actor, &ChildImpl::Release)));
+ }
+ }
+ delete threadLocalInfo;
+ }
+ }
+
+ static void
+ DispatchFailureCallback(nsIEventTarget* aEventTarget);
+
+ // This class is reference counted.
+ ~ChildImpl()
+ {
+ AssertActorDestroyed();
+ }
+
+ void
+ SetBoundThread()
+ {
+ THREADSAFETY_ASSERT(!mBoundThread);
+
+#if defined(DEBUG) || !defined(RELEASE_OR_BETA)
+ mBoundThread = NS_GetCurrentThread();
+#endif
+
+ THREADSAFETY_ASSERT(mBoundThread);
+ }
+
+ // Only called by IPDL.
+ virtual void
+ ActorDestroy(ActorDestroyReason aWhy) override;
+
+ static already_AddRefed<nsIIPCBackgroundChildCreateCallback>
+ GetNextCallback();
+};
+
+// -----------------------------------------------------------------------------
+// ParentImpl Helper Declarations
+// -----------------------------------------------------------------------------
+
+class ParentImpl::ShutdownObserver final : public nsIObserver
+{
+public:
+ ShutdownObserver()
+ {
+ AssertIsOnMainThread();
+ }
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+private:
+ ~ShutdownObserver()
+ {
+ AssertIsOnMainThread();
+ }
+};
+
+class ParentImpl::RequestMessageLoopRunnable final : public Runnable
+{
+ nsCOMPtr<nsIThread> mTargetThread;
+ MessageLoop* mMessageLoop;
+
+public:
+ explicit RequestMessageLoopRunnable(nsIThread* aTargetThread)
+ : mTargetThread(aTargetThread), mMessageLoop(nullptr)
+ {
+ AssertIsInMainProcess();
+ AssertIsOnMainThread();
+ MOZ_ASSERT(aTargetThread);
+ }
+
+private:
+ ~RequestMessageLoopRunnable()
+ { }
+
+ NS_DECL_NSIRUNNABLE
+};
+
+class ParentImpl::ShutdownBackgroundThreadRunnable final : public Runnable
+{
+public:
+ ShutdownBackgroundThreadRunnable()
+ {
+ AssertIsInMainProcess();
+ AssertIsOnMainThread();
+ }
+
+private:
+ ~ShutdownBackgroundThreadRunnable()
+ { }
+
+ NS_DECL_NSIRUNNABLE
+};
+
+class ParentImpl::ForceCloseBackgroundActorsRunnable final : public Runnable
+{
+ nsTArray<ParentImpl*>* mActorArray;
+
+public:
+ explicit ForceCloseBackgroundActorsRunnable(nsTArray<ParentImpl*>* aActorArray)
+ : mActorArray(aActorArray)
+ {
+ AssertIsInMainProcess();
+ AssertIsOnMainThread();
+ MOZ_ASSERT(aActorArray);
+ }
+
+private:
+ ~ForceCloseBackgroundActorsRunnable()
+ { }
+
+ NS_DECL_NSIRUNNABLE
+};
+
+class ParentImpl::CreateCallbackRunnable final : public Runnable
+{
+ RefPtr<CreateCallback> mCallback;
+
+public:
+ explicit CreateCallbackRunnable(CreateCallback* aCallback)
+ : mCallback(aCallback)
+ {
+ AssertIsInMainProcess();
+ AssertIsOnMainThread();
+ MOZ_ASSERT(aCallback);
+ }
+
+private:
+ ~CreateCallbackRunnable()
+ { }
+
+ NS_DECL_NSIRUNNABLE
+};
+
+class ParentImpl::ConnectActorRunnable final : public Runnable
+{
+ RefPtr<ParentImpl> mActor;
+ Transport* mTransport;
+ ProcessId mOtherPid;
+ nsTArray<ParentImpl*>* mLiveActorArray;
+
+public:
+ ConnectActorRunnable(ParentImpl* aActor,
+ Transport* aTransport,
+ ProcessId aOtherPid,
+ nsTArray<ParentImpl*>* aLiveActorArray)
+ : mActor(aActor), mTransport(aTransport), mOtherPid(aOtherPid),
+ mLiveActorArray(aLiveActorArray)
+ {
+ AssertIsInMainProcess();
+ AssertIsOnMainThread();
+ MOZ_ASSERT(aActor);
+ MOZ_ASSERT(aTransport);
+ MOZ_ASSERT(aLiveActorArray);
+ }
+
+private:
+ ~ConnectActorRunnable()
+ {
+ AssertIsInMainProcess();
+ }
+
+ NS_DECL_NSIRUNNABLE
+};
+
+class NS_NO_VTABLE ParentImpl::CreateCallback
+{
+public:
+ NS_INLINE_DECL_REFCOUNTING(CreateCallback)
+
+ virtual void
+ Success(already_AddRefed<ParentImpl> aActor, MessageLoop* aMessageLoop) = 0;
+
+ virtual void
+ Failure() = 0;
+
+protected:
+ virtual ~CreateCallback()
+ { }
+};
+
+// -----------------------------------------------------------------------------
+// ChildImpl Helper Declarations
+// -----------------------------------------------------------------------------
+
+class ChildImpl::ShutdownObserver final : public nsIObserver
+{
+public:
+ ShutdownObserver()
+ {
+ AssertIsOnMainThread();
+ }
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+private:
+ ~ShutdownObserver()
+ {
+ AssertIsOnMainThread();
+ }
+};
+
+class ChildImpl::CreateActorRunnable final : public Runnable
+{
+ nsCOMPtr<nsIEventTarget> mEventTarget;
+
+public:
+ CreateActorRunnable()
+ : mEventTarget(NS_GetCurrentThread())
+ {
+ MOZ_ASSERT(mEventTarget);
+ }
+
+private:
+ ~CreateActorRunnable()
+ { }
+
+ NS_DECL_NSIRUNNABLE
+};
+
+class ChildImpl::ParentCreateCallback final :
+ public ParentImpl::CreateCallback
+{
+ nsCOMPtr<nsIEventTarget> mEventTarget;
+
+public:
+ explicit ParentCreateCallback(nsIEventTarget* aEventTarget)
+ : mEventTarget(aEventTarget)
+ {
+ AssertIsInMainProcess();
+ AssertIsOnMainThread();
+ MOZ_ASSERT(aEventTarget);
+ }
+
+private:
+ ~ParentCreateCallback()
+ { }
+
+ virtual void
+ Success(already_AddRefed<ParentImpl> aActor, MessageLoop* aMessageLoop)
+ override;
+
+ virtual void
+ Failure() override;
+};
+
+// Must be cancelable in order to dispatch on active worker threads
+class ChildImpl::AlreadyCreatedCallbackRunnable final :
+ public CancelableRunnable
+{
+public:
+ AlreadyCreatedCallbackRunnable()
+ {
+ // May be created on any thread!
+ }
+
+protected:
+ virtual ~AlreadyCreatedCallbackRunnable()
+ { }
+
+ NS_DECL_NSIRUNNABLE
+ nsresult Cancel() override;
+};
+
+class ChildImpl::FailedCreateCallbackRunnable final : public Runnable
+{
+public:
+ FailedCreateCallbackRunnable()
+ {
+ // May be created on any thread!
+ }
+
+protected:
+ virtual ~FailedCreateCallbackRunnable()
+ { }
+
+ NS_DECL_NSIRUNNABLE
+};
+
+class ChildImpl::OpenChildProcessActorRunnable final : public Runnable
+{
+ RefPtr<ChildImpl> mActor;
+ nsAutoPtr<Transport> mTransport;
+ ProcessId mOtherPid;
+
+public:
+ OpenChildProcessActorRunnable(already_AddRefed<ChildImpl>&& aActor,
+ Transport* aTransport,
+ ProcessId aOtherPid)
+ : mActor(aActor), mTransport(aTransport),
+ mOtherPid(aOtherPid)
+ {
+ AssertIsOnMainThread();
+ MOZ_ASSERT(mActor);
+ MOZ_ASSERT(aTransport);
+ }
+
+private:
+ ~OpenChildProcessActorRunnable()
+ {
+ if (mTransport) {
+ CRASH_IN_CHILD_PROCESS("Leaking transport!");
+ Unused << mTransport.forget();
+ }
+ }
+
+ NS_DECL_NSIRUNNABLE
+};
+
+class ChildImpl::OpenMainProcessActorRunnable final : public Runnable
+{
+ RefPtr<ChildImpl> mActor;
+ RefPtr<ParentImpl> mParentActor;
+ MessageLoop* mParentMessageLoop;
+
+public:
+ OpenMainProcessActorRunnable(already_AddRefed<ChildImpl>&& aChildActor,
+ already_AddRefed<ParentImpl> aParentActor,
+ MessageLoop* aParentMessageLoop)
+ : mActor(aChildActor), mParentActor(aParentActor),
+ mParentMessageLoop(aParentMessageLoop)
+ {
+ AssertIsOnMainThread();
+ MOZ_ASSERT(mParentActor);
+ MOZ_ASSERT(aParentMessageLoop);
+ }
+
+private:
+ ~OpenMainProcessActorRunnable()
+ { }
+
+ NS_DECL_NSIRUNNABLE
+};
+
+} // namespace
+
+namespace mozilla {
+namespace ipc {
+
+bool
+IsOnBackgroundThread()
+{
+ return ParentImpl::IsOnBackgroundThread();
+}
+
+#ifdef DEBUG
+
+void
+AssertIsOnBackgroundThread()
+{
+ ParentImpl::AssertIsOnBackgroundThread();
+}
+
+#endif // DEBUG
+
+} // namespace ipc
+} // namespace mozilla
+
+// -----------------------------------------------------------------------------
+// BackgroundParent Public Methods
+// -----------------------------------------------------------------------------
+
+// static
+bool
+BackgroundParent::IsOtherProcessActor(PBackgroundParent* aBackgroundActor)
+{
+ return ParentImpl::IsOtherProcessActor(aBackgroundActor);
+}
+
+// static
+already_AddRefed<ContentParent>
+BackgroundParent::GetContentParent(PBackgroundParent* aBackgroundActor)
+{
+ return ParentImpl::GetContentParent(aBackgroundActor);
+}
+
+// static
+PBlobParent*
+BackgroundParent::GetOrCreateActorForBlobImpl(
+ PBackgroundParent* aBackgroundActor,
+ BlobImpl* aBlobImpl)
+{
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(aBackgroundActor);
+ MOZ_ASSERT(aBlobImpl);
+
+ BlobParent* actor = BlobParent::GetOrCreate(aBackgroundActor, aBlobImpl);
+ if (NS_WARN_IF(!actor)) {
+ return nullptr;
+ }
+
+ return actor;
+}
+
+// static
+intptr_t
+BackgroundParent::GetRawContentParentForComparison(
+ PBackgroundParent* aBackgroundActor)
+{
+ return ParentImpl::GetRawContentParentForComparison(aBackgroundActor);
+}
+
+// static
+PBackgroundParent*
+BackgroundParent::Alloc(ContentParent* aContent,
+ Transport* aTransport,
+ ProcessId aOtherPid)
+{
+ return ParentImpl::Alloc(aContent, aTransport, aOtherPid);
+}
+
+// -----------------------------------------------------------------------------
+// BackgroundChild Public Methods
+// -----------------------------------------------------------------------------
+
+// static
+void
+BackgroundChild::Startup()
+{
+ ChildImpl::Startup();
+}
+
+// static
+PBackgroundChild*
+BackgroundChild::Alloc(Transport* aTransport, ProcessId aOtherPid)
+{
+ return ChildImpl::Alloc(aTransport, aOtherPid);
+}
+
+// static
+PBackgroundChild*
+BackgroundChild::GetForCurrentThread()
+{
+ return ChildImpl::GetForCurrentThread();
+}
+
+// static
+bool
+BackgroundChild::GetOrCreateForCurrentThread(
+ nsIIPCBackgroundChildCreateCallback* aCallback)
+{
+ return ChildImpl::GetOrCreateForCurrentThread(aCallback);
+}
+
+// static
+PBackgroundChild*
+BackgroundChild::SynchronouslyCreateForCurrentThread()
+{
+ return ChildImpl::SynchronouslyCreateForCurrentThread();
+}
+
+// static
+PBlobChild*
+BackgroundChild::GetOrCreateActorForBlob(PBackgroundChild* aBackgroundActor,
+ nsIDOMBlob* aBlob)
+{
+ MOZ_ASSERT(aBlob);
+
+ RefPtr<BlobImpl> blobImpl = static_cast<Blob*>(aBlob)->Impl();
+ MOZ_ASSERT(blobImpl);
+
+ return GetOrCreateActorForBlobImpl(aBackgroundActor, blobImpl);
+}
+
+// static
+PBlobChild*
+BackgroundChild::GetOrCreateActorForBlobImpl(PBackgroundChild* aBackgroundActor,
+ BlobImpl* aBlobImpl)
+{
+ MOZ_ASSERT(aBackgroundActor);
+ MOZ_ASSERT(aBlobImpl);
+ MOZ_ASSERT(GetForCurrentThread(),
+ "BackgroundChild not created on this thread yet!");
+ MOZ_ASSERT(aBackgroundActor == GetForCurrentThread(),
+ "BackgroundChild is bound to a different thread!");
+
+ BlobChild* actor = BlobChild::GetOrCreate(aBackgroundActor, aBlobImpl);
+ if (NS_WARN_IF(!actor)) {
+ return nullptr;
+ }
+
+ return actor;
+}
+
+// static
+void
+BackgroundChild::CloseForCurrentThread()
+{
+ ChildImpl::CloseForCurrentThread();
+}
+
+// -----------------------------------------------------------------------------
+// BackgroundChildImpl Public Methods
+// -----------------------------------------------------------------------------
+
+// static
+BackgroundChildImpl::ThreadLocal*
+BackgroundChildImpl::GetThreadLocalForCurrentThread()
+{
+ return ChildImpl::GetThreadLocalForCurrentThread();
+}
+
+// -----------------------------------------------------------------------------
+// ParentImpl Static Members
+// -----------------------------------------------------------------------------
+
+StaticRefPtr<nsIThread> ParentImpl::sBackgroundThread;
+
+nsTArray<ParentImpl*>* ParentImpl::sLiveActorsForBackgroundThread;
+
+StaticRefPtr<nsITimer> ParentImpl::sShutdownTimer;
+
+Atomic<PRThread*> ParentImpl::sBackgroundPRThread;
+
+MessageLoop* ParentImpl::sBackgroundThreadMessageLoop = nullptr;
+
+uint64_t ParentImpl::sLiveActorCount = 0;
+
+bool ParentImpl::sShutdownObserverRegistered = false;
+
+bool ParentImpl::sShutdownHasStarted = false;
+
+StaticAutoPtr<nsTArray<RefPtr<ParentImpl::CreateCallback>>>
+ ParentImpl::sPendingCallbacks;
+
+// -----------------------------------------------------------------------------
+// ChildImpl Static Members
+// -----------------------------------------------------------------------------
+
+unsigned int ChildImpl::sThreadLocalIndex = kBadThreadLocalIndex;
+
+StaticAutoPtr<nsTArray<nsCOMPtr<nsIEventTarget>>> ChildImpl::sPendingTargets;
+
+bool ChildImpl::sShutdownHasStarted = false;
+
+// -----------------------------------------------------------------------------
+// ParentImpl Implementation
+// -----------------------------------------------------------------------------
+
+// static
+bool
+ParentImpl::IsOtherProcessActor(PBackgroundParent* aBackgroundActor)
+{
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(aBackgroundActor);
+
+ return static_cast<ParentImpl*>(aBackgroundActor)->mIsOtherProcessActor;
+}
+
+// static
+already_AddRefed<ContentParent>
+ParentImpl::GetContentParent(PBackgroundParent* aBackgroundActor)
+{
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(aBackgroundActor);
+
+ auto actor = static_cast<ParentImpl*>(aBackgroundActor);
+ if (actor->mActorDestroyed) {
+ MOZ_ASSERT(false, "GetContentParent called after ActorDestroy was called!");
+ return nullptr;
+ }
+
+ if (actor->mContent) {
+ // We need to hand out a reference to our ContentParent but we also need to
+ // keep the one we have. We can't call AddRef here because ContentParent is
+ // not threadsafe so instead we dispatch a runnable to the main thread to do
+ // it for us. This is safe since we are guaranteed that our AddRef runnable
+ // will run before the reference we hand out can be released, and the
+ // ContentParent can't die as long as the existing reference is maintained.
+ MOZ_ALWAYS_SUCCEEDS(
+ NS_DispatchToMainThread(NewNonOwningRunnableMethod(actor->mContent, &ContentParent::AddRef)));
+ }
+
+ return already_AddRefed<ContentParent>(actor->mContent.get());
+}
+
+// static
+intptr_t
+ParentImpl::GetRawContentParentForComparison(
+ PBackgroundParent* aBackgroundActor)
+{
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(aBackgroundActor);
+
+ auto actor = static_cast<ParentImpl*>(aBackgroundActor);
+ if (actor->mActorDestroyed) {
+ MOZ_ASSERT(false,
+ "GetRawContentParentForComparison called after ActorDestroy was "
+ "called!");
+ return intptr_t(-1);
+ }
+
+ return intptr_t(static_cast<nsIContentParent*>(actor->mContent.get()));
+}
+
+// static
+PBackgroundParent*
+ParentImpl::Alloc(ContentParent* aContent,
+ Transport* aTransport,
+ ProcessId aOtherPid)
+{
+ AssertIsInMainProcess();
+ AssertIsOnMainThread();
+ MOZ_ASSERT(aTransport);
+
+ if (!sBackgroundThread && !CreateBackgroundThread()) {
+ NS_WARNING("Failed to create background thread!");
+ return nullptr;
+ }
+
+ MOZ_ASSERT(sLiveActorsForBackgroundThread);
+
+ sLiveActorCount++;
+
+ RefPtr<ParentImpl> actor = new ParentImpl(aContent);
+
+ nsCOMPtr<nsIRunnable> connectRunnable =
+ new ConnectActorRunnable(actor, aTransport, aOtherPid,
+ sLiveActorsForBackgroundThread);
+
+ if (NS_FAILED(sBackgroundThread->Dispatch(connectRunnable,
+ NS_DISPATCH_NORMAL))) {
+ NS_WARNING("Failed to dispatch connect runnable!");
+
+ MOZ_ASSERT(sLiveActorCount);
+ sLiveActorCount--;
+
+ 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<nsIRunnable> callbackRunnable =
+ new CreateCallbackRunnable(aCallback);
+ MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(callbackRunnable));
+ return true;
+ }
+
+ if (!sPendingCallbacks) {
+ sPendingCallbacks = new nsTArray<RefPtr<CreateCallback>>();
+ }
+
+ 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<nsITimer> newShutdownTimer;
+
+ if (!sShutdownTimer) {
+ nsresult rv;
+ newShutdownTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return false;
+ }
+ }
+
+ if (!sShutdownObserverRegistered) {
+ nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+ if (NS_WARN_IF(!obs)) {
+ return false;
+ }
+
+ nsCOMPtr<nsIObserver> 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<nsIThread> thread;
+ if (NS_FAILED(NS_NewNamedThread("IPDL Background", getter_AddRefs(thread)))) {
+ NS_WARNING("NS_NewNamedThread failed!");
+ return false;
+ }
+
+ nsCOMPtr<nsIRunnable> 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<ParentImpl*>(1);
+
+ if (!sShutdownTimer) {
+ MOZ_ASSERT(newShutdownTimer);
+ sShutdownTimer = newShutdownTimer;
+ }
+
+ return true;
+}
+
+// static
+void
+ParentImpl::ShutdownBackgroundThread()
+{
+ AssertIsInMainProcess();
+ AssertIsOnMainThread();
+ MOZ_ASSERT_IF(!sBackgroundThread, !sBackgroundThreadMessageLoop);
+ MOZ_ASSERT(sShutdownHasStarted);
+ MOZ_ASSERT_IF(!sBackgroundThread, !sLiveActorCount);
+ MOZ_ASSERT_IF(sBackgroundThread, sShutdownTimer);
+
+ if (sPendingCallbacks) {
+ if (!sPendingCallbacks->IsEmpty()) {
+ nsTArray<RefPtr<CreateCallback>> callbacks;
+ sPendingCallbacks->SwapElements(callbacks);
+
+ for (uint32_t index = 0; index < callbacks.Length(); index++) {
+ RefPtr<CreateCallback> callback;
+ callbacks[index].swap(callback);
+ MOZ_ASSERT(callback);
+
+ callback->Failure();
+ }
+ }
+
+ sPendingCallbacks = nullptr;
+ }
+
+ nsCOMPtr<nsITimer> shutdownTimer = sShutdownTimer.get();
+ sShutdownTimer = nullptr;
+
+ if (sBackgroundThread) {
+ nsCOMPtr<nsIThread> thread = sBackgroundThread.get();
+ sBackgroundThread = nullptr;
+
+ nsAutoPtr<nsTArray<ParentImpl*>> liveActors(sLiveActorsForBackgroundThread);
+ sLiveActorsForBackgroundThread = nullptr;
+
+ sBackgroundThreadMessageLoop = nullptr;
+
+ MOZ_ASSERT_IF(!sShutdownHasStarted, !sLiveActorCount);
+
+ if (sLiveActorCount) {
+ // We need to spin the event loop while we wait for all the actors to be
+ // cleaned up. We also set a timeout to force-kill any hanging actors.
+ TimerCallbackClosure closure(thread, liveActors);
+
+ MOZ_ALWAYS_SUCCEEDS(
+ 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_SUCCEEDS(shutdownTimer->Cancel());
+ }
+
+ // Dispatch this runnable to unregister the thread from the profiler.
+ nsCOMPtr<nsIRunnable> shutdownRunnable =
+ new ShutdownBackgroundThreadRunnable();
+ MOZ_ALWAYS_SUCCEEDS(thread->Dispatch(shutdownRunnable, NS_DISPATCH_NORMAL));
+
+ MOZ_ALWAYS_SUCCEEDS(thread->Shutdown());
+ }
+}
+
+// static
+void
+ParentImpl::ShutdownTimerCallback(nsITimer* aTimer, void* aClosure)
+{
+ AssertIsInMainProcess();
+ AssertIsOnMainThread();
+ MOZ_ASSERT(sShutdownHasStarted);
+ MOZ_ASSERT(sLiveActorCount);
+
+ auto closure = static_cast<TimerCallbackClosure*>(aClosure);
+ MOZ_ASSERT(closure);
+
+ // Don't let the stack unwind until the ForceCloseBackgroundActorsRunnable has
+ // finished.
+ sLiveActorCount++;
+
+ nsCOMPtr<nsIRunnable> forceCloseRunnable =
+ new ForceCloseBackgroundActorsRunnable(closure->mLiveActors);
+ MOZ_ALWAYS_SUCCEEDS(closure->mThread->Dispatch(forceCloseRunnable,
+ NS_DISPATCH_NORMAL));
+}
+
+void
+ParentImpl::Destroy()
+{
+ // May be called on any thread!
+
+ AssertIsInMainProcess();
+
+ MOZ_ALWAYS_SUCCEEDS(
+ NS_DispatchToMainThread(NewNonOwningRunnableMethod(this, &ParentImpl::MainThreadActorDestroy)));
+}
+
+void
+ParentImpl::MainThreadActorDestroy()
+{
+ AssertIsInMainProcess();
+ AssertIsOnMainThread();
+ MOZ_ASSERT_IF(mIsOtherProcessActor, mContent);
+ MOZ_ASSERT_IF(!mIsOtherProcessActor, !mContent);
+
+ mContent = nullptr;
+
+ MOZ_ASSERT(sLiveActorCount);
+ sLiveActorCount--;
+
+ // This may be the last reference!
+ Release();
+}
+
+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.
+
+ MOZ_ALWAYS_SUCCEEDS(
+ NS_DispatchToCurrentThread(NewNonOwningRunnableMethod(this, &ParentImpl::Destroy)));
+}
+
+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_IMETHODIMP
+ParentImpl::RequestMessageLoopRunnable::Run()
+{
+ AssertIsInMainProcess();
+ MOZ_ASSERT(mTargetThread);
+
+ char stackBaseGuess;
+
+ 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<RefPtr<CreateCallback>> callbacks;
+ sPendingCallbacks->SwapElements(callbacks);
+
+ for (uint32_t index = 0; index < callbacks.Length(); index++) {
+ MOZ_ASSERT(callbacks[index]);
+
+ nsCOMPtr<nsIRunnable> callbackRunnable =
+ new CreateCallbackRunnable(callbacks[index]);
+ if (NS_FAILED(NS_DispatchToCurrentThread(callbackRunnable))) {
+ NS_WARNING("Failed to dispatch callback runnable!");
+ }
+ }
+ }
+
+ return NS_OK;
+ }
+
+ profiler_register_thread("IPDL Background", &stackBaseGuess);
+
+#ifdef DEBUG
+ {
+ bool correctThread;
+ MOZ_ASSERT(NS_SUCCEEDED(mTargetThread->IsOnCurrentThread(&correctThread)));
+ MOZ_ASSERT(correctThread);
+ }
+#endif
+
+ DebugOnly<PRThread*> 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_WARNING("Failed to dispatch RequestMessageLoopRunnable to main thread!");
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+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_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<ParentImpl*> actorsToClose(*mActorArray);
+
+ for (uint32_t index = 0; index < actorsToClose.Length(); index++) {
+ actorsToClose[index]->Close();
+ }
+ }
+
+ MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ParentImpl::CreateCallbackRunnable::Run()
+{
+ AssertIsInMainProcess();
+ AssertIsOnMainThread();
+ MOZ_ASSERT(sBackgroundThreadMessageLoop);
+ MOZ_ASSERT(mCallback);
+
+ RefPtr<CreateCallback> callback;
+ mCallback.swap(callback);
+
+ RefPtr<ParentImpl> actor = new ParentImpl();
+
+ callback->Success(actor.forget(), sBackgroundThreadMessageLoop);
+
+ return NS_OK;
+}
+
+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, mOtherPid, 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<nsIObserverService> observerService = services::GetObserverService();
+ MOZ_RELEASE_ASSERT(observerService);
+
+ nsCOMPtr<nsIObserver> 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;
+
+#ifdef DEBUG
+ MOZ_ASSERT(sThreadLocalIndex != kBadThreadLocalIndex);
+
+ auto threadLocalInfo =
+ static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(sThreadLocalIndex));
+
+ if (threadLocalInfo) {
+ MOZ_ASSERT(!threadLocalInfo->mClosed);
+ threadLocalInfo->mClosed = true;
+ }
+#endif
+
+ DebugOnly<PRStatus> status = PR_SetThreadPrivate(sThreadLocalIndex, nullptr);
+ MOZ_ASSERT(status == PR_SUCCESS);
+}
+
+// static
+PBackgroundChild*
+ChildImpl::Alloc(Transport* aTransport, ProcessId aOtherPid)
+{
+ AssertIsInChildProcess();
+ AssertIsOnMainThread();
+ MOZ_ASSERT(aTransport);
+ MOZ_ASSERT(sPendingTargets);
+ MOZ_ASSERT(!sPendingTargets->IsEmpty());
+
+ nsCOMPtr<nsIEventTarget> eventTarget;
+ sPendingTargets->ElementAt(0).swap(eventTarget);
+
+ sPendingTargets->RemoveElementAt(0);
+
+ RefPtr<ChildImpl> actor = new ChildImpl();
+
+ ChildImpl* weakActor = actor;
+
+ nsCOMPtr<nsIRunnable> openRunnable =
+ new OpenChildProcessActorRunnable(actor.forget(), aTransport,
+ aOtherPid);
+ 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<ThreadLocalInfo*>(PR_GetThreadPrivate(sThreadLocalIndex));
+
+ if (!threadLocalInfo) {
+ return nullptr;
+ }
+
+ return threadLocalInfo->mActor;
+}
+
+// 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<ThreadLocalInfo*>(PR_GetThreadPrivate(sThreadLocalIndex));
+
+ if (threadLocalInfo) {
+ threadLocalInfo->mCallbacks.AppendElement(aCallback);
+ } else {
+ nsAutoPtr<ThreadLocalInfo> 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) {
+ // Runnable will use GetForCurrentThread() to retrieve actor again. This
+ // allows us to avoid addref'ing on the wrong thread.
+ nsCOMPtr<nsIRunnable> runnable = new AlreadyCreatedCallbackRunnable();
+ MOZ_ALWAYS_SUCCEEDS(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 the schedule runnable.
+ return true;
+ }
+
+ if (NS_IsMainThread()) {
+ if (NS_WARN_IF(!OpenProtocolOnMainThread(NS_GetCurrentThread()))) {
+ return false;
+ }
+
+ return true;
+ }
+
+ RefPtr<CreateActorRunnable> runnable = new CreateActorRunnable();
+ if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
+ CRASH_IN_CHILD_PROCESS("Failed to dispatch to main thread!");
+ return false;
+ }
+
+ return true;
+}
+
+namespace {
+
+class Callback final : public nsIIPCBackgroundChildCreateCallback
+{
+ bool* mDone;
+
+public:
+ explicit Callback(bool* aDone)
+ : mDone(aDone)
+ {
+ MOZ_ASSERT(mDone);
+ }
+
+ NS_DECL_ISUPPORTS
+
+private:
+ ~Callback()
+ { }
+
+ virtual void
+ ActorCreated(PBackgroundChild* aActor) override
+ {
+ *mDone = true;
+ }
+
+ virtual void
+ ActorFailed() override
+ {
+ *mDone = true;
+ }
+};
+
+NS_IMPL_ISUPPORTS(Callback, nsIIPCBackgroundChildCreateCallback)
+
+} // anonymous namespace
+
+/* static */
+PBackgroundChild*
+ChildImpl::SynchronouslyCreateForCurrentThread()
+{
+ MOZ_ASSERT(!GetForCurrentThread());
+
+ bool done = false;
+ nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback = new Callback(&done);
+
+ if (NS_WARN_IF(!GetOrCreateForCurrentThread(callback))) {
+ return nullptr;
+ }
+
+ nsIThread* currentThread = NS_GetCurrentThread();
+ MOZ_ASSERT(currentThread);
+
+ while (!done) {
+ if (NS_WARN_IF(!NS_ProcessNextEvent(currentThread, true /* aMayWait */))) {
+ return nullptr;
+ }
+ }
+
+ return GetForCurrentThread();
+}
+
+// static
+void
+ChildImpl::CloseForCurrentThread()
+{
+ if (sThreadLocalIndex == kBadThreadLocalIndex) {
+ return;
+ }
+
+ auto threadLocalInfo =
+ static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(sThreadLocalIndex));
+
+ if (!threadLocalInfo) {
+ return;
+ }
+
+#ifdef DEBUG
+ MOZ_ASSERT(!threadLocalInfo->mClosed);
+ threadLocalInfo->mClosed = true;
+#endif
+
+ // Clearing the thread local will synchronously close the actor.
+ DebugOnly<PRStatus> status = PR_SetThreadPrivate(sThreadLocalIndex, nullptr);
+ MOZ_ASSERT(status == PR_SUCCESS);
+}
+
+// static
+BackgroundChildImpl::ThreadLocal*
+ChildImpl::GetThreadLocalForCurrentThread()
+{
+ MOZ_ASSERT(sThreadLocalIndex != kBadThreadLocalIndex,
+ "BackgroundChild::Startup() was never called!");
+
+ auto threadLocalInfo =
+ static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(sThreadLocalIndex));
+
+ if (!threadLocalInfo) {
+ return nullptr;
+ }
+
+ if (!threadLocalInfo->mConsumerThreadLocal) {
+ threadLocalInfo->mConsumerThreadLocal =
+ new BackgroundChildImpl::ThreadLocal();
+ }
+
+ return threadLocalInfo->mConsumerThreadLocal;
+}
+
+// static
+already_AddRefed<nsIIPCBackgroundChildCreateCallback>
+ChildImpl::GetNextCallback()
+{
+ // May run on any thread!
+
+ auto threadLocalInfo =
+ static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(sThreadLocalIndex));
+ MOZ_ASSERT(threadLocalInfo);
+
+ if (threadLocalInfo->mCallbacks.IsEmpty()) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback;
+ threadLocalInfo->mCallbacks[0].swap(callback);
+
+ threadLocalInfo->mCallbacks.RemoveElementAt(0);
+
+ return callback.forget();
+}
+
+NS_IMETHODIMP
+ChildImpl::AlreadyCreatedCallbackRunnable::Run()
+{
+ // May run on any thread!
+
+ // Report the current actor back in the callback.
+ PBackgroundChild* actor = ChildImpl::GetForCurrentThread();
+
+ // If the current actor is null, do not create a new actor here. This likely
+ // means we are in the process of cleaning up a worker thread and do not want
+ // a new actor created. Unfortunately we cannot report back to the callback
+ // because the thread local is gone at this point. Instead simply do nothing
+ // and return.
+ if (NS_WARN_IF(!actor)) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback =
+ ChildImpl::GetNextCallback();
+ while (callback) {
+ callback->ActorCreated(actor);
+ callback = ChildImpl::GetNextCallback();
+ }
+
+ return NS_OK;
+}
+
+nsresult
+ChildImpl::AlreadyCreatedCallbackRunnable::Cancel()
+{
+ // These are IPC infrastructure objects and need to run unconditionally.
+ Run();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildImpl::FailedCreateCallbackRunnable::Run()
+{
+ // May run on any thread!
+
+ nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback =
+ ChildImpl::GetNextCallback();
+ while (callback) {
+ callback->ActorFailed();
+ callback = ChildImpl::GetNextCallback();
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildImpl::OpenChildProcessActorRunnable::Run()
+{
+ // May be run on any thread!
+
+ AssertIsInChildProcess();
+ MOZ_ASSERT(mActor);
+ MOZ_ASSERT(mTransport);
+
+ nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback =
+ ChildImpl::GetNextCallback();
+ MOZ_ASSERT(callback,
+ "There should be at least one callback when first creating the "
+ "actor!");
+
+ RefPtr<ChildImpl> strongActor;
+ mActor.swap(strongActor);
+
+ if (!strongActor->Open(mTransport.forget(), mOtherPid,
+ XRE_GetIOMessageLoop(), ChildSide)) {
+ CRASH_IN_CHILD_PROCESS("Failed to open ChildImpl!");
+
+ while (callback) {
+ callback->ActorFailed();
+ callback = ChildImpl::GetNextCallback();
+ }
+
+ return NS_OK;
+ }
+
+ // Now that Open() has succeeded transfer the ownership of the actor to IPDL.
+ auto threadLocalInfo =
+ static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(sThreadLocalIndex));
+
+ MOZ_ASSERT(threadLocalInfo);
+ MOZ_ASSERT(!threadLocalInfo->mActor);
+
+ RefPtr<ChildImpl>& actor = threadLocalInfo->mActor;
+ strongActor.swap(actor);
+
+ actor->SetBoundThread();
+
+ while (callback) {
+ callback->ActorCreated(actor);
+ callback = ChildImpl::GetNextCallback();
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildImpl::OpenMainProcessActorRunnable::Run()
+{
+ // May run on any thread!
+
+ AssertIsInMainProcess();
+ MOZ_ASSERT(mActor);
+ MOZ_ASSERT(mParentActor);
+ MOZ_ASSERT(mParentMessageLoop);
+
+ nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback =
+ ChildImpl::GetNextCallback();
+ MOZ_ASSERT(callback,
+ "There should be at least one callback when first creating the "
+ "actor!");
+
+ RefPtr<ChildImpl> strongChildActor;
+ mActor.swap(strongChildActor);
+
+ RefPtr<ParentImpl> 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 = ChildImpl::GetNextCallback();
+ }
+
+ return NS_OK;
+ }
+
+ // Make sure the parent knows it is same process.
+ parentActor->SetOtherProcessId(base::GetCurrentProcId());
+
+ // Now that Open() has succeeded transfer the ownership of the actors to IPDL.
+ Unused << parentActor.forget();
+
+ auto threadLocalInfo =
+ static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(sThreadLocalIndex));
+
+ MOZ_ASSERT(threadLocalInfo);
+ MOZ_ASSERT(!threadLocalInfo->mActor);
+
+ RefPtr<ChildImpl>& childActor = threadLocalInfo->mActor;
+ strongChildActor.swap(childActor);
+
+ childActor->SetBoundThread();
+
+ while (callback) {
+ callback->ActorCreated(childActor);
+ callback = ChildImpl::GetNextCallback();
+ }
+
+ return NS_OK;
+}
+
+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<ParentImpl> aParentActor,
+ MessageLoop* aParentMessageLoop)
+{
+ AssertIsInMainProcess();
+ AssertIsOnMainThread();
+
+ RefPtr<ParentImpl> parentActor = aParentActor;
+ MOZ_ASSERT(parentActor);
+ MOZ_ASSERT(aParentMessageLoop);
+ MOZ_ASSERT(mEventTarget);
+
+ RefPtr<ChildImpl> childActor = new ChildImpl();
+
+ nsCOMPtr<nsIEventTarget> target;
+ mEventTarget.swap(target);
+
+ nsCOMPtr<nsIRunnable> 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<nsIEventTarget> 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 (XRE_IsParentProcess()) {
+ RefPtr<ParentImpl::CreateCallback> 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 (content->IsShuttingDown()) {
+ // The transport for ContentChild is shut down and can't be used to open
+ // PBackground.
+ DispatchFailureCallback(aEventTarget);
+ return false;
+ }
+
+ if (!PBackground::Open(content)) {
+ MOZ_CRASH("Failed to create top level actor!");
+ return false;
+ }
+
+ if (!sPendingTargets) {
+ sPendingTargets = new nsTArray<nsCOMPtr<nsIEventTarget>>(1);
+ ClearOnShutdown(&sPendingTargets);
+ }
+
+ sPendingTargets->AppendElement(aEventTarget);
+
+ return true;
+}
+
+// static
+void
+ChildImpl::DispatchFailureCallback(nsIEventTarget* aEventTarget)
+{
+ MOZ_ASSERT(aEventTarget);
+
+ nsCOMPtr<nsIRunnable> callbackRunnable = new FailedCreateCallbackRunnable();
+ if (NS_FAILED(aEventTarget->Dispatch(callbackRunnable, NS_DISPATCH_NORMAL))) {
+ NS_WARNING("Failed to dispatch CreateCallbackRunnable!");
+ }
+}
+
+void
+ChildImpl::ActorDestroy(ActorDestroyReason aWhy)
+{
+ AssertIsOnBoundThread();
+
+#ifdef DEBUG
+ MOZ_ASSERT(!mActorDestroyed);
+ mActorDestroyed = true;
+#endif
+
+ BackgroundChildImpl::ActorDestroy(aWhy);
+}
+
+NS_IMPL_ISUPPORTS(ChildImpl::ShutdownObserver, nsIObserver)
+
+NS_IMETHODIMP
+ChildImpl::ShutdownObserver::Observe(nsISupports* aSubject,
+ const char* aTopic,
+ const char16_t* aData)
+{
+ AssertIsOnMainThread();
+ MOZ_ASSERT(!strcmp(aTopic, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID));
+
+ ChildImpl::Shutdown();
+
+ return NS_OK;
+}