Line data Source code
1 : /*
2 : * Copyright (C) 2010 Google Inc. All rights reserved.
3 : *
4 : * Redistribution and use in source and binary forms, with or without
5 : * modification, are permitted provided that the following conditions
6 : * are met:
7 : *
8 : * 1. Redistributions of source code must retain the above copyright
9 : * notice, this list of conditions and the following disclaimer.
10 : * 2. Redistributions in binary form must reproduce the above copyright
11 : * notice, this list of conditions and the following disclaimer in the
12 : * documentation and/or other materials provided with the distribution.
13 : * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 : * its contributors may be used to endorse or promote products derived
15 : * from this software without specific prior written permission.
16 : *
17 : * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 : * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 : * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 : * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 : * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 : * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 : * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 : * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 : * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 : */
28 :
29 : #include "HRTFDatabaseLoader.h"
30 : #include "HRTFDatabase.h"
31 : #include "GeckoProfiler.h"
32 : #include "nsThreadUtils.h"
33 :
34 : using namespace mozilla;
35 :
36 : namespace WebCore {
37 :
38 : // Singleton
39 : nsTHashtable<HRTFDatabaseLoader::LoaderByRateEntry>*
40 : HRTFDatabaseLoader::s_loaderMap = nullptr;
41 :
42 0 : size_t HRTFDatabaseLoader::sizeOfLoaders(mozilla::MallocSizeOf aMallocSizeOf)
43 : {
44 0 : return s_loaderMap ? s_loaderMap->SizeOfIncludingThis(aMallocSizeOf) : 0;
45 : }
46 :
47 0 : already_AddRefed<HRTFDatabaseLoader> HRTFDatabaseLoader::createAndLoadAsynchronouslyIfNecessary(float sampleRate)
48 : {
49 0 : MOZ_ASSERT(NS_IsMainThread());
50 :
51 0 : RefPtr<HRTFDatabaseLoader> loader;
52 :
53 0 : if (!s_loaderMap) {
54 0 : s_loaderMap = new nsTHashtable<LoaderByRateEntry>();
55 : }
56 :
57 0 : LoaderByRateEntry* entry = s_loaderMap->PutEntry(sampleRate);
58 0 : loader = entry->mLoader;
59 0 : if (loader) { // existing entry
60 0 : MOZ_ASSERT(sampleRate == loader->databaseSampleRate());
61 0 : return loader.forget();
62 : }
63 :
64 0 : loader = new HRTFDatabaseLoader(sampleRate);
65 0 : entry->mLoader = loader;
66 :
67 0 : loader->loadAsynchronously();
68 :
69 0 : return loader.forget();
70 : }
71 :
72 0 : HRTFDatabaseLoader::HRTFDatabaseLoader(float sampleRate)
73 : : m_refCnt(0)
74 : , m_threadLock("HRTFDatabaseLoader")
75 : , m_databaseLoaderThread(nullptr)
76 0 : , m_databaseSampleRate(sampleRate)
77 : {
78 0 : MOZ_ASSERT(NS_IsMainThread());
79 0 : }
80 :
81 0 : HRTFDatabaseLoader::~HRTFDatabaseLoader()
82 : {
83 0 : MOZ_ASSERT(NS_IsMainThread());
84 :
85 0 : waitForLoaderThreadCompletion();
86 0 : m_hrtfDatabase.reset();
87 :
88 0 : if (s_loaderMap) {
89 : // Remove ourself from the map.
90 0 : s_loaderMap->RemoveEntry(m_databaseSampleRate);
91 0 : if (s_loaderMap->Count() == 0) {
92 0 : delete s_loaderMap;
93 0 : s_loaderMap = nullptr;
94 : }
95 : }
96 0 : }
97 :
98 0 : size_t HRTFDatabaseLoader::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
99 : {
100 0 : size_t amount = aMallocSizeOf(this);
101 :
102 : // NB: Need to make sure we're not competing with the loader thread.
103 0 : const_cast<HRTFDatabaseLoader*>(this)->waitForLoaderThreadCompletion();
104 :
105 0 : if (m_hrtfDatabase) {
106 0 : amount += m_hrtfDatabase->sizeOfIncludingThis(aMallocSizeOf);
107 : }
108 :
109 0 : return amount;
110 : }
111 :
112 0 : class HRTFDatabaseLoader::ProxyReleaseEvent final : public Runnable {
113 : public:
114 0 : explicit ProxyReleaseEvent(HRTFDatabaseLoader* loader)
115 0 : : mozilla::Runnable("WebCore::HRTFDatabaseLoader::ProxyReleaseEvent")
116 0 : , mLoader(loader)
117 : {
118 0 : }
119 0 : NS_IMETHOD Run() override
120 : {
121 0 : mLoader->MainThreadRelease();
122 0 : return NS_OK;
123 : }
124 : private:
125 : HRTFDatabaseLoader* mLoader;
126 : };
127 :
128 0 : void HRTFDatabaseLoader::ProxyRelease()
129 : {
130 0 : nsCOMPtr<nsIEventTarget> mainTarget = GetMainThreadEventTarget();
131 0 : if (MOZ_LIKELY(mainTarget)) {
132 0 : RefPtr<ProxyReleaseEvent> event = new ProxyReleaseEvent(this);
133 : DebugOnly<nsresult> rv =
134 0 : mainTarget->Dispatch(event, NS_DISPATCH_NORMAL);
135 0 : MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed to dispatch release event");
136 : } else {
137 : // Should be in XPCOM shutdown.
138 0 : MOZ_ASSERT(NS_IsMainThread(),
139 : "Main thread is not available for dispatch.");
140 0 : MainThreadRelease();
141 : }
142 0 : }
143 :
144 0 : void HRTFDatabaseLoader::MainThreadRelease()
145 : {
146 0 : MOZ_ASSERT(NS_IsMainThread());
147 0 : int count = --m_refCnt;
148 0 : MOZ_ASSERT(count >= 0, "extra release");
149 0 : NS_LOG_RELEASE(this, count, "HRTFDatabaseLoader");
150 0 : if (count == 0) {
151 : // It is safe to delete here as the first reference can only be added
152 : // on this (main) thread.
153 0 : delete this;
154 : }
155 0 : }
156 :
157 : // Asynchronously load the database in this thread.
158 0 : static void databaseLoaderEntry(void* threadData)
159 : {
160 0 : AutoProfilerRegisterThread registerThread("HRTFDatabaseLdr");
161 0 : NS_SetCurrentThreadName("HRTFDatabaseLdr");
162 :
163 0 : HRTFDatabaseLoader* loader = reinterpret_cast<HRTFDatabaseLoader*>(threadData);
164 0 : MOZ_ASSERT(loader);
165 0 : loader->load();
166 0 : }
167 :
168 0 : void HRTFDatabaseLoader::load()
169 : {
170 0 : MOZ_ASSERT(!NS_IsMainThread());
171 0 : MOZ_ASSERT(!m_hrtfDatabase.get(), "Called twice");
172 : // Load the default HRTF database.
173 0 : m_hrtfDatabase = HRTFDatabase::create(m_databaseSampleRate);
174 : // Notifies the main thread of completion. See loadAsynchronously().
175 0 : Release();
176 0 : }
177 :
178 0 : void HRTFDatabaseLoader::loadAsynchronously()
179 : {
180 0 : MOZ_ASSERT(NS_IsMainThread());
181 0 : MOZ_ASSERT(m_refCnt, "Must not be called before a reference is added");
182 :
183 : // Add a reference so that the destructor won't run and wait for the
184 : // loader thread, until load() has completed.
185 0 : AddRef();
186 :
187 0 : MutexAutoLock locker(m_threadLock);
188 :
189 0 : MOZ_ASSERT(!m_hrtfDatabase.get() && !m_databaseLoaderThread,
190 : "Called twice");
191 : // Start the asynchronous database loading process.
192 0 : m_databaseLoaderThread =
193 0 : PR_CreateThread(PR_USER_THREAD, databaseLoaderEntry, this,
194 : PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
195 : PR_JOINABLE_THREAD, 0);
196 0 : }
197 :
198 0 : bool HRTFDatabaseLoader::isLoaded() const
199 : {
200 0 : return m_hrtfDatabase.get();
201 : }
202 :
203 0 : void HRTFDatabaseLoader::waitForLoaderThreadCompletion()
204 : {
205 0 : MutexAutoLock locker(m_threadLock);
206 :
207 : // waitForThreadCompletion() should not be called twice for the same thread.
208 0 : if (m_databaseLoaderThread) {
209 0 : DebugOnly<PRStatus> status = PR_JoinThread(m_databaseLoaderThread);
210 0 : MOZ_ASSERT(status == PR_SUCCESS, "PR_JoinThread failed");
211 : }
212 0 : m_databaseLoaderThread = nullptr;
213 0 : }
214 :
215 0 : void HRTFDatabaseLoader::shutdown()
216 : {
217 0 : MOZ_ASSERT(NS_IsMainThread());
218 0 : if (s_loaderMap) {
219 : // Set s_loaderMap to nullptr so that the hashtable is not modified on
220 : // reference release during enumeration.
221 0 : nsTHashtable<LoaderByRateEntry>* loaderMap = s_loaderMap;
222 0 : s_loaderMap = nullptr;
223 0 : for (auto iter = loaderMap->Iter(); !iter.Done(); iter.Next()) {
224 0 : iter.Get()->mLoader->waitForLoaderThreadCompletion();
225 : }
226 0 : delete loaderMap;
227 : }
228 0 : }
229 :
230 : } // namespace WebCore
|