/src/mozilla-central/dom/media/webaudio/blink/HRTFDatabaseLoader.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | size_t HRTFDatabaseLoader::sizeOfLoaders(mozilla::MallocSizeOf aMallocSizeOf) |
43 | 0 | { |
44 | 0 | return s_loaderMap ? s_loaderMap->SizeOfIncludingThis(aMallocSizeOf) : 0; |
45 | 0 | } |
46 | | |
47 | | already_AddRefed<HRTFDatabaseLoader> HRTFDatabaseLoader::createAndLoadAsynchronouslyIfNecessary(float sampleRate) |
48 | 0 | { |
49 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
50 | 0 |
|
51 | 0 | RefPtr<HRTFDatabaseLoader> loader; |
52 | 0 |
|
53 | 0 | if (!s_loaderMap) { |
54 | 0 | s_loaderMap = new nsTHashtable<LoaderByRateEntry>(); |
55 | 0 | } |
56 | 0 |
|
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 | 0 | } |
63 | 0 |
|
64 | 0 | loader = new HRTFDatabaseLoader(sampleRate); |
65 | 0 | entry->mLoader = loader; |
66 | 0 |
|
67 | 0 | loader->loadAsynchronously(); |
68 | 0 |
|
69 | 0 | return loader.forget(); |
70 | 0 | } |
71 | | |
72 | | HRTFDatabaseLoader::HRTFDatabaseLoader(float sampleRate) |
73 | | : m_refCnt(0) |
74 | | , m_threadLock("HRTFDatabaseLoader") |
75 | | , m_databaseLoaderThread(nullptr) |
76 | | , m_databaseSampleRate(sampleRate) |
77 | 0 | { |
78 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
79 | 0 | } |
80 | | |
81 | | HRTFDatabaseLoader::~HRTFDatabaseLoader() |
82 | 0 | { |
83 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
84 | 0 |
|
85 | 0 | waitForLoaderThreadCompletion(); |
86 | 0 | m_hrtfDatabase.reset(); |
87 | 0 |
|
88 | 0 | if (s_loaderMap) { |
89 | 0 | // 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 | 0 | } |
95 | 0 | } |
96 | 0 | } |
97 | | |
98 | | size_t HRTFDatabaseLoader::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const |
99 | 0 | { |
100 | 0 | size_t amount = aMallocSizeOf(this); |
101 | 0 |
|
102 | 0 | // NB: Need to make sure we're not competing with the loader thread. |
103 | 0 | const_cast<HRTFDatabaseLoader*>(this)->waitForLoaderThreadCompletion(); |
104 | 0 |
|
105 | 0 | if (m_hrtfDatabase) { |
106 | 0 | amount += m_hrtfDatabase->sizeOfIncludingThis(aMallocSizeOf); |
107 | 0 | } |
108 | 0 |
|
109 | 0 | return amount; |
110 | 0 | } |
111 | | |
112 | | class HRTFDatabaseLoader::ProxyReleaseEvent final : public Runnable { |
113 | | public: |
114 | | explicit ProxyReleaseEvent(HRTFDatabaseLoader* loader) |
115 | | : mozilla::Runnable("WebCore::HRTFDatabaseLoader::ProxyReleaseEvent") |
116 | | , mLoader(loader) |
117 | 0 | { |
118 | 0 | } |
119 | | NS_IMETHOD Run() override |
120 | 0 | { |
121 | 0 | mLoader->MainThreadRelease(); |
122 | 0 | return NS_OK; |
123 | 0 | } |
124 | | private: |
125 | | // Ownership transferred by ProxyRelease |
126 | | HRTFDatabaseLoader* MOZ_OWNING_REF mLoader; |
127 | | }; |
128 | | |
129 | | void HRTFDatabaseLoader::ProxyRelease() |
130 | 0 | { |
131 | 0 | nsCOMPtr<nsIEventTarget> mainTarget = GetMainThreadEventTarget(); |
132 | 0 | if (MOZ_LIKELY(mainTarget)) { |
133 | 0 | RefPtr<ProxyReleaseEvent> event = new ProxyReleaseEvent(this); |
134 | 0 | DebugOnly<nsresult> rv = |
135 | 0 | mainTarget->Dispatch(event, NS_DISPATCH_NORMAL); |
136 | 0 | MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed to dispatch release event"); |
137 | 0 | } else { |
138 | 0 | // Should be in XPCOM shutdown. |
139 | 0 | MOZ_ASSERT(NS_IsMainThread(), |
140 | 0 | "Main thread is not available for dispatch."); |
141 | 0 | MainThreadRelease(); |
142 | 0 | } |
143 | 0 | } |
144 | | |
145 | | void HRTFDatabaseLoader::MainThreadRelease() |
146 | 0 | { |
147 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
148 | 0 | int count = --m_refCnt; |
149 | 0 | MOZ_ASSERT(count >= 0, "extra release"); |
150 | 0 | NS_LOG_RELEASE(this, count, "HRTFDatabaseLoader"); |
151 | 0 | if (count == 0) { |
152 | 0 | // It is safe to delete here as the first reference can only be added |
153 | 0 | // on this (main) thread. |
154 | 0 | delete this; |
155 | 0 | } |
156 | 0 | } |
157 | | |
158 | | // Asynchronously load the database in this thread. |
159 | | static void databaseLoaderEntry(void* threadData) |
160 | 0 | { |
161 | 0 | AUTO_PROFILER_REGISTER_THREAD("HRTFDatabaseLdr"); |
162 | 0 | NS_SetCurrentThreadName("HRTFDatabaseLdr"); |
163 | 0 |
|
164 | 0 | HRTFDatabaseLoader* loader = reinterpret_cast<HRTFDatabaseLoader*>(threadData); |
165 | 0 | MOZ_ASSERT(loader); |
166 | 0 | loader->load(); |
167 | 0 | } |
168 | | |
169 | | void HRTFDatabaseLoader::load() |
170 | 0 | { |
171 | 0 | MOZ_ASSERT(!NS_IsMainThread()); |
172 | 0 | MOZ_ASSERT(!m_hrtfDatabase.get(), "Called twice"); |
173 | 0 | // Load the default HRTF database. |
174 | 0 | m_hrtfDatabase = HRTFDatabase::create(m_databaseSampleRate); |
175 | 0 | // Notifies the main thread of completion. See loadAsynchronously(). |
176 | 0 | Release(); |
177 | 0 | } |
178 | | |
179 | | void HRTFDatabaseLoader::loadAsynchronously() |
180 | 0 | { |
181 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
182 | 0 | MOZ_ASSERT(m_refCnt, "Must not be called before a reference is added"); |
183 | 0 |
|
184 | 0 | // Add a reference so that the destructor won't run and wait for the |
185 | 0 | // loader thread, until load() has completed. |
186 | 0 | AddRef(); |
187 | 0 |
|
188 | 0 | MutexAutoLock locker(m_threadLock); |
189 | 0 |
|
190 | 0 | MOZ_ASSERT(!m_hrtfDatabase.get() && !m_databaseLoaderThread, |
191 | 0 | "Called twice"); |
192 | 0 | // Start the asynchronous database loading process. |
193 | 0 | m_databaseLoaderThread = |
194 | 0 | PR_CreateThread(PR_USER_THREAD, databaseLoaderEntry, this, |
195 | 0 | PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, |
196 | 0 | PR_JOINABLE_THREAD, 0); |
197 | 0 | } |
198 | | |
199 | | bool HRTFDatabaseLoader::isLoaded() const |
200 | 0 | { |
201 | 0 | return m_hrtfDatabase.get(); |
202 | 0 | } |
203 | | |
204 | | void HRTFDatabaseLoader::waitForLoaderThreadCompletion() |
205 | 0 | { |
206 | 0 | MutexAutoLock locker(m_threadLock); |
207 | 0 |
|
208 | 0 | // waitForThreadCompletion() should not be called twice for the same thread. |
209 | 0 | if (m_databaseLoaderThread) { |
210 | 0 | DebugOnly<PRStatus> status = PR_JoinThread(m_databaseLoaderThread); |
211 | 0 | MOZ_ASSERT(status == PR_SUCCESS, "PR_JoinThread failed"); |
212 | 0 | } |
213 | 0 | m_databaseLoaderThread = nullptr; |
214 | 0 | } |
215 | | |
216 | | void HRTFDatabaseLoader::shutdown() |
217 | 0 | { |
218 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
219 | 0 | if (s_loaderMap) { |
220 | 0 | // Set s_loaderMap to nullptr so that the hashtable is not modified on |
221 | 0 | // reference release during enumeration. |
222 | 0 | nsTHashtable<LoaderByRateEntry>* loaderMap = s_loaderMap; |
223 | 0 | s_loaderMap = nullptr; |
224 | 0 | for (auto iter = loaderMap->Iter(); !iter.Done(); iter.Next()) { |
225 | 0 | iter.Get()->mLoader->waitForLoaderThreadCompletion(); |
226 | 0 | } |
227 | 0 | delete loaderMap; |
228 | 0 | } |
229 | 0 | } |
230 | | |
231 | | } // namespace WebCore |