Bug 1212333 - WorkerDebuggerManager should live on the main thread;r=khuey
authorEddy Bruel <ejpbruel@mozilla.com>
Wed, 10 Feb 2016 10:45:02 +0100
changeset 283797 86f89d86b235c8cfdadd77f96c731dcdd3191b05
parent 283796 b6ac829c36bbb76734b496bd7b1678f5a079300b
child 283798 56e66f43d7ee4e52da7fc7add39ca86743225cc2
push id29991
push usercbook@mozilla.com
push dateThu, 11 Feb 2016 10:52:20 +0000
treeherdermozilla-central@d4d72e7b30da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskhuey
bugs1212333
milestone47.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 1212333 - WorkerDebuggerManager should live on the main thread;r=khuey
dom/workers/RuntimeService.cpp
dom/workers/WorkerDebuggerManager.cpp
dom/workers/WorkerDebuggerManager.h
layout/build/nsLayoutModule.cpp
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -1969,22 +1969,16 @@ void
 RuntimeService::Shutdown()
 {
   AssertIsOnMainThread();
 
   MOZ_ASSERT(!mShuttingDown);
   // That's it, no more workers.
   mShuttingDown = true;
 
-  // Remove all listeners from the worker debugger manager to ensure that it
-  // gets properly destroyed.
-  if (NS_FAILED(ClearWorkerDebuggerManagerListeners())) {
-    NS_WARNING("Failed to clear worker debugger manager listeners!");
-  }
-
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   NS_WARN_IF_FALSE(obs, "Failed to get observer service?!");
 
   // Tell anyone that cares that they're about to lose worker support.
   if (obs && NS_FAILED(obs->NotifyObservers(nullptr, WORKERS_SHUTDOWN_TOPIC,
                                             nullptr))) {
     NS_WARNING("NotifyObservers failed!");
   }
--- a/dom/workers/WorkerDebuggerManager.cpp
+++ b/dom/workers/WorkerDebuggerManager.cpp
@@ -3,72 +3,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 "WorkerDebuggerManager.h"
 
 #include "nsISimpleEnumerator.h"
 
+#include "mozilla/ClearOnShutdown.h"
+
 #include "WorkerPrivate.h"
 
 USING_WORKERS_NAMESPACE
 
+namespace {
+
 class RegisterDebuggerMainThreadRunnable final : public nsRunnable
 {
-  RefPtr<WorkerDebuggerManager> mManager;
   WorkerPrivate* mWorkerPrivate;
   bool mNotifyListeners;
 
 public:
-  RegisterDebuggerMainThreadRunnable(WorkerDebuggerManager* aManager,
-                                     WorkerPrivate* aWorkerPrivate,
+  RegisterDebuggerMainThreadRunnable(WorkerPrivate* aWorkerPrivate,
                                      bool aNotifyListeners)
-  : mManager(aManager),
-    mWorkerPrivate(aWorkerPrivate),
+  : mWorkerPrivate(aWorkerPrivate),
     mNotifyListeners(aNotifyListeners)
   { }
 
 private:
   ~RegisterDebuggerMainThreadRunnable()
   { }
 
   NS_IMETHOD
   Run() override
   {
-    mManager->RegisterDebuggerMainThread(mWorkerPrivate, mNotifyListeners);
+    WorkerDebuggerManager* manager = WorkerDebuggerManager::Get();
+    MOZ_ASSERT(manager);
 
+    manager->RegisterDebuggerMainThread(mWorkerPrivate, mNotifyListeners);
     return NS_OK;
   }
 };
 
 class UnregisterDebuggerMainThreadRunnable final : public nsRunnable
 {
-  RefPtr<WorkerDebuggerManager> mManager;
   WorkerPrivate* mWorkerPrivate;
 
 public:
-  UnregisterDebuggerMainThreadRunnable(WorkerDebuggerManager* aManager,
-                                       WorkerPrivate* aWorkerPrivate)
-  : mManager(aManager), mWorkerPrivate(aWorkerPrivate)
+  explicit UnregisterDebuggerMainThreadRunnable(WorkerPrivate* aWorkerPrivate)
+  : mWorkerPrivate(aWorkerPrivate)
   { }
 
 private:
   ~UnregisterDebuggerMainThreadRunnable()
   { }
 
   NS_IMETHOD
   Run() override
   {
-    mManager->UnregisterDebuggerMainThread(mWorkerPrivate);
+    WorkerDebuggerManager* manager = WorkerDebuggerManager::Get();
+    MOZ_ASSERT(manager);
 
+    manager->UnregisterDebuggerMainThread(mWorkerPrivate);
     return NS_OK;
   }
 };
 
+// Does not hold an owning reference.
+static WorkerDebuggerManager* gWorkerDebuggerManager;
+
+} /* anonymous namespace */
+
 BEGIN_WORKERS_NAMESPACE
 
 class WorkerDebuggerEnumerator final : public nsISimpleEnumerator
 {
   nsTArray<RefPtr<WorkerDebugger>> mDebuggers;
   uint32_t mIndex;
 
 public:
@@ -111,17 +119,64 @@ WorkerDebuggerManager::WorkerDebuggerMan
   AssertIsOnMainThread();
 }
 
 WorkerDebuggerManager::~WorkerDebuggerManager()
 {
   AssertIsOnMainThread();
 }
 
-NS_IMPL_ISUPPORTS(WorkerDebuggerManager, nsIWorkerDebuggerManager);
+// static
+already_AddRefed<WorkerDebuggerManager>
+WorkerDebuggerManager::GetInstance()
+{
+  RefPtr<WorkerDebuggerManager> manager = WorkerDebuggerManager::GetOrCreate();
+  return manager.forget();
+}
+
+// static
+WorkerDebuggerManager*
+WorkerDebuggerManager::GetOrCreate()
+{
+  AssertIsOnMainThread();
+
+  if (!gWorkerDebuggerManager) {
+    // The observer service now owns us until shutdown.
+    gWorkerDebuggerManager = new WorkerDebuggerManager();
+    if (NS_FAILED(gWorkerDebuggerManager->Init())) {
+      NS_WARNING("Failed to initialize worker debugger manager!");
+      gWorkerDebuggerManager = nullptr;
+      return nullptr;
+    }
+  }
+
+  return gWorkerDebuggerManager;
+}
+
+WorkerDebuggerManager*
+WorkerDebuggerManager::Get()
+{
+  MOZ_ASSERT(gWorkerDebuggerManager);
+  return gWorkerDebuggerManager;
+}
+
+NS_IMPL_ISUPPORTS(WorkerDebuggerManager, nsIObserver, nsIWorkerDebuggerManager);
+
+NS_IMETHODIMP
+WorkerDebuggerManager::Observe(nsISupports* aSubject, const char* aTopic,
+                               const char16_t* aData)
+{
+  if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
+    Shutdown();
+    return NS_OK;
+  }
+
+  NS_NOTREACHED("Unknown observer topic!");
+  return NS_OK;
+}
 
 NS_IMETHODIMP
 WorkerDebuggerManager::GetWorkerDebuggerEnumerator(
                                                   nsISimpleEnumerator** aResult)
 {
   AssertIsOnMainThread();
 
   RefPtr<WorkerDebuggerEnumerator> enumerator =
@@ -156,18 +211,30 @@ WorkerDebuggerManager::RemoveListener(
   if (!mListeners.Contains(aListener)) {
     return NS_OK;
   }
 
   mListeners.RemoveElement(aListener);
   return NS_OK;
 }
 
+nsresult
+WorkerDebuggerManager::Init()
+{
+  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+  NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE);
+
+  nsresult rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
 void
-WorkerDebuggerManager::ClearListeners()
+WorkerDebuggerManager::Shutdown()
 {
   AssertIsOnMainThread();
 
   MutexAutoLock lock(mMutex);
 
   mListeners.Clear();
 }
 
@@ -200,18 +267,17 @@ WorkerDebuggerManager::RegisterDebugger(
     bool hasListeners = false;
     {
       MutexAutoLock lock(mMutex);
 
       hasListeners = !mListeners.IsEmpty();
     }
 
     nsCOMPtr<nsIRunnable> runnable =
-      new RegisterDebuggerMainThreadRunnable(this, aWorkerPrivate,
-                                             hasListeners);
+      new RegisterDebuggerMainThreadRunnable(aWorkerPrivate, hasListeners);
     MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
       NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL)));
 
     if (hasListeners) {
       aWorkerPrivate->WaitForIsDebuggerRegistered(true);
     }
   }
 }
