b=865241 make HRTFDatabaseLoader ref-counting thread-safe r=ehsan
authorKarl Tomlinson <karlt+@karlt.net>
Fri, 09 Aug 2013 09:59:56 +1200
changeset 142378 c19e2f326d156dfa40341a34c3454004bff0d006
parent 142377 316df2d96b2420f285f9698cd2bacc819d4c9e5a
child 142379 7ab5c4babe56d9c00954d9f503b132afea499d26
push id25093
push usercbook@mozilla.com
push dateTue, 13 Aug 2013 10:34:53 +0000
treeherdermozilla-central@a0e613b4f4d8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersehsan
bugs865241
milestone26.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
b=865241 make HRTFDatabaseLoader ref-counting thread-safe r=ehsan
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;
 };