Bug 796829 - Final version: Destroy BluetoothScoManager instance while in shutdown, r=qdot
authorGina Yeh <gyeh@mozilla.com>
Wed, 03 Oct 2012 11:27:36 +0800
changeset 109108 61129bd6f26827fa5cc43ffd1ee1392035792106
parent 109107 c9a2c26e6ea6f1827fca456f992632c827df39cd
child 109109 d9cc5f6386106ac7ec85924425a3f5f4af0ca92b
push id82
push usershu@rfrn.org
push dateFri, 05 Oct 2012 13:20:22 +0000
reviewersqdot
bugs796829
milestone18.0a1
Bug 796829 - Final version: Destroy BluetoothScoManager instance while in shutdown, r=qdot
dom/bluetooth/BluetoothScoManager.cpp
dom/bluetooth/BluetoothScoManager.h
--- a/dom/bluetooth/BluetoothScoManager.cpp
+++ b/dom/bluetooth/BluetoothScoManager.cpp
@@ -8,76 +8,161 @@
 
 #include "BluetoothScoManager.h"
 
 #include "BluetoothReplyRunnable.h"
 #include "BluetoothService.h"
 #include "BluetoothServiceUuid.h"
 
 #include "mozilla/Services.h"
+#include "mozilla/StaticPtr.h"
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
 #include "nsContentUtils.h"
 #include "nsIDOMDOMRequest.h"
 #include "nsIObserverService.h"
 #include "nsISystemMessagesInternal.h"
 #include "nsVariant.h"
 
 USING_BLUETOOTH_NAMESPACE
+using namespace mozilla;
 using namespace mozilla::ipc;
+USING_BLUETOOTH_NAMESPACE
 
-static nsRefPtr<BluetoothScoManager> sInstance;
+namespace {
+StaticRefPtr<BluetoothScoManager> gBluetoothScoManager;
+bool gInShutdown = false;
 static nsCOMPtr<nsIThread> sScoCommandThread;
+} // anonymous namespace
+
+NS_IMPL_ISUPPORTS1(BluetoothScoManager, nsIObserver)
 
 BluetoothScoManager::BluetoothScoManager()
+  : mConnected(false)
 {
-  if (!sScoCommandThread) {
-    if (NS_FAILED(NS_NewThread(getter_AddRefs(sScoCommandThread)))) {
-      NS_ERROR("Failed to new thread for sScoCommandThread");
-    }
+}
+
+bool
+BluetoothScoManager::Init()
+{
+  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+  NS_ENSURE_TRUE(obs, false);
+
+  if (NS_FAILED(obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false))) {
+    NS_WARNING("Failed to add shutdown observer!");
+    return false;
   }
-  mConnected = false;
+
+  if (!sScoCommandThread &&
+      NS_FAILED(NS_NewThread(getter_AddRefs(sScoCommandThread)))) {
+    NS_ERROR("Failed to new thread for sScoCommandThread");
+    return false;
+  }
+  return true;
 }
 
 BluetoothScoManager::~BluetoothScoManager()
 {
+  Cleanup();
+}
+
+void
+BluetoothScoManager::Cleanup()
+{
   // Shut down the command thread if it still exists.
   if (sScoCommandThread) {
     nsCOMPtr<nsIThread> thread;
     sScoCommandThread.swap(thread);
     if (NS_FAILED(thread->Shutdown())) {
       NS_WARNING("Failed to shut down the bluetooth hfpmanager command thread!");
     }
   }
+
+  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+  if (obs &&
+      NS_FAILED(obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID))) {
+    NS_WARNING("Can't unregister observers!");
+  }
 }
 
 //static
 BluetoothScoManager*
 BluetoothScoManager::Get()
 {
-  if (sInstance == nullptr) {
-    sInstance = new BluetoothScoManager();
+  MOZ_ASSERT(NS_IsMainThread());
+
+  // If we already exist, exit early
+  if (!gBluetoothScoManager) {
+    return gBluetoothScoManager;
+  }
+
+  // If we already exist, exit early
+  if (gBluetoothScoManager) {
+    return gBluetoothScoManager;
+  }
+
+  // If we're in shutdown, don't create a new instance
+  if (gInShutdown) {
+    NS_WARNING("BluetoothScoManager can't be created during shutdown");
+    return nullptr;
   }
 
-  // TODO: destroy pointer sInstance on shutdown
-  return sInstance;
+  // Create new instance, register, return
+  nsRefPtr<BluetoothScoManager> manager = new BluetoothScoManager();
+  NS_ENSURE_TRUE(manager, nullptr);
+
+  if (!manager->Init()) {
+    manager->Cleanup();
+    return nullptr;
+  }
+
+  gBluetoothScoManager = manager;
+  return gBluetoothScoManager;
+}
+
+nsresult
+BluetoothScoManager::Observe(nsISupports* aSubject,
+                             const char* aTopic,
+                             const PRUnichar* aData)
+{
+  if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
+    return HandleShutdown();
+  }
+
+  MOZ_ASSERT(false, "BluetoothScoManager got unexpected topic!");
+  return NS_ERROR_UNEXPECTED;
 }
 
 // Virtual function of class SocketConsumer
 void
 BluetoothScoManager::ReceiveSocketData(mozilla::ipc::UnixSocketRawData* aMessage)
 {
   // SCO socket do nothing here
   MOZ_NOT_REACHED("This should never be called!");
 }
 
+nsresult
+BluetoothScoManager::HandleShutdown()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  gInShutdown = true;
+  Cleanup();
+  gBluetoothScoManager = nullptr;
+  return NS_OK;
+}
+
 bool
 BluetoothScoManager::Connect(const nsAString& aDeviceObjectPath)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
+  if (gInShutdown) {
+    MOZ_ASSERT(false, "Connect called while in shutdown!");
+    return false;
+  }
+
   if (mConnected) {
     NS_WARNING("Sco socket has been ready");
     return true;
   }
 
   BluetoothService* bs = BluetoothService::Get();
   if (!bs) {
     NS_WARNING("BluetoothService not available!");
--- a/dom/bluetooth/BluetoothScoManager.h
+++ b/dom/bluetooth/BluetoothScoManager.h
@@ -4,35 +4,43 @@
  * 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_bluetooth_bluetoothscomanager_h__
 #define mozilla_dom_bluetooth_bluetoothscomanager_h__
 
 #include "BluetoothCommon.h"
 #include "mozilla/ipc/UnixSocket.h"
+#include "nsIObserver.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothReplyRunnable;
 
 class BluetoothScoManager : public mozilla::ipc::UnixSocketConsumer
+                          , public nsIObserver
 {
 public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+
   ~BluetoothScoManager();
 
   static BluetoothScoManager* Get();
   void ReceiveSocketData(mozilla::ipc::UnixSocketRawData* aMessage);
 
   bool Connect(const nsAString& aDeviceObjectPath);
   void Disconnect();
   void SetConnected(bool aConnected);
   bool GetConnected();
 
 private:
   BluetoothScoManager();
+  bool Init();
+  void Cleanup();
+  nsresult HandleShutdown();
   void CreateScoSocket(const nsAString& aDevicePath);
   bool mConnected;
 };
 
 END_BLUETOOTH_NAMESPACE
 
 #endif