@@ -220,17 +286,17 @@ void
 WorkerDebuggerManager::UnregisterDebugger(WorkerPrivate* aWorkerPrivate)
 {
   aWorkerPrivate->AssertIsOnParentThread();
 
   if (NS_IsMainThread()) {
     UnregisterDebuggerMainThread(aWorkerPrivate);
   } else {
     nsCOMPtr<nsIRunnable> runnable =
-      new UnregisterDebuggerMainThreadRunnable(this, aWorkerPrivate);
+      new UnregisterDebuggerMainThreadRunnable(aWorkerPrivate);
     MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
       NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL)));
 
     aWorkerPrivate->WaitForIsDebuggerRegistered(false);
   }
 }
 
 void
--- a/dom/workers/WorkerDebuggerManager.h
+++ b/dom/workers/WorkerDebuggerManager.h
@@ -4,103 +4,116 @@
  * 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/. */
 
 #ifndef mozilla_dom_workers_workerdebuggermanager_h
 #define mozilla_dom_workers_workerdebuggermanager_h
 
 #include "Workers.h"
 
+#include "nsIObserver.h"
 #include "nsIWorkerDebuggerManager.h"
 
 #include "nsServiceManagerUtils.h"
 #include "nsTArray.h"
 
 #define WORKERDEBUGGERMANAGER_CID \
   { 0x62ec8731, 0x55ad, 0x4246, \
     { 0xb2, 0xea, 0xf2, 0x6c, 0x1f, 0xe1, 0x9d, 0x2d } }
 #define WORKERDEBUGGERMANAGER_CONTRACTID \
   "@mozilla.org/dom/workers/workerdebuggermanager;1"
 
 BEGIN_WORKERS_NAMESPACE
 
 class WorkerDebugger;
 
