b=865241 make HRTFDatabaseLoader ref-counting thread-safe r=ehsan
authorKarl Tomlinson <karlt+@karlt.net>
Fri, 09 Aug 2013 09:59:56 +1200
changeset 153777 b085cd4bd4cad752405777968990dd048ee27acf
parent 153776 a867ee21d619a885d5aa8cce589092b70549237b
child 153778 c02414b48056f93938993e63db8e638f91397b2f
push id2859
push userakeybl@mozilla.com
push dateMon, 16 Sep 2013 19:14:59 +0000
treeherdermozilla-beta@87d3c51cd2bf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersehsan
bugs865241
milestone25.0a2
b=865241 make HRTFDatabaseLoader ref-counting thread-safe r=ehsan (transplanted from c19e2f326d156dfa40341a34c3454004bff0d006)
content/media/webaudio/blink/HRTFDatabaseLoader.cpp
content/media/webaudio/blink/HRTFDatabaseLoader.h
--- a/content/media/webaudio/blink/HRTFDatabaseLoader.cpp
+++ b/content/media/webaudio/blink/HRTFDatabaseLoader.cpp
@@ -60,17 +60,18 @@ TemporaryRef<HRTFDatabaseLoader> HRTFDat
     entry->mLoader = loader;
 
     loader->loadAsynchronously();
 
     return loader;
 }
 
 HRTFDatabaseLoader::HRTFDatabaseLoader(float sampleRate)
