diff --git a/ipc/glue/BackgroundImpl.cpp b/ipc/glue/BackgroundImpl.cpp index 8c498f1b9b52340a9172f5ff4534ef3967dd9d40..f3e987031301ede37c6f17ca69862d7af70a1809 100644 --- a/ipc/glue/BackgroundImpl.cpp +++ b/ipc/glue/BackgroundImpl.cpp @@ -35,6 +35,12 @@ #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()) { \ @@ -86,7 +92,7 @@ AssertIsInChildProcess() void AssertIsOnMainThread() { - MOZ_ASSERT(NS_IsMainThread()); + THREADSAFETY_ASSERT(NS_IsMainThread()); } // ----------------------------------------------------------------------------- @@ -174,10 +180,18 @@ private: // 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<ParentImpl*>* mLiveActorArray; - // Used to assert things in DEBUG builds. - DebugOnly<bool> mIsOtherProcessActorDEBUG; + // 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 @@ -192,7 +206,7 @@ public: static void AssertIsOnBackgroundThread() { - MOZ_ASSERT(IsOnBackgroundThread()); + THREADSAFETY_ASSERT(IsOnBackgroundThread()); } NS_INLINE_DECL_REFCOUNTING(ParentImpl) @@ -201,6 +215,14 @@ public: Destroy(); private: + // Forwarded from BackgroundParent. + static bool + IsOtherProcessActor(PBackgroundParent* aBackgroundActor); + + // Forwarded from BackgroundParent. + static already_AddRefed<ContentParent> + GetContentParent(PBackgroundParent* aBackgroundActor); + // Forwarded from BackgroundParent. static PBackgroundParent* Alloc(ContentParent* aContent, @@ -218,8 +240,8 @@ private: // For same-process actors. ParentImpl() - : mTransport(nullptr), mLiveActorArray(nullptr), - mIsOtherProcessActorDEBUG(false) + : mTransport(nullptr), mLiveActorArray(nullptr), mIsOtherProcessActor(false), + mActorDestroyed(false) { AssertIsInMainProcess(); AssertIsOnMainThread(); @@ -230,7 +252,7 @@ private: // For other-process actors. ParentImpl(ContentParent* aContent, Transport* aTransport) : mContent(aContent), mTransport(aTransport), mLiveActorArray(nullptr), - mIsOtherProcessActorDEBUG(true) + mIsOtherProcessActor(true), mActorDestroyed(false) { AssertIsInMainProcess(); AssertIsOnMainThread(); @@ -257,7 +279,7 @@ private: MOZ_ASSERT(aLiveActorArray); MOZ_ASSERT(!aLiveActorArray->Contains(this)); MOZ_ASSERT(!mLiveActorArray); - MOZ_ASSERT(mIsOtherProcessActorDEBUG); + MOZ_ASSERT(mIsOtherProcessActor); mLiveActorArray = aLiveActorArray; mLiveActorArray->AppendElement(this); @@ -318,7 +340,11 @@ class ChildImpl MOZ_FINAL : public BackgroundChildImpl // create the background thread after application shutdown has started. static bool sShutdownHasStarted; +#ifdef RELEASE_BUILD DebugOnly<nsIThread*> mBoundThread; +#else + nsIThread* mBoundThread; +#endif public: static bool @@ -330,15 +356,18 @@ public: void AssertIsOnBoundThread() { - MOZ_ASSERT(mBoundThread); + THREADSAFETY_ASSERT(mBoundThread); +#ifdef RELEASE_BUILD DebugOnly<bool> current; - MOZ_ASSERT(NS_SUCCEEDED(mBoundThread->IsOnCurrentThread(¤t))); - - MOZ_ASSERT(current); +#else + bool current; +#endif + THREADSAFETY_ASSERT( + NS_SUCCEEDED(mBoundThread->IsOnCurrentThread(¤t))); + THREADSAFETY_ASSERT(current); } - ChildImpl() : mBoundThread(nullptr) { @@ -387,11 +416,13 @@ private: void SetBoundThread() { -#ifdef DEBUG - MOZ_ASSERT(!mBoundThread); + THREADSAFETY_ASSERT(!mBoundThread); + +#if defined(DEBUG) || !defined(RELEASE_BUILD) mBoundThread = NS_GetCurrentThread(); - MOZ_ASSERT(mBoundThread); #endif + + THREADSAFETY_ASSERT(mBoundThread); } // Only called by IPDL. @@ -740,6 +771,20 @@ AssertIsOnBackgroundThread() // 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 PBackgroundParent* BackgroundParent::Alloc(ContentParent* aContent, @@ -826,6 +871,46 @@ 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. + nsCOMPtr<nsIRunnable> 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, @@ -1095,10 +1180,10 @@ ParentImpl::MainThreadActorDestroy() { AssertIsInMainProcess(); AssertIsOnMainThread(); - MOZ_ASSERT_IF(mIsOtherProcessActorDEBUG, mContent); - MOZ_ASSERT_IF(!mIsOtherProcessActorDEBUG, !mContent); - MOZ_ASSERT_IF(mIsOtherProcessActorDEBUG, mTransport); - MOZ_ASSERT_IF(!mIsOtherProcessActorDEBUG, !mTransport); + 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, @@ -1167,10 +1252,13 @@ ParentImpl::ActorDestroy(ActorDestroyReason aWhy) { AssertIsInMainProcess(); AssertIsOnBackgroundThread(); - MOZ_ASSERT_IF(mIsOtherProcessActorDEBUG, mLiveActorArray); + MOZ_ASSERT(!mActorDestroyed); + MOZ_ASSERT_IF(mIsOtherProcessActor, mLiveActorArray); BackgroundParentImpl::ActorDestroy(aWhy); + mActorDestroyed = true; + if (mLiveActorArray) { MOZ_ALWAYS_TRUE(mLiveActorArray->RemoveElement(this)); mLiveActorArray = nullptr; diff --git a/ipc/glue/BackgroundParent.h b/ipc/glue/BackgroundParent.h index 6e47ae0d9d94bab3d7a5224127dcf88d1c99ff5d..615e34dbc4d49c027837fceccc9279c687912233 100644 --- a/ipc/glue/BackgroundParent.h +++ b/ipc/glue/BackgroundParent.h @@ -9,6 +9,8 @@ #include "mozilla/Attributes.h" #include "mozilla/ipc/Transport.h" +template <class> class already_AddRefed; + namespace mozilla { namespace dom { @@ -20,8 +22,8 @@ namespace ipc { class PBackgroundParent; -// This class is not designed for public consumption. It must only be used by -// ContentParent. +// This class is not designed for public consumption beyond the few static +// member functions. class BackgroundParent MOZ_FINAL { friend class mozilla::dom::ContentParent; @@ -30,6 +32,25 @@ class BackgroundParent MOZ_FINAL typedef mozilla::dom::ContentParent ContentParent; typedef mozilla::ipc::Transport Transport; +public: + // This function allows the caller to determine if the given parent actor + // corresponds to a child actor from another process or a child actor from a + // different thread in the same process. + // This function may only be called on the background thread. + static bool + IsOtherProcessActor(PBackgroundParent* aBackgroundActor); + + // This function returns the ContentParent associated with the parent actor if + // the parent actor corresponds to a child actor from another process. If the + // parent actor corresponds to a child actor from a different thread in the + // same process then this function returns null. + // This function may only be called on the background thread. However, + // ContentParent is not threadsafe and the returned pointer may not be used on + // any thread other than the main thread. Callers must take care to use (and + // release) the returned pointer appropriately. + static already_AddRefed<ContentParent> + GetContentParent(PBackgroundParent* aBackgroundActor); + private: // Only called by ContentParent for cross-process actors. static PBackgroundParent*