michael@0: /* michael@0: * Copyright (C) 2010 Google Inc. All rights reserved. michael@0: * michael@0: * Redistribution and use in source and binary forms, with or without michael@0: * modification, are permitted provided that the following conditions michael@0: * are met: michael@0: * michael@0: * 1. Redistributions of source code must retain the above copyright michael@0: * notice, this list of conditions and the following disclaimer. michael@0: * 2. Redistributions in binary form must reproduce the above copyright michael@0: * notice, this list of conditions and the following disclaimer in the michael@0: * documentation and/or other materials provided with the distribution. michael@0: * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of michael@0: * its contributors may be used to endorse or promote products derived michael@0: * from this software without specific prior written permission. michael@0: * michael@0: * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY michael@0: * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED michael@0: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE michael@0: * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY michael@0: * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES michael@0: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; michael@0: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND michael@0: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT michael@0: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF michael@0: * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. michael@0: */ michael@0: michael@0: #include "HRTFDatabaseLoader.h" michael@0: #include "HRTFDatabase.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: namespace WebCore { michael@0: michael@0: // Singleton michael@0: nsTHashtable* michael@0: HRTFDatabaseLoader::s_loaderMap = nullptr; michael@0: michael@0: TemporaryRef HRTFDatabaseLoader::createAndLoadAsynchronouslyIfNecessary(float sampleRate) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: RefPtr loader; michael@0: michael@0: if (!s_loaderMap) { michael@0: s_loaderMap = new nsTHashtable(); michael@0: } michael@0: michael@0: LoaderByRateEntry* entry = s_loaderMap->PutEntry(sampleRate); michael@0: loader = entry->mLoader; michael@0: if (loader) { // existing entry michael@0: MOZ_ASSERT(sampleRate == loader->databaseSampleRate()); michael@0: return loader; michael@0: } michael@0: michael@0: loader = new HRTFDatabaseLoader(sampleRate); michael@0: entry->mLoader = loader; michael@0: michael@0: loader->loadAsynchronously(); michael@0: michael@0: return loader; michael@0: } michael@0: michael@0: HRTFDatabaseLoader::HRTFDatabaseLoader(float sampleRate) michael@0: : m_refCnt(0) michael@0: , m_threadLock("HRTFDatabaseLoader") michael@0: , m_databaseLoaderThread(nullptr) michael@0: , m_databaseSampleRate(sampleRate) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: } michael@0: michael@0: HRTFDatabaseLoader::~HRTFDatabaseLoader() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: waitForLoaderThreadCompletion(); michael@0: m_hrtfDatabase.reset(); michael@0: michael@0: if (s_loaderMap) { michael@0: // Remove ourself from the map. michael@0: s_loaderMap->RemoveEntry(m_databaseSampleRate); michael@0: if (s_loaderMap->Count() == 0) { michael@0: delete s_loaderMap; michael@0: s_loaderMap = nullptr; michael@0: } michael@0: } michael@0: } michael@0: michael@0: size_t HRTFDatabaseLoader::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const michael@0: { michael@0: size_t amount = aMallocSizeOf(this); michael@0: michael@0: // NB: Need to make sure we're not competing with the loader thread. michael@0: const_cast(this)->waitForLoaderThreadCompletion(); michael@0: michael@0: if (m_hrtfDatabase) { michael@0: amount += m_hrtfDatabase->sizeOfIncludingThis(aMallocSizeOf); michael@0: } michael@0: michael@0: return amount; michael@0: } michael@0: michael@0: class HRTFDatabaseLoader::ProxyReleaseEvent MOZ_FINAL : public nsRunnable { michael@0: public: michael@0: explicit ProxyReleaseEvent(HRTFDatabaseLoader* loader) : mLoader(loader) {} michael@0: NS_IMETHOD Run() MOZ_OVERRIDE michael@0: { michael@0: mLoader->MainThreadRelease(); michael@0: return NS_OK; michael@0: } michael@0: private: michael@0: HRTFDatabaseLoader* mLoader; michael@0: }; michael@0: michael@0: void HRTFDatabaseLoader::ProxyRelease() michael@0: { michael@0: nsCOMPtr mainThread = do_GetMainThread(); michael@0: if (MOZ_LIKELY(mainThread)) { michael@0: nsRefPtr event = new ProxyReleaseEvent(this); michael@0: DebugOnly rv = michael@0: mainThread->Dispatch(event, NS_DISPATCH_NORMAL); michael@0: MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed to dispatch release event"); michael@0: } else { michael@0: // Should be in XPCOM shutdown. michael@0: MOZ_ASSERT(NS_IsMainThread(), michael@0: "Main thread is not available for dispatch."); michael@0: MainThreadRelease(); michael@0: } michael@0: } michael@0: michael@0: void HRTFDatabaseLoader::MainThreadRelease() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: int count = --m_refCnt; michael@0: MOZ_ASSERT(count >= 0, "extra release"); michael@0: NS_LOG_RELEASE(this, count, "HRTFDatabaseLoader"); michael@0: if (count == 0) { michael@0: // It is safe to delete here as the first reference can only be added michael@0: // on this (main) thread. michael@0: delete this; michael@0: } michael@0: } michael@0: michael@0: // Asynchronously load the database in this thread. michael@0: static void databaseLoaderEntry(void* threadData) michael@0: { michael@0: PR_SetCurrentThreadName("HRTFDatabaseLdr"); michael@0: michael@0: HRTFDatabaseLoader* loader = reinterpret_cast(threadData); michael@0: MOZ_ASSERT(loader); michael@0: loader->load(); michael@0: } michael@0: michael@0: void HRTFDatabaseLoader::load() michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); michael@0: MOZ_ASSERT(!m_hrtfDatabase.get(), "Called twice"); michael@0: // Load the default HRTF database. michael@0: m_hrtfDatabase = HRTFDatabase::create(m_databaseSampleRate); michael@0: // Notifies the main thread of completion. See loadAsynchronously(). michael@0: Release(); michael@0: } michael@0: michael@0: void HRTFDatabaseLoader::loadAsynchronously() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(m_refCnt, "Must not be called before a reference is added"); michael@0: michael@0: // Add a reference so that the destructor won't run and wait for the michael@0: // loader thread, until load() has completed. michael@0: AddRef(); michael@0: michael@0: MutexAutoLock locker(m_threadLock); michael@0: michael@0: MOZ_ASSERT(!m_hrtfDatabase.get() && !m_databaseLoaderThread, michael@0: "Called twice"); michael@0: // Start the asynchronous database loading process. michael@0: m_databaseLoaderThread = michael@0: PR_CreateThread(PR_USER_THREAD, databaseLoaderEntry, this, michael@0: PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, michael@0: PR_JOINABLE_THREAD, 0); michael@0: } michael@0: michael@0: bool HRTFDatabaseLoader::isLoaded() const michael@0: { michael@0: return m_hrtfDatabase.get(); michael@0: } michael@0: michael@0: void HRTFDatabaseLoader::waitForLoaderThreadCompletion() michael@0: { michael@0: MutexAutoLock locker(m_threadLock); michael@0: michael@0: // waitForThreadCompletion() should not be called twice for the same thread. michael@0: if (m_databaseLoaderThread) { michael@0: DebugOnly status = PR_JoinThread(m_databaseLoaderThread); michael@0: MOZ_ASSERT(status == PR_SUCCESS, "PR_JoinThread failed"); michael@0: } michael@0: m_databaseLoaderThread = nullptr; michael@0: } michael@0: michael@0: PLDHashOperator michael@0: HRTFDatabaseLoader::shutdownEnumFunc(LoaderByRateEntry *entry, void* unused) michael@0: { michael@0: // Ensure the loader thread's reference is removed for leak analysis. michael@0: entry->mLoader->waitForLoaderThreadCompletion(); michael@0: return PLDHashOperator::PL_DHASH_NEXT; michael@0: } michael@0: michael@0: void HRTFDatabaseLoader::shutdown() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: if (s_loaderMap) { michael@0: // Set s_loaderMap to nullptr so that the hashtable is not modified on michael@0: // reference release during enumeration. michael@0: nsTHashtable* loaderMap = s_loaderMap; michael@0: s_loaderMap = nullptr; michael@0: loaderMap->EnumerateEntries(shutdownEnumFunc, nullptr); michael@0: delete loaderMap; michael@0: } michael@0: } michael@0: } // namespace WebCore