Bug Summary

File:root/firefox-clang/obj-x86_64-pc-linux-gnu/dom/media/webaudio/blink/./../../../../../dom/media/webaudio/blink/ReverbConvolver.cpp
Warning:line 233, column 13
Although the value stored to 'readIndex' is used in the enclosing expression, the value is never actually read from 'readIndex'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -O2 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name Unified_cpp_webaudio_blink0.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/dom/media/webaudio/blink -fcoverage-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/dom/media/webaudio/blink -resource-dir /usr/lib/llvm-23/lib/clang/23 -include /root/firefox-clang/config/gcc_hidden.h -include /root/firefox-clang/obj-x86_64-pc-linux-gnu/mozilla-config.h -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/stl_wrappers -D _GLIBCXX_ASSERTIONS=1 -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D DEBUG=1 -D USE_SSE2 -D MOZ_HAS_MOZGLUE -D MOZILLA_INTERNAL_API -D IMPL_LIBXUL -D MOZ_SUPPORT_LEAKCHECKING -D STATIC_EXPORTABLE_JS_API -I /root/firefox-clang/dom/media/webaudio/blink -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dom/media/webaudio/blink -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/ipc/ipdl/_ipdlheaders -I /root/firefox-clang/ipc/chromium/src -I /root/firefox-clang/third_party/abseil-cpp -I /root/firefox-clang/dom/media/webaudio -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/x86_64-linux-gnu/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16/backward -internal-isystem /usr/lib/llvm-23/lib/clang/23/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-error=pessimizing-move -Wno-error=large-by-value-copy=128 -Wno-error=implicit-int-float-conversion -Wno-error=thread-safety-analysis -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -Wno-character-conversion -std=gnu++20 -fdeprecated-macro -ferror-limit 19 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fno-implicit-modules -fskip-odr-check-in-gmf -fno-sized-deallocation -fno-aligned-allocation -fdiagnostics-absolute-paths -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -mllvm -dwarf-linkage-names=Abstract -faddrsig -fdwarf2-cfi-asm -o /tmp/scan-build-2026-11-12-001017-1011516-1 -x c++ Unified_cpp_webaudio_blink0.cpp
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 "ReverbConvolver.h"
30
31#include <memory>
32
33#include "ReverbConvolverStage.h"
34
35using namespace mozilla;
36
37namespace WebCore {
38
39const int InputBufferSize = 8 * 16384;
40
41// We only process the leading portion of the impulse response in the real-time
42// thread. We don't exceed this length. It turns out then, that the background
43// thread has about 278msec of scheduling slop. Empirically, this has been found
44// to be a good compromise between giving enough time for scheduling slop, while
45// still minimizing the amount of processing done in the primary (high-priority)
46// thread. This was found to be a good value on Mac OS X, and may work well on
47// other platforms as well, assuming the very rough scheduling latencies are
48// similar on these time-scales. Of course, this code may need to be tuned for
49// individual platforms if this assumption is found to be incorrect.
50const size_t RealtimeFrameLimit = 8192 + 4096 // ~278msec @ 44.1KHz
51 - WEBAUDIO_BLOCK_SIZE;
52// First stage will have size MinFFTSize - successive stages will double in
53// size each time until we hit the maximum size.
54const size_t MinFFTSize = 256;
55// If we are using background threads then don't exceed this FFT size for the
56// stages which run in the real-time thread. This avoids having only one or
57// two large stages (size 16384 or so) at the end which take a lot of time
58// every several processing slices. This way we amortize the cost over more
59// processing slices.
60const size_t MaxRealtimeFFTSize = 4096;
61
62ReverbConvolver::ReverbConvolver(const float* impulseResponseData,
63 size_t impulseResponseLength,
64 size_t maxFFTSize, size_t convolverRenderPhase,
65 bool useBackgroundThreads,
66 bool* aAllocationFailure)
67 : m_impulseResponseLength(impulseResponseLength),
68 m_inputBuffer(InputBufferSize),
69 m_backgroundThread("ConvolverWorker"),
70 m_backgroundThreadMonitor("ConvolverMonitor"),
71 m_useBackgroundThreads(useBackgroundThreads),
72 m_wantsToExit(false),
73 m_moreInputBuffered(false) {
74 *aAllocationFailure = !m_accumulationBuffer.allocate(impulseResponseLength +
75 WEBAUDIO_BLOCK_SIZE);
76 if (*aAllocationFailure) {
77 return;
78 }
79 // For the moment, a good way to know if we have real-time constraint is to
80 // check if we're using background threads. Otherwise, assume we're being run
81 // from a command-line tool.
82 bool hasRealtimeConstraint = useBackgroundThreads;
83
84 const float* response = impulseResponseData;
85 size_t totalResponseLength = impulseResponseLength;
86
87 // The total latency is zero because the first FFT stage is small enough
88 // to return output in the first block.
89 size_t reverbTotalLatency = 0;
90
91 size_t stageOffset = 0;
92 size_t stagePhase = 0;
93 size_t fftSize = MinFFTSize;
94 while (stageOffset < totalResponseLength) {
95 size_t stageSize = fftSize / 2;
96
97 // For the last stage, it's possible that stageOffset is such that we're
98 // straddling the end of the impulse response buffer (if we use stageSize),
99 // so reduce the last stage's length...
100 if (stageSize + stageOffset > totalResponseLength) {
101 stageSize = totalResponseLength - stageOffset;
102 // Use smallest FFT that is large enough to cover the last stage.
103 fftSize = MinFFTSize;
104 while (stageSize * 2 > fftSize) {
105 fftSize *= 2;
106 }
107 }
108
109 // This "staggers" the time when each FFT happens so they don't all happen
110 // at the same time
111 int renderPhase = convolverRenderPhase + stagePhase;
112
113 auto stage = std::make_unique<ReverbConvolverStage>(
114 response, totalResponseLength, reverbTotalLatency, stageOffset,
115 stageSize, fftSize, renderPhase, &m_accumulationBuffer);
116
117 bool isBackgroundStage = false;
118
119 if (this->useBackgroundThreads() && stageOffset > RealtimeFrameLimit) {
120 m_backgroundStages.AppendElement(std::move(stage));
121 isBackgroundStage = true;
122 } else
123 m_stages.AppendElement(std::move(stage));
124
125 // Figure out next FFT size
126 fftSize *= 2;
127
128 stageOffset += stageSize;
129
130 if (hasRealtimeConstraint && !isBackgroundStage &&
131 fftSize > MaxRealtimeFFTSize) {
132 fftSize = MaxRealtimeFFTSize;
133 // Custom phase positions for all but the first of the realtime
134 // stages of largest size. These spread out the work of the
135 // larger realtime stages. None of the FFTs of size 1024, 2048 or
136 // 4096 are performed when processing the same block. The first
137 // MaxRealtimeFFTSize = 4096 stage, at the end of the doubling,
138 // performs its FFT at block 7. The FFTs of size 2048 are
139 // performed in blocks 3 + 8 * n and size 1024 at 1 + 4 * n.
140 const uint32_t phaseLookup[] = {14, 0, 10, 4};
141 stagePhase = WEBAUDIO_BLOCK_SIZE *
142 phaseLookup[m_stages.Length() % std::size(phaseLookup)];
143 } else if (fftSize > maxFFTSize) {
144 fftSize = maxFFTSize;
145 // A prime offset spreads out FFTs in a way that all
146 // available phase positions will be used if there are sufficient
147 // stages.
148 stagePhase += 5 * WEBAUDIO_BLOCK_SIZE;
149 } else if (stageSize > WEBAUDIO_BLOCK_SIZE) {
150 // As the stages are doubling in size, the next FFT will occur
151 // mid-way between FFTs for this stage.
152 stagePhase = stageSize - WEBAUDIO_BLOCK_SIZE;
153 }
154 }
155
156 // Start up background thread
157 // FIXME: would be better to up the thread priority here. It doesn't need to
158 // be real-time, but higher than the default...
159 if (this->useBackgroundThreads() && m_backgroundStages.Length() > 0) {
160 if (!m_backgroundThread.Start()) {
161 NS_WARNING("Cannot start convolver thread.")NS_DebugBreak(NS_DEBUG_WARNING, "Cannot start convolver thread."
, nullptr, "./../../../../../dom/media/webaudio/blink/ReverbConvolver.cpp"
, 161)
;
162 return;
163 }
164 m_backgroundThread.message_loop()->PostTask(NewNonOwningRunnableMethod(
165 "WebCore::ReverbConvolver::backgroundThreadEntry", this,
166 &ReverbConvolver::backgroundThreadEntry));
167 }
168}
169
170ReverbConvolver::~ReverbConvolver() {
171 // Wait for background thread to stop
172 if (useBackgroundThreads() && m_backgroundThread.IsRunning()) {
173 m_wantsToExit = true;
174
175 // Wake up thread so it can return
176 {
177 MonitorAutoLock locker(m_backgroundThreadMonitor);
178 m_moreInputBuffered = true;
179 m_backgroundThreadMonitor.Notify();
180 }
181
182 m_backgroundThread.Stop();
183 }
184}
185
186size_t ReverbConvolver::sizeOfIncludingThis(
187 mozilla::MallocSizeOf aMallocSizeOf) const {
188 size_t amount = aMallocSizeOf(this);
189 amount += m_stages.ShallowSizeOfExcludingThis(aMallocSizeOf);
190 for (size_t i = 0; i < m_stages.Length(); i++) {
191 if (m_stages[i]) {
192 amount += m_stages[i]->sizeOfIncludingThis(aMallocSizeOf);
193 }
194 }
195
196 amount += m_backgroundStages.ShallowSizeOfExcludingThis(aMallocSizeOf);
197 for (size_t i = 0; i < m_backgroundStages.Length(); i++) {
198 if (m_backgroundStages[i]) {
199 amount += m_backgroundStages[i]->sizeOfIncludingThis(aMallocSizeOf);
200 }
201 }
202
203 // NB: The buffer sizes are static, so even though they might be accessed
204 // in another thread it's safe to measure them.
205 amount += m_accumulationBuffer.sizeOfExcludingThis(aMallocSizeOf);
206 amount += m_inputBuffer.sizeOfExcludingThis(aMallocSizeOf);
207
208 // Possible future measurements:
209 // - m_backgroundThread
210 // - m_backgroundThreadMonitor
211 return amount;
212}
213
214void ReverbConvolver::backgroundThreadEntry() {
215 while (!m_wantsToExit) {
216 // Wait for realtime thread to give us more input
217 m_moreInputBuffered = false;
218 {
219 MonitorAutoLock locker(m_backgroundThreadMonitor);
220 while (!m_moreInputBuffered && !m_wantsToExit)
221 m_backgroundThreadMonitor.Wait();
222 }
223
224 // Process all of the stages until their read indices reach the input
225 // buffer's write index
226 int writeIndex = m_inputBuffer.writeIndex();
227
228 // Even though it doesn't seem like every stage needs to maintain its own
229 // version of readIndex we do this in case we want to run in more than one
230 // background thread.
231 int readIndex;
232
233 while ((readIndex = m_backgroundStages[0]->inputReadIndex()) !=
Although the value stored to 'readIndex' is used in the enclosing expression, the value is never actually read from 'readIndex'
234 writeIndex) { // FIXME: do better to detect buffer overrun...
235 // Accumulate contributions from each stage
236 for (size_t i = 0; i < m_backgroundStages.Length(); ++i)
237 m_backgroundStages[i]->processInBackground(this);
238 }
239 }
240}
241
242void ReverbConvolver::process(const float* sourceChannelData,
243 float* destinationChannelData) {
244 const float* source = sourceChannelData;
245 float* destination = destinationChannelData;
246 bool isDataSafe = source && destination;
247 MOZ_ASSERT(isDataSafe)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(isDataSafe)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(isDataSafe))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("isDataSafe", "./../../../../../dom/media/webaudio/blink/ReverbConvolver.cpp"
, 247); AnnotateMozCrashReason("MOZ_ASSERT" "(" "isDataSafe" ")"
); do { MOZ_CrashSequence(__null, 247); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
248 if (!isDataSafe) return;
249
250 // Feed input buffer (read by all threads)
251 m_inputBuffer.write(source, WEBAUDIO_BLOCK_SIZE);
252
253 // Accumulate contributions from each stage
254 for (size_t i = 0; i < m_stages.Length(); ++i) m_stages[i]->process(source);
255
256 // Finally read from accumulation buffer
257 m_accumulationBuffer.readAndClear(destination, WEBAUDIO_BLOCK_SIZE);
258
259 // Now that we've buffered more input, wake up our background thread.
260
261 // Not using a MonitorAutoLock looks strange, but we use a TryLock() instead
262 // because this is run on the real-time thread where it is a disaster for the
263 // lock to be contended (causes audio glitching). It's OK if we fail to
264 // signal from time to time, since we'll get to it the next time we're called.
265 // We're called repeatedly and frequently (around every 3ms). The background
266 // thread is processing well into the future and has a considerable amount of
267 // leeway here...
268 if (m_backgroundThreadMonitor.TryLock()) {
269 m_moreInputBuffered = true;
270 m_backgroundThreadMonitor.Notify();
271 m_backgroundThreadMonitor.Unlock();
272 }
273}
274
275} // namespace WebCore