-class WorkerDebuggerManager final : public nsIWorkerDebuggerManager
+class WorkerDebuggerManager final : public nsIObserver,
+                                    public nsIWorkerDebuggerManager
 {
   Mutex mMutex;
 
   // Protected by mMutex.
   nsTArray<nsCOMPtr<nsIWorkerDebuggerManagerListener>> mListeners;
 
   // Only touched on the main thread.
   nsTArray<RefPtr<WorkerDebugger>> mDebuggers;
 
 public:
+  static already_AddRefed<WorkerDebuggerManager>
+  GetInstance();
+
   static WorkerDebuggerManager*
-  GetOrCreateService()
-  {
-    nsCOMPtr<nsIWorkerDebuggerManager> manager =
-      do_GetService(WORKERDEBUGGERMANAGER_CONTRACTID);
-    return static_cast<WorkerDebuggerManager*>(manager.get());
-  }
+  GetOrCreate();
+
+  static WorkerDebuggerManager*
+  Get();
 
   WorkerDebuggerManager();
 
-  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIOBSERVER
   NS_DECL_NSIWORKERDEBUGGERMANAGER
 
-  void ClearListeners();
+  nsresult
+  Init();
 
-  void RegisterDebugger(WorkerPrivate* aWorkerPrivate);
+  void
+  Shutdown();
+
+  void
+  RegisterDebugger(WorkerPrivate* aWorkerPrivate);
 
-  void UnregisterDebugger(WorkerPrivate* aWorkerPrivate);
+  void
+  RegisterDebuggerMainThread(WorkerPrivate* aWorkerPrivate,
+                             bool aNotifyListeners);
 
-  void RegisterDebuggerMainThread(WorkerPrivate* aWorkerPrivate,
-                                  bool aNotifyListeners);
+  void
+  UnregisterDebugger(WorkerPrivate* aWorkerPrivate);
 
-  void UnregisterDebuggerMainThread(WorkerPrivate* aWorkerPrivate);
+  void
+  UnregisterDebuggerMainThread(WorkerPrivate* aWorkerPrivate);
 
 private:
   virtual ~WorkerDebuggerManager();
 };
 
 inline nsresult
-ClearWorkerDebuggerManagerListeners()
-{
-  RefPtr<WorkerDebuggerManager> manager =
-    WorkerDebuggerManager::GetOrCreateService();
-  if (!manager) {
-    return NS_ERROR_FAILURE;
-  }
-
-  manager->ClearListeners();
-  return NS_OK;
-}
-
-inline nsresult
 RegisterWorkerDebugger(WorkerPrivate* aWorkerPrivate)
 {
-  RefPtr<WorkerDebuggerManager> manager =
-    WorkerDebuggerManager::GetOrCreateService();
-  if (!manager) {
-    return NS_ERROR_FAILURE;
+  WorkerDebuggerManager* manager;
+
+  if (NS_IsMainThread()) {
+    manager = WorkerDebuggerManager::GetOrCreate();
+    if (!manager) {
+      NS_WARNING("Failed to create worker debugger manager!");
+      return NS_ERROR_FAILURE;
+    }
+  }
+  else {
+    manager = WorkerDebuggerManager::Get();
   }
 
   manager->RegisterDebugger(aWorkerPrivate);
   return NS_OK;
 }
 
 inline nsresult
 UnregisterWorkerDebugger(WorkerPrivate* aWorkerPrivate)
 {
-  RefPtr<WorkerDebuggerManager> manager =
-    WorkerDebuggerManager::GetOrCreateService();
-  if (!manager) {
-    return NS_ERROR_FAILURE;
+  WorkerDebuggerManager* manager;
+
+  if (NS_IsMainThread()) {
+    manager = WorkerDebuggerManager::GetOrCreate();
+    if (!manager) {
+      NS_WARNING("Failed to create worker debugger manager!");
+      return NS_ERROR_FAILURE;
+    }
+  }
+  else {
+    manager = WorkerDebuggerManager::Get();
   }
 
   manager->UnregisterDebugger(aWorkerPrivate);
   return NS_OK;
 }
 
 END_WORKERS_NAMESPACE
 
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -317,17 +317,18 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(Exception
 NS_GENERIC_FACTORY_CONSTRUCTOR(DOMSessionStorageManager)
 NS_GENERIC_FACTORY_CONSTRUCTOR(DOMLocalStorageManager)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(DOMRequestService,
                                          DOMRequestService::FactoryCreate)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(QuotaManagerService,
                                          QuotaManagerService::FactoryCreate)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(ServiceWorkerManager,
                                          ServiceWorkerManager::GetInstance)
-NS_GENERIC_FACTORY_CONSTRUCTOR(WorkerDebuggerManager)
+NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(WorkerDebuggerManager,
+                                         WorkerDebuggerManager::GetInstance)
 
 #ifdef MOZ_WIDGET_GONK
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(SystemWorkerManager,
                                          SystemWorkerManager::FactoryCreate)
 #endif
 #ifdef MOZ_B2G_BT
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(BluetoothService,
                                          BluetoothService::FactoryCreate)