Allow top-level protocols the ability to notify GPUProcessManager when their actors are unexpectedly destroyed. (bug 1282348 part 4, r=billm)
authorDavid Anderson <danderson@mozilla.com>
Sun, 17 Jul 2016 21:24:28 -0700
changeset 330400 1276872114941b9cb7ab00bca24bd9c322191748
parent 330399 f26200be9e80dcab281d843d4cc3ce70fe2fbc1a
child 330401 e6bd9062617fecf80cf520885e367776d02c9745
push id9858
push userjlund@mozilla.com
push dateMon, 01 Aug 2016 14:37:10 +0000
treeherdermozilla-aurora@203106ef6cb6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbillm
bugs1282348
milestone50.0a1
Allow top-level protocols the ability to notify GPUProcessManager when their actors are unexpectedly destroyed. (bug 1282348 part 4, r=billm)
gfx/ipc/GPUProcessHost.cpp
gfx/ipc/GPUProcessHost.h
gfx/ipc/GPUProcessManager.cpp
gfx/ipc/GPUProcessManager.h
--- a/gfx/ipc/GPUProcessHost.cpp
+++ b/gfx/ipc/GPUProcessHost.cpp
@@ -13,16 +13,17 @@
 namespace mozilla {
 namespace gfx {
 
 GPUProcessHost::GPUProcessHost(Listener* aListener)
  : GeckoChildProcessHost(GeckoProcessType_GPU),
    mListener(aListener),
    mTaskFactory(this),
    mLaunchPhase(LaunchPhase::Unlaunched),
+   mProcessToken(0),
    mShutdownRequested(false)
 {
   MOZ_COUNT_CTOR(GPUProcessHost);
 }
 
 GPUProcessHost::~GPUProcessHost()
 {
   MOZ_COUNT_DTOR(GPUProcessHost);
@@ -104,25 +105,28 @@ GPUProcessHost::OnChannelConnectedTask()
 void
 GPUProcessHost::OnChannelErrorTask()
 {
   if (mLaunchPhase == LaunchPhase::Waiting) {
     InitAfterConnect(false);
   }
 }
 
+static uint64_t sProcessTokenCounter = 0;
+
 void
 GPUProcessHost::InitAfterConnect(bool aSucceeded)
 {
   MOZ_ASSERT(mLaunchPhase == LaunchPhase::Waiting);
   MOZ_ASSERT(!mGPUChild);
 
   mLaunchPhase = LaunchPhase::Complete;
 
   if (aSucceeded) {
+    mProcessToken = ++sProcessTokenCounter;
     mGPUChild = MakeUnique<GPUChild>(this);
     DebugOnly<bool> rv =
       mGPUChild->Open(GetChannel(), base::GetProcId(GetChildProcessHandle()));
     MOZ_ASSERT(rv);
 
     mGPUChild->Init();
   }
 
@@ -189,16 +193,22 @@ GPUProcessHost::KillHard(const char* aRe
     NS_WARNING("failed to kill subprocess!");
   }
 
   SetAlreadyDead();
   XRE_GetIOMessageLoop()->PostTask(
     NewRunnableFunction(&ProcessWatcher::EnsureProcessTerminated, handle, /*force=*/true));
 }
 
+uint64_t
+GPUProcessHost::GetProcessToken() const
+{
+  return mProcessToken;
+}
+
 static void
 DelayedDeleteSubprocess(GeckoChildProcessHost* aSubprocess)
 {
   XRE_GetIOMessageLoop()->
     PostTask(mozilla::MakeAndAddRef<DeleteTask<GeckoChildProcessHost>>(aSubprocess));
 }
 
 void
--- a/gfx/ipc/GPUProcessHost.h
+++ b/gfx/ipc/GPUProcessHost.h
@@ -69,16 +69,20 @@ public:
   void Shutdown();
 
   // Return the actor for the top-level actor of the process. If the process
   // has not connected yet, this returns null.
   GPUChild* GetActor() const {
     return mGPUChild.get();
   }
 
+  // Return a unique id for this process, guaranteed not to be shared with any
+  // past or future instance of GPUProcessHost.
+  uint64_t GetProcessToken() const;
+
   bool IsConnected() const {
     return !!mGPUChild;
   }
 
   // Called on the IO thread.
   void OnChannelConnected(int32_t peer_pid) override;
   void OnChannelError() override;
 
@@ -109,17 +113,17 @@ private:
   enum class LaunchPhase {
     Unlaunched,
     Waiting,
     Complete
   };
   LaunchPhase mLaunchPhase;
 
   UniquePtr<GPUChild> mGPUChild;
-  Listener* listener_;
+  uint64_t mProcessToken;
 
   bool mShutdownRequested;
 };
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif // _include_mozilla_gfx_ipc_GPUProcessHost_h_
--- a/gfx/ipc/GPUProcessManager.cpp
+++ b/gfx/ipc/GPUProcessManager.cpp
@@ -1,17 +1,17 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=99: */
 /* 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 "GPUProcessManager.h"
 #include "GPUProcessHost.h"
+#include "mozilla/layers/InProcessCompositorSession.h"
 #include "mozilla/StaticPtr.h"
-#include "mozilla/layers/InProcessCompositorSession.h"
 #include "nsContentUtils.h"
 
 namespace mozilla {
 namespace gfx {
 
 using namespace mozilla::layers;
 
 static StaticAutoPtr<GPUProcessManager> sSingleton;
@@ -31,17 +31,18 @@ GPUProcessManager::Initialize()
 
 void
 GPUProcessManager::Shutdown()
 {
   sSingleton = nullptr;
 }
 
 GPUProcessManager::GPUProcessManager()
- : mNextLayerTreeId(0),
+ : mTaskFactory(this),
+   mNextLayerTreeId(0),
    mProcess(nullptr),
    mGPUChild(nullptr)
 {
   mObserver = new Observer(this);
   nsContentUtils::RegisterShutdownObserver(mObserver);
 }
 
 GPUProcessManager::~GPUProcessManager()
@@ -123,34 +124,59 @@ GPUProcessManager::OnProcessLaunchComple
   MOZ_ASSERT(mProcess && mProcess == aHost);
 
   if (!mProcess->IsConnected()) {
     DisableGPUProcess("Failed to launch GPU process");
     return;
   }
 
   mGPUChild = mProcess->GetActor();
+  mProcessToken = mProcess->GetProcessToken();
 }
 
 void
 GPUProcessManager::OnProcessUnexpectedShutdown(GPUProcessHost* aHost)
 {
   MOZ_ASSERT(mProcess && mProcess == aHost);
 
   DestroyProcess();
 }
 
 void
+GPUProcessManager::NotifyRemoteActorDestroyed(const uint64_t& aProcessToken)
+{
+  if (!NS_IsMainThread()) {
+    RefPtr<Runnable> task = mTaskFactory.NewRunnableMethod(
+      &GPUProcessManager::NotifyRemoteActorDestroyed, aProcessToken);
+    NS_DispatchToMainThread(task.forget());
+    return;
+  }
+
+  if (mProcessToken != aProcessToken) {
+    // This token is for an older process; we can safely ignore it.
+    return;
+  }
+
+  // One of the bridged top-level actors for the GPU process has been
+  // prematurely terminated, and we're receiving a notification. This
+  // can happen if the ActorDestroy for a bridged protocol fires
+  // before the ActorDestroy for PGPUChild.
+  MOZ_ASSERT(mProcess);
+  DestroyProcess();
+}
+
+void
 GPUProcessManager::DestroyProcess()
 {
   if (!mProcess) {
     return;
   }
 
   mProcess->Shutdown();
+  mProcessToken = 0;
   mProcess = nullptr;
   mGPUChild = nullptr;
 }
 
 RefPtr<CompositorSession>
 GPUProcessManager::CreateTopLevelCompositor(nsIWidget* aWidget,
                                             ClientLayerManager* aLayerManager,
                                             CSSToLayoutDeviceScale aScale,
--- a/gfx/ipc/GPUProcessManager.h
+++ b/gfx/ipc/GPUProcessManager.h
@@ -7,16 +7,17 @@
 #define _include_mozilla_gfx_ipc_GPUProcessManager_h_
 
 #include "base/basictypes.h"
 #include "base/process.h"
 #include "Units.h"
 #include "mozilla/dom/ipc/IdType.h"
 #include "mozilla/gfx/GPUProcessHost.h"
 #include "mozilla/gfx/Point.h"
+#include "mozilla/ipc/TaskFactory.h"
 #include "mozilla/ipc/Transport.h"
 #include "nsIObserverService.h"
 
 namespace mozilla {
 namespace layers {
 class APZCTreeManager;
 class CompositorSession;
 class ClientLayerManager;
@@ -101,16 +102,20 @@ public:
   bool UpdateRemoteContentController(uint64_t aLayersId,
                                      dom::ContentParent* aContentParent,
                                      const dom::TabId& aTabId,
                                      dom::TabParent* aBrowserParent);
 
   void OnProcessLaunchComplete(GPUProcessHost* aHost) override;
   void OnProcessUnexpectedShutdown(GPUProcessHost* aHost) override;
 
+  // Notify the GPUProcessManager that a top-level PGPU protocol has been
+  // terminated. This may be called from any thread.
+  void NotifyRemoteActorDestroyed(const uint64_t& aProcessToken);
+
   // Returns access to the PGPU protocol if a GPU process is present.
   GPUChild* GetGPUChild() {
     return mGPUChild;
   }
 
 private:
   // Called from our xpcom-shutdown observer.
   void OnXPCOMShutdown();
@@ -136,17 +141,20 @@ private:
     ~Observer() {}
 
     GPUProcessManager* mManager;
   };
   friend class Observer;
 
 private:
   RefPtr<Observer> mObserver;
+  ipc::TaskFactory<GPUProcessManager> mTaskFactory;
   uint64_t mNextLayerTreeId;
+
   GPUProcessHost* mProcess;
+  uint64_t mProcessToken;
   GPUChild* mGPUChild;
 };
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif // _include_mozilla_gfx_ipc_GPUProcessManager_h_