-    : m_threadLock("HRTFDatabaseLoader")
+    : m_refCnt(0)
+    , m_threadLock("HRTFDatabaseLoader")
     , m_databaseLoaderThread(nullptr)
     , m_databaseSampleRate(sampleRate)
 {
     MOZ_ASSERT(NS_IsMainThread());
 }
 
 HRTFDatabaseLoader::~HRTFDatabaseLoader()
 {
@@ -82,16 +83,57 @@ HRTFDatabaseLoader::~HRTFDatabaseLoader(
     // Remove ourself from the map.
     s_loaderMap->RemoveEntry(m_databaseSampleRate);
     if (s_loaderMap->Count() == 0) {
         delete s_loaderMap;
         s_loaderMap = nullptr;
     }
 }
 
+class HRTFDatabaseLoader::ProxyReleaseEvent MOZ_FINAL : public nsRunnable {
+public:
+    explicit ProxyReleaseEvent(HRTFDatabaseLoader* loader) : mLoader(loader) {}
+    NS_IMETHOD Run() MOZ_OVERRIDE
+    {
+        mLoader->MainThreadRelease();
+        return NS_OK;
+    }
+private:
+    HRTFDatabaseLoader* mLoader;
+};
+
+void HRTFDatabaseLoader::ProxyRelease()
+{
+    nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
+    if (MOZ_LIKELY(mainThread)) {
+        nsRefPtr<ProxyReleaseEvent> event = new ProxyReleaseEvent(this);
+        DebugOnly<nsresult> rv =
+            mainThread->Dispatch(event, NS_DISPATCH_NORMAL);
+        MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed to dispatch release event");
+    } else {
+        // Should be in XPCOM shutdown.
+        MOZ_ASSERT(NS_IsMainThread(),
+                   "Main thread is not available for dispatch.");
+        MainThreadRelease();
+    }
+}
+
+void HRTFDatabaseLoader::MainThreadRelease()
+{
+    MOZ_ASSERT(NS_IsMainThread());
+    int count = --m_refCnt;
+    MOZ_ASSERT(count >= 0, "extra release");
+    NS_LOG_RELEASE(this, count, "HRTFDatabaseLoader");
+    if (count == 0) {
+        // It is safe to delete here as the first reference can only be added
+        // on this (main) thread.
+        delete this;
+    }
+}
+
 // Asynchronously load the database in this thread.
 static void databaseLoaderEntry(void* threadData)
 {
     PR_SetCurrentThreadName("HRTFDatabaseLdr");
 
     HRTFDatabaseLoader* loader = reinterpret_cast<HRTFDatabaseLoader*>(threadData);
     MOZ_ASSERT(loader);
     loader->load();
--- a/content/media/webaudio/blink/HRTFDatabaseLoader.h
+++ b/content/media/webaudio/blink/HRTFDatabaseLoader.h
@@ -34,27 +34,52 @@
 #include "mozilla/RefPtr.h"
 #include "nsIThread.h"
 #include "mozilla/Mutex.h"
 
 namespace WebCore {
 
 // HRTFDatabaseLoader will asynchronously load the default HRTFDatabase in a new thread.
 
-class HRTFDatabaseLoader : public mozilla::RefCounted<HRTFDatabaseLoader> {
+class HRTFDatabaseLoader {
 public:
     // Lazily creates a HRTFDatabaseLoader (if not already created) for the given sample-rate
     // and starts loading asynchronously (when created the first time).
     // Returns the HRTFDatabaseLoader.
     // Must be called from the main thread.
     static mozilla::TemporaryRef<HRTFDatabaseLoader> createAndLoadAsynchronouslyIfNecessary(float sampleRate);
 
-    // Both constructor and destructor must be called from the main thread.
-    ~HRTFDatabaseLoader();
-    
+    // AddRef and Release may be called from any thread.
+    void AddRef()
+    {
+#if defined(DEBUG) || defined(NS_BUILD_REFCNT_LOGGING)
+        int count =
+#endif
+          ++m_refCnt;
+        MOZ_ASSERT(count > 0, "invalid ref count");
+        NS_LOG_ADDREF(this, count, "HRTFDatabaseLoader", sizeof(*this));
+    }
+
+    void Release()
+    {
+        // The last reference can't be removed on a non-main thread because
+        // the object can be accessed on the main thread from the hash
+        // table via createAndLoadAsynchronouslyIfNecessary().
+        int count = m_refCnt;
+        MOZ_ASSERT(count > 0, "extra release");
+        // Optimization attempt to possibly skip proxying the release to the
+        // main thread.
+        if (count != 1 && m_refCnt.compareExchange(count, count - 1)) {
+            NS_LOG_RELEASE(this, count - 1, "HRTFDatabaseLoader");
+            return;
+        }
+
+        ProxyRelease();
+    }
+
     // Returns true once the default database has been completely loaded.
     bool isLoaded() const;
 
     // waitForLoaderThreadCompletion() may be called more than once,
     // on any thread except m_databaseLoaderThread.
     void waitForLoaderThreadCompletion();
     
     HRTFDatabase* database() { return m_hrtfDatabase.get(); }
@@ -62,17 +87,22 @@ public:
     float databaseSampleRate() const { return m_databaseSampleRate; }
     
     // Called in asynchronous loading thread.
     void load();
 
 private:
     // Both constructor and destructor must be called from the main thread.
     explicit HRTFDatabaseLoader(float sampleRate);
+    ~HRTFDatabaseLoader();
     
+    void ProxyRelease(); // any thread
+    void MainThreadRelease(); // main thread only
+    class ProxyReleaseEvent;
+
     // If it hasn't already been loaded, creates a new thread and initiates asynchronous loading of the default database.
     // This must be called from the main thread.
     void loadAsynchronously();
 
     // Map from sample-rate to loader.
     class LoaderByRateEntry : public nsFloatHashKey {
     public:
         LoaderByRateEntry(KeyTypePointer aKey)
@@ -80,16 +110,18 @@ private:
             , mLoader() // so PutEntry() will zero-initialize
         {
         }
         HRTFDatabaseLoader* mLoader;
     };
     // Keeps track of loaders on a per-sample-rate basis.
     static nsTHashtable<LoaderByRateEntry> *s_loaderMap; // singleton
 
+    mozilla::Atomic<int> m_refCnt;
+
     nsAutoRef<HRTFDatabase> m_hrtfDatabase;
 
     // Holding a m_threadLock is required when accessing m_databaseLoaderThread.
     mozilla::Mutex m_threadLock;
     PRThread* m_databaseLoaderThread;
 
     float m_databaseSampleRate;
 };