Changeset 270127 in webkit
- Timestamp:
- Nov 20, 2020 12:48:22 PM (3 years ago)
- Location:
- trunk
- Files:
-
- 3 added
- 9 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r270126 r270127 1 2020-11-20 Chris Dumez <cdumez@apple.com> 2 3 AudioWorkletProcessor::process() may get called on non-audio worklet thread and crash 4 https://bugs.webkit.org/show_bug.cgi?id=219209 5 6 Reviewed by Geoffrey Garen. 7 8 Add layout test coverage. 9 10 * http/wpt/webaudio/the-audio-api/the-audioworklet-interface/context-already-rendering-expected.txt: Added. 11 * http/wpt/webaudio/the-audio-api/the-audioworklet-interface/context-already-rendering.html: Added. 12 * http/wpt/webaudio/the-audio-api/the-audioworklet-interface/processors/basic-processor.js: Added. 13 (BasicProcessor.prototype.process): 14 (BasicProcessor): 15 1 16 2020-11-20 Lauro Moura <lmoura@igalia.com> 2 17 -
trunk/Source/WebCore/ChangeLog
r270126 r270127 1 2020-11-20 Chris Dumez <cdumez@apple.com> 2 3 AudioWorkletProcessor::process() may get called on non-audio worklet thread and crash 4 https://bugs.webkit.org/show_bug.cgi?id=219209 5 6 Reviewed by Geoffrey Garen. 7 8 If the AudioContext is already running when the AudioWorklet becomes ready, then the 9 AudioWorkletNode::process() was getting called on the CoreAudio rendering thread instead 10 of the AudioWorklet thread. This was causing us to run the AudioWorkletProcessor's JS 11 on the wrong thread, which would cause flaky crashes in release and assertion hits in 12 debug. 13 14 To address the issue, I updated AudioWorkletNode::process() to output silence when 15 it is called on another thread than the AudioWorklet thread. Also, if the AudioContext 16 is already running when the AudioWorklet becomes ready, we need to restart the 17 AudioDestination so that we switch the audio rendering from the CoreAudio rendering 18 thread to the AudioWorklet thread. 19 20 Test: http/wpt/webaudio/the-audio-api/the-audioworklet-interface/context-already-rendering.html 21 22 * Modules/webaudio/AudioDestinationNode.h: 23 (WebCore::AudioDestinationNode::restartRendering): 24 * Modules/webaudio/AudioWorkletNode.cpp: 25 (WebCore::AudioWorkletNode::isWorkletThread): 26 (WebCore::AudioWorkletNode::process): 27 * Modules/webaudio/AudioWorkletNode.h: 28 * Modules/webaudio/BaseAudioContext.cpp: 29 (WebCore::BaseAudioContext::addAudioParamDescriptors): 30 (WebCore::BaseAudioContext::workletIsReady): 31 * Modules/webaudio/BaseAudioContext.h: 32 * Modules/webaudio/DefaultAudioDestinationNode.cpp: 33 (WebCore::DefaultAudioDestinationNode::uninitialize): 34 (WebCore::DefaultAudioDestinationNode::startRendering): 35 (WebCore::DefaultAudioDestinationNode::resume): 36 (WebCore::DefaultAudioDestinationNode::suspend): 37 (WebCore::DefaultAudioDestinationNode::restartRendering): 38 (WebCore::DefaultAudioDestinationNode::setChannelCount): 39 * Modules/webaudio/DefaultAudioDestinationNode.h: 40 1 41 2020-11-20 Lauro Moura <lmoura@igalia.com> 2 42 -
trunk/Source/WebCore/Modules/webaudio/AudioDestinationNode.h
r269475 r270127 60 60 virtual void suspend(CompletionHandler<void(Optional<Exception>&&)>&& completionHandler) { completionHandler(WTF::nullopt); } 61 61 virtual void close(CompletionHandler<void()>&& completionHandler) { completionHandler(); } 62 virtual void restartRendering() { } 62 63 63 64 virtual bool isPlaying() { return false; } -
trunk/Source/WebCore/Modules/webaudio/AudioWorkletNode.cpp
r268728 r270127 183 183 auto locker = holdLock(m_processLock); 184 184 m_processor = WTFMove(processor); 185 m_workletThread = &Thread::current(); 185 186 } else 186 187 fireProcessorErrorOnMainThread(ProcessorError::ConstructorError); … … 192 193 193 194 auto locker = tryHoldLock(m_processLock); 194 if (!locker || !m_processor ) {195 if (!locker || !m_processor || &Thread::current() != m_workletThread) { 195 196 // We're not ready yet or we are getting destroyed. In this case, we output silence. 196 197 for (unsigned i = 0; i < numberOfOutputs(); ++i) -
trunk/Source/WebCore/Modules/webaudio/AudioWorkletNode.h
r268517 r270127 88 88 RefPtr<AudioWorkletProcessor> m_processor; // Should only be used on the rendering thread. 89 89 HashMap<String, std::unique_ptr<AudioFloatArray>> m_paramValuesMap; 90 Thread* m_workletThread { nullptr }; 90 91 91 92 // Keeps the reference of AudioBus objects from AudioNodeInput and AudioNodeOutput in order -
trunk/Source/WebCore/Modules/webaudio/BaseAudioContext.cpp
r269475 r270127 1041 1041 { 1042 1042 ASSERT(!m_parameterDescriptorMap.contains(processorName)); 1043 bool wasEmpty = m_parameterDescriptorMap.isEmpty(); 1043 1044 m_parameterDescriptorMap.add(processorName, WTFMove(descriptors)); 1045 if (wasEmpty) 1046 workletIsReady(); 1044 1047 } 1045 1048 … … 1054 1057 1055 1058 m_finishedSourceNodes.append(&node); 1059 } 1060 1061 void BaseAudioContext::workletIsReady() 1062 { 1063 ASSERT(isMainThread()); 1064 1065 // If we're already rendering when the worklet becomes ready, we need to restart 1066 // rendering in order to switch to the audio worklet thread. 1067 if (m_destinationNode) 1068 m_destinationNode->restartRendering(); 1056 1069 } 1057 1070 -
trunk/Source/WebCore/Modules/webaudio/BaseAudioContext.h
r269475 r270127 318 318 319 319 void scheduleNodeDeletion(); 320 void workletIsReady(); 320 321 321 322 // When source nodes begin playing, the BaseAudioContext keeps them alive inside m_referencedSourceNodes. -
trunk/Source/WebCore/Modules/webaudio/DefaultAudioDestinationNode.cpp
r269475 r270127 76 76 77 77 ALWAYS_LOG(LOGIDENTIFIER); 78 m_wasDestinationStarted = false; 78 79 m_destination->stop(); 79 80 m_destination = nullptr; … … 129 130 }; 130 131 132 m_wasDestinationStarted = true; 131 133 m_destination->start(dispatchToRenderThreadFunction(), WTFMove(innerCompletionHandler)); 132 134 } … … 141 143 return; 142 144 } 145 m_wasDestinationStarted = true; 143 146 m_destination->start(dispatchToRenderThreadFunction(), [completionHandler = WTFMove(completionHandler)](bool success) mutable { 144 147 completionHandler(success ? WTF::nullopt : makeOptional(Exception { InvalidStateError, "Failed to start the audio device"_s })); … … 156 159 } 157 160 161 m_wasDestinationStarted = false; 158 162 m_destination->stop([completionHandler = WTFMove(completionHandler)](bool success) mutable { 159 163 completionHandler(success ? WTF::nullopt : makeOptional(Exception { InvalidStateError, "Failed to stop the audio device"_s })); 160 164 }); 165 } 166 167 void DefaultAudioDestinationNode::restartRendering() 168 { 169 if (!m_wasDestinationStarted) 170 return; 171 172 m_destination->stop(); 173 m_destination->start(dispatchToRenderThreadFunction()); 161 174 } 162 175 … … 192 205 if (this->channelCount() != oldChannelCount && isInitialized()) { 193 206 // Re-create destination. 194 m_destination->stop(); 207 if (m_wasDestinationStarted) 208 m_destination->stop(); 195 209 createDestination(); 196 m_destination->start(dispatchToRenderThreadFunction()); 210 if (m_wasDestinationStarted) 211 m_destination->start(dispatchToRenderThreadFunction()); 197 212 } 198 213 -
trunk/Source/WebCore/Modules/webaudio/DefaultAudioDestinationNode.h
r269475 r270127 60 60 void resume(CompletionHandler<void(Optional<Exception>&&)>&&) final; 61 61 void suspend(CompletionHandler<void(Optional<Exception>&&)>&&) final; 62 void restartRendering() final; 62 63 void close(CompletionHandler<void()>&&) final; 63 64 unsigned maxChannelCount() const final; … … 65 66 66 67 RefPtr<AudioDestination> m_destination; 68 bool m_wasDestinationStarted { false }; 67 69 String m_inputDeviceId; 68 70 unsigned m_numberOfInputChannels { 0 };
Note: See TracChangeset
for help on using the changeset viewer.