Bug 977762 - 'Provide a mechanism to get the PContentParent associated with a PBackgroundParent'. r=mrbkap.
authorBen Turner <bent.mozilla@gmail.com>
Thu, 27 Feb 2014 11:59:12 -0800
changeset 194929 cd8094ea37af9f658509720ec7747101d738a75b
parent 194928 ae1fe345280677dd22355833b27848d34ece49c5
child 194930 0308295814658f77bc5ebb48ee2456dbbe780970
push id3624
push userasasaki@mozilla.com
push dateMon, 09 Jun 2014 21:49:01 +0000
treeherdermozilla-beta@b1a5da15899a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmrbkap
bugs977762
milestone31.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 977762 - 'Provide a mechanism to get the PContentParent associated with a PBackgroundParent'. r=mrbkap.
ipc/glue/BackgroundImpl.cpp
ipc/glue/BackgroundParent.h
--- a/ipc/glue/BackgroundImpl.cpp
+++ b/ipc/glue/BackgroundImpl.cpp
@@ -30,16 +30,22 @@
 #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);                                                         \
     }                                                                          \
   }                                                                            \
@@ -81,17 +87,17 @@ void
 AssertIsInChildProcess()
 {
   MOZ_ASSERT(IsChildProcess());
 }
 
 void
 AssertIsOnMainThread()
 {
-  MOZ_ASSERT(NS_IsMainThread());
+  THREADSAFETY_ASSERT(NS_IsMainThread());
 }
 
 // -----------------------------------------------------------------------------
 // ParentImpl Declaration
 // -----------------------------------------------------------------------------
 
 class ParentImpl MOZ_FINAL : public BackgroundParentImpl
 {
@@ -169,73 +175,89 @@ private:
   // Only touched on the main thread, null if this is a same-process actor.
   nsRefPtr<ContentParent> 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<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
   CreateActorForSameProcess(CreateCallback* aCallback);
 
   static bool
   IsOnBackgroundThread()
   {
     return PR_GetCurrentThread() == sBackgroundPRThread;
   }
 
   static void
   AssertIsOnBackgroundThread()
   {
-    MOZ_ASSERT(IsOnBackgroundThread());
+    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 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),
-    mIsOtherProcessActorDEBUG(false)
+  : 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),
-    mIsOtherProcessActorDEBUG(true)
+    mIsOtherProcessActor(true), mActorDestroyed(false)
   {
     AssertIsInMainProcess();
     AssertIsOnMainThread();
     MOZ_ASSERT(aContent);
     MOZ_ASSERT(aTransport);
   }
 
   ~ParentImpl()
@@ -252,17 +274,17 @@ private:
   void
   SetLiveActorArray(nsTArray<ParentImpl*>* aLiveActorArray)
   {
     AssertIsInMainProcess();
     AssertIsOnBackgroundThread();
     MOZ_ASSERT(aLiveActorArray);
     MOZ_ASSERT(!aLiveActorArray->Contains(this));
     MOZ_ASSERT(!mLiveActorArray);
-    MOZ_ASSERT(mIsOtherProcessActorDEBUG);
+    MOZ_ASSERT(mIsOtherProcessActor);
 
     mLiveActorArray = aLiveActorArray;
     mLiveActorArray->AppendElement(this);
   }
 
   // These methods are only called by IPDL.
   virtual IToplevelProtocol*
   CloneToplevel(const InfallibleTArray<ProtocolFdMapping>& aFds,
@@ -313,37 +335,44 @@ class ChildImpl MOZ_FINAL : public Backg
   // 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;
 
+#ifdef RELEASE_BUILD
   DebugOnly<nsIThread*> mBoundThread;
+#else
+  nsIThread* mBoundThread;
+#endif
 
 public:
   static bool
   OpenProtocolOnMainThread(nsIEventTarget* aEventTarget);
 
   static void
   Shutdown();
 
   void
   AssertIsOnBoundThread()
   {
-    MOZ_ASSERT(mBoundThread);
+    THREADSAFETY_ASSERT(mBoundThread);
 
+#ifdef RELEASE_BUILD
     DebugOnly<bool> current;
-    MOZ_ASSERT(NS_SUCCEEDED(mBoundThread->IsOnCurrentThread(&current)));
-
-    MOZ_ASSERT(current);
+#else
+    bool current;
+#endif
+    THREADSAFETY_ASSERT(
+      NS_SUCCEEDED(mBoundThread->IsOnCurrentThread(&current)));
+    THREADSAFETY_ASSERT(current);
   }
 
-
   ChildImpl()
   : mBoundThread(nullptr)
   {
     AssertIsOnMainThread();
   }
 
   NS_INLINE_DECL_REFCOUNTING(ChildImpl)
 
@@ -382,21 +411,23 @@ private:
 
   // This class is reference counted.
   ~ChildImpl()
   { }
 
   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.
   virtual void
   ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
 };
 
 // -----------------------------------------------------------------------------
@@ -736,16 +767,30 @@ AssertIsOnBackgroundThread()
 } // 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
 PBackgroundParent*
 BackgroundParent::Alloc(ContentParent* aContent,
                         Transport* aTransport,
                         ProcessId aOtherProcess)
 {
   return ParentImpl::Alloc(aContent, aTransport, aOtherProcess);
 }
 
@@ -822,16 +867,56 @@ StaticAutoPtr<nsTArray<nsCOMPtr<nsIEvent
 
 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,
                   Transport* aTransport,
                   ProcessId aOtherProcess)
 {
   AssertIsInMainProcess();
   AssertIsOnMainThread();
   MOZ_ASSERT(aTransport);
@@ -1090,20 +1175,20 @@ ParentImpl::Destroy()
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(destroyRunnable)));
 }
 
 void
 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,
                                      new DeleteTask<Transport>(mTransport));
     mTransport = nullptr;
   }
 
   ProcessHandle otherProcess = OtherProcess();
@@ -1162,20 +1247,23 @@ ParentImpl::CloneToplevel(const Infallib
   return nullptr;
 }
 
 void
 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;
   }
 
   // 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
--- a/ipc/glue/BackgroundParent.h
+++ b/ipc/glue/BackgroundParent.h
@@ -4,37 +4,58 @@
 
 #ifndef mozilla_ipc_backgroundparent_h__
 #define mozilla_ipc_backgroundparent_h__
 
 #include "base/process.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/ipc/Transport.h"
 
+template <class> class already_AddRefed;
+
 namespace mozilla {
 namespace dom {
 
 class ContentParent;
 
 } // namespace dom
 
 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;
 
   typedef base::ProcessId ProcessId;
   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*
   Alloc(ContentParent* aContent,
         Transport* aTransport,
         ProcessId aOtherProcess);
 };