Title: [276747] trunk/Source/WebCore
Revision
276747
Author
[email protected]
Date
2021-04-28 18:53:30 -0700 (Wed, 28 Apr 2021)

Log Message

Crash under WebCore::AudioNodeOutput::pull(WebCore::AudioBus*, unsigned long)
https://bugs.webkit.org/show_bug.cgi?id=225174
<rdar://77270701>

Unreviewed, revert r274767 as it introduced this crash.


* Modules/webaudio/AudioNodeInput.cpp:
(WebCore::AudioNodeInput::connect):
(WebCore::AudioNodeInput::disconnect):
(WebCore::AudioNodeInput::disable):
(WebCore::AudioNodeInput::enable):
(WebCore::AudioNodeInput::bus):
(WebCore::AudioNodeInput::sumAllConnections):
(WebCore::AudioNodeInput::pull):
* Modules/webaudio/AudioNodeInput.h:
* Modules/webaudio/AudioNodeOutput.cpp:
(WebCore::AudioNodeOutput::disable):
(WebCore::AudioNodeOutput::enable):
* Modules/webaudio/AudioParam.cpp:
(WebCore::AudioParam::calculateFinalValues):
* Modules/webaudio/AudioSummingJunction.cpp:
(WebCore::AudioSummingJunction::addOutput):
(WebCore::AudioSummingJunction::removeOutput):
(WebCore::AudioSummingJunction::updateRenderingState):
(WebCore::AudioSummingJunction::maximumNumberOfChannels const):
* Modules/webaudio/AudioSummingJunction.h:
(WebCore::AudioSummingJunction::renderingOutput):
(WebCore::AudioSummingJunction::renderingOutput const):
(WebCore::AudioSummingJunction::isConnected const):
(WebCore::AudioSummingJunction::numberOfConnections const):
* Modules/webaudio/WebKitAudioPannerNode.cpp:
(WebCore::WebKitAudioPannerNode::notifyAudioSourcesConnectedToNode):

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (276746 => 276747)


--- trunk/Source/WebCore/ChangeLog	2021-04-29 01:36:32 UTC (rev 276746)
+++ trunk/Source/WebCore/ChangeLog	2021-04-29 01:53:30 UTC (rev 276747)
@@ -1,3 +1,38 @@
+2021-04-28  Chris Dumez  <[email protected]>
+
+        Crash under WebCore::AudioNodeOutput::pull(WebCore::AudioBus*, unsigned long)
+        https://bugs.webkit.org/show_bug.cgi?id=225174
+        <rdar://77270701>
+
+        Unreviewed, revert r274767 as it introduced this crash.
+
+        * Modules/webaudio/AudioNodeInput.cpp:
+        (WebCore::AudioNodeInput::connect):
+        (WebCore::AudioNodeInput::disconnect):
+        (WebCore::AudioNodeInput::disable):
+        (WebCore::AudioNodeInput::enable):
+        (WebCore::AudioNodeInput::bus):
+        (WebCore::AudioNodeInput::sumAllConnections):
+        (WebCore::AudioNodeInput::pull):
+        * Modules/webaudio/AudioNodeInput.h:
+        * Modules/webaudio/AudioNodeOutput.cpp:
+        (WebCore::AudioNodeOutput::disable):
+        (WebCore::AudioNodeOutput::enable):
+        * Modules/webaudio/AudioParam.cpp:
+        (WebCore::AudioParam::calculateFinalValues):
+        * Modules/webaudio/AudioSummingJunction.cpp:
+        (WebCore::AudioSummingJunction::addOutput):
+        (WebCore::AudioSummingJunction::removeOutput):
+        (WebCore::AudioSummingJunction::updateRenderingState):
+        (WebCore::AudioSummingJunction::maximumNumberOfChannels const):
+        * Modules/webaudio/AudioSummingJunction.h:
+        (WebCore::AudioSummingJunction::renderingOutput):
+        (WebCore::AudioSummingJunction::renderingOutput const):
+        (WebCore::AudioSummingJunction::isConnected const):
+        (WebCore::AudioSummingJunction::numberOfConnections const):
+        * Modules/webaudio/WebKitAudioPannerNode.cpp:
+        (WebCore::WebKitAudioPannerNode::notifyAudioSourcesConnectedToNode):
+
 2021-04-28  Wenson Hsieh  <[email protected]>
 
         [iOS] Coalesce adjacent selection geometries when rendering individual selection quads

Modified: trunk/Source/WebCore/Modules/webaudio/AudioNodeInput.cpp (276746 => 276747)


--- trunk/Source/WebCore/Modules/webaudio/AudioNodeInput.cpp	2021-04-29 01:36:32 UTC (rev 276746)
+++ trunk/Source/WebCore/Modules/webaudio/AudioNodeInput.cpp	2021-04-29 01:53:30 UTC (rev 276747)
@@ -54,7 +54,13 @@
     if (!output || !node())
         return;
 
-    if (addOutput(*output))
+    auto addPotentiallyDisabledOutput = [this](AudioNodeOutput& output) {
+        if (output.isEnabled())
+            return addOutput(output);
+        return m_disabledOutputs.add(&output).isNewEntry;
+    };
+
+    if (addPotentiallyDisabledOutput(*output))
         output->addInput(this);
 }
 
@@ -72,24 +78,60 @@
         output->removeInput(this); // Note: it's important to return immediately after this since the node may be deleted.
         return;
     }
+    
+    // Otherwise, try to disconnect from disabled connections.
+    if (m_disabledOutputs.remove(output)) {
+        output->removeInput(this); // Note: it's important to return immediately after this since the node may be deleted.
+        return;
+    }
 
     ASSERT_NOT_REACHED();
 }
 
-void AudioNodeInput::outputEnabledStateChanged(AudioNodeOutput& output)
+void AudioNodeInput::disable(AudioNodeOutput* output)
 {
     ASSERT(context());
     ASSERT(context()->isGraphOwner());
-    AudioSummingJunction::outputEnabledStateChanged(output);
 
+    ASSERT(output && node());
+    if (!output || !node())
+        return;
+
+    {
+        // Heap allocations are forbidden on the audio thread for performance reasons so we need to
+        // explicitly allow the following allocation(s).
+        DisableMallocRestrictionsForCurrentThreadScope disableMallocRestrictions;
+        m_disabledOutputs.add(output);
+        bool wasRemoved = removeOutput(*output);
+        ASSERT_UNUSED(wasRemoved, wasRemoved);
+    }
+
     // Propagate disabled state to outputs.
-    ASSERT(node());
-    if (!node())
+    node()->disableOutputsIfNecessary();
+}
+
+void AudioNodeInput::enable(AudioNodeOutput* output)
+{
+    ASSERT(context());
+    ASSERT(context()->isGraphOwner());
+
+    ASSERT(output && node());
+    if (!output || !node())
         return;
-    if (output.isEnabled())
-        node()->enableOutputsIfNecessary();
-    else
-        node()->disableOutputsIfNecessary();
+
+    ASSERT(m_disabledOutputs.contains(output));
+
+    // Move output from disabled list to active list.
+    {
+        // Heap allocations are forbidden on the audio thread for performance reasons so we need to
+        // explicitly allow the following allocation(s).
+        DisableMallocRestrictionsForCurrentThreadScope disableMallocRestrictions;
+        addOutput(*output);
+    }
+    m_disabledOutputs.remove(output);
+
+    // Propagate enabled state to outputs.
+    node()->enableOutputsIfNecessary();
 }
 
 void AudioNodeInput::didUpdate()
@@ -135,7 +177,7 @@
 
     // Handle single connection specially to allow for in-place processing.
     if (numberOfRenderingConnections() == 1 && node()->channelCountMode() == ChannelCountMode::Max)
-        return (*renderingOutputs().begin())->bus();
+        return renderingOutput(0)->bus();
 
     // Multiple connections case or complex ChannelCountMode (or no connections).
     return internalSummingBus();
@@ -165,7 +207,7 @@
 
     auto interpretation = node()->channelInterpretation();
 
-    for (auto* output : renderingOutputs()) {
+    for (auto& output : m_renderingOutputs) {
         ASSERT(output);
 
         // Render audio from this output.
@@ -181,17 +223,16 @@
     ASSERT(context());
     ASSERT(context()->isAudioThread());
 
-    auto numberOfRenderingConnections = this->numberOfRenderingConnections();
     // Handle single connection case.
-    if (numberOfRenderingConnections == 1 && node()->channelCountMode() == ChannelCountMode::Max) {
+    if (numberOfRenderingConnections() == 1 && node()->channelCountMode() == ChannelCountMode::Max) {
         // The output will optimize processing using inPlaceBus if it's able.
-        AudioNodeOutput* output = *renderingOutputs().begin();
+        AudioNodeOutput* output = this->renderingOutput(0);
         return output->pull(inPlaceBus, framesToProcess);
     }
 
     AudioBus* internalSummingBus = this->internalSummingBus();
 
-    if (!numberOfRenderingConnections) {
+    if (!numberOfRenderingConnections()) {
         // At least, generate silence if we're not connected to anything.
         // FIXME: if we wanted to get fancy, we could propagate a 'silent hint' here to optimize the downstream graph processing.
         internalSummingBus->zero();

Modified: trunk/Source/WebCore/Modules/webaudio/AudioNodeInput.h (276746 => 276747)


--- trunk/Source/WebCore/Modules/webaudio/AudioNodeInput.h	2021-04-29 01:36:32 UTC (rev 276746)
+++ trunk/Source/WebCore/Modules/webaudio/AudioNodeInput.h	2021-04-29 01:53:30 UTC (rev 276747)
@@ -55,8 +55,11 @@
     void connect(AudioNodeOutput*);
     void disconnect(AudioNodeOutput*);
 
+    // disable() will take the output out of the active connections list and set aside in a disabled list.
+    // enable() will put the output back into the active connections list.
     // Must be called with the context's graph lock.
-    void outputEnabledStateChanged(AudioNodeOutput&) final;
+    void enable(AudioNodeOutput*);
+    void disable(AudioNodeOutput*);
 
     // pull() processes all of the AudioNodes connected to us.
     // In the case of multiple connections it sums the result into an internal summing bus.
@@ -79,6 +82,11 @@
 private:
     AudioNode* m_node;
 
+    // m_disabledOutputs contains the AudioNodeOutputs which are disabled (will not be processed) by the audio graph rendering.
+    // But, from _javascript_'s perspective, these outputs are still connected to us.
+    // Generally, these represent disabled connections from "notes" which have finished playing but are not yet garbage collected.
+    HashSet<AudioNodeOutput*> m_disabledOutputs;
+
     // Called from context's audio thread.
     AudioBus* internalSummingBus();
     void sumAllConnections(AudioBus* summingBus, size_t framesToProcess);

Modified: trunk/Source/WebCore/Modules/webaudio/AudioNodeOutput.cpp (276746 => 276747)


--- trunk/Source/WebCore/Modules/webaudio/AudioNodeOutput.cpp	2021-04-29 01:36:32 UTC (rev 276746)
+++ trunk/Source/WebCore/Modules/webaudio/AudioNodeOutput.cpp	2021-04-29 01:53:30 UTC (rev 276747)
@@ -228,12 +228,11 @@
 {
     ASSERT(context().isGraphOwner());
 
-    if (!m_isEnabled)
-        return;
-
-    m_isEnabled = false;
-    for (auto& input : m_inputs.keys())
-        input->outputEnabledStateChanged(*this);
+    if (m_isEnabled) {
+        for (auto& input : m_inputs.keys())
+            input->disable(this);
+        m_isEnabled = false;
+    }
 }
 
 void AudioNodeOutput::enable()
@@ -240,12 +239,11 @@
 {
     ASSERT(context().isGraphOwner());
 
-    if (m_isEnabled)
-        return;
-
-    m_isEnabled = true;
-    for (auto& input : m_inputs.keys())
-        input->outputEnabledStateChanged(*this);
+    if (!m_isEnabled) {
+        for (auto& input : m_inputs.keys())
+            input->enable(this);
+        m_isEnabled = true;
+    }
 }
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/Modules/webaudio/AudioParam.cpp (276746 => 276747)


--- trunk/Source/WebCore/Modules/webaudio/AudioParam.cpp	2021-04-29 01:36:32 UTC (rev 276746)
+++ trunk/Source/WebCore/Modules/webaudio/AudioParam.cpp	2021-04-29 01:53:30 UTC (rev 276747)
@@ -314,7 +314,7 @@
     ASSERT(numberOfValues <= AudioUtilities::renderQuantumSize);
     m_summingBus->setChannelMemory(0, values, sampleAccurate ? numberOfValues : 1);
 
-    for (auto* output : renderingOutputs()) {
+    for (auto& output : m_renderingOutputs) {
         ASSERT(output);
 
         // Render audio from this output.

Modified: trunk/Source/WebCore/Modules/webaudio/AudioSummingJunction.cpp (276746 => 276747)


--- trunk/Source/WebCore/Modules/webaudio/AudioSummingJunction.cpp	2021-04-29 01:36:32 UTC (rev 276746)
+++ trunk/Source/WebCore/Modules/webaudio/AudioSummingJunction.cpp	2021-04-29 01:53:30 UTC (rev 276747)
@@ -62,13 +62,10 @@
     if (!m_outputs.add(&output).isNewEntry)
         return false;
 
-    if (!output.isEnabled())
-        return true;
-
-    if (!m_pendingRenderingOutputs)
-        m_pendingRenderingOutputs.emplace(m_outputs);
+    if (m_pendingRenderingOutputs.isEmpty())
+        m_pendingRenderingOutputs = copyToVector(m_outputs);
     else
-        m_pendingRenderingOutputs->append(output);
+        m_pendingRenderingOutputs.append(&output);
 
     markRenderingStateAsDirty();
     return true;
@@ -81,19 +78,11 @@
     if (!m_outputs.remove(&output))
         return false;
 
-    if (!output.isEnabled())
-        return true;
+    if (m_pendingRenderingOutputs.isEmpty())
+        m_pendingRenderingOutputs = copyToVector(m_outputs);
+    else
+        m_pendingRenderingOutputs.removeFirst(&output);
 
-    if (!m_pendingRenderingOutputs) {
-        // Heap allocations are forbidden on the audio thread for performance reasons so we need to
-        // explicitly allow the following allocation(s).
-        DisableMallocRestrictionsForCurrentThreadScope disableMallocRestrictions;
-        m_pendingRenderingOutputs.emplace(m_outputs);
-    } else {
-        bool wasRemoved = m_pendingRenderingOutputs->remove(output);
-        ASSERT_UNUSED(wasRemoved, wasRemoved);
-    }
-
     markRenderingStateAsDirty();
     return true;
 }
@@ -105,13 +94,9 @@
 
     if (m_renderingStateNeedUpdating && canUpdateState()) {
         // Copy from m_outputs to m_renderingOutputs.
-        if (!m_pendingRenderingOutputs)
-            m_renderingOutputs = { };
-        else {
-            m_renderingOutputs = *std::exchange(m_pendingRenderingOutputs, WTF::nullopt);
-            for (auto* output : m_renderingOutputs)
-                output->updateRenderingState();
-        }
+        m_renderingOutputs = std::exchange(m_pendingRenderingOutputs, { });
+        for (auto& output : m_renderingOutputs)
+            output->updateRenderingState();
 
         didUpdate();
 
@@ -123,9 +108,6 @@
 {
     unsigned maxChannels = 0;
     for (auto* output : m_outputs) {
-        if (!output->isEnabled())
-            continue;
-
         // Use output()->numberOfChannels() instead of output->bus()->numberOfChannels(),
         // because the calling of AudioNodeOutput::bus() is not safe here.
         maxChannels = std::max(maxChannels, output->numberOfChannels());
@@ -133,34 +115,6 @@
     return maxChannels;
 }
 
-void AudioSummingJunction::outputEnabledStateChanged(AudioNodeOutput& output)
-{
-    ASSERT(context());
-    ASSERT(context()->isGraphOwner());
-    if (!m_pendingRenderingOutputs) {
-        // Heap allocations are forbidden on the audio thread for performance reasons so we need to
-        // explicitly allow the following allocation(s).
-        DisableMallocRestrictionsForCurrentThreadScope disableMallocRestrictions;
-        m_pendingRenderingOutputs.emplace(m_outputs);
-    } else
-        m_pendingRenderingOutputs->updatedEnabledState(output);
-    markRenderingStateAsDirty();
-}
-
-inline AudioSummingJunction::RenderingOutputCollection::RenderingOutput::RenderingOutput(AudioNodeOutput* output)
-    : output(output)
-    , isEnabled(output->isEnabled())
-{ }
-
-void WebCore::AudioSummingJunction::RenderingOutputCollection::updatedEnabledState(AudioNodeOutput& output)
-{
-    auto index = m_outputs.find(&output);
-    ASSERT(index != notFound);
-    if (index == notFound)
-        return;
-    m_outputs[index].isEnabled = output.isEnabled();
-}
-
 } // namespace WebCore
 
 #endif // ENABLE(WEB_AUDIO)

Modified: trunk/Source/WebCore/Modules/webaudio/AudioSummingJunction.h (276746 => 276747)


--- trunk/Source/WebCore/Modules/webaudio/AudioSummingJunction.h	2021-04-29 01:36:32 UTC (rev 276746)
+++ trunk/Source/WebCore/Modules/webaudio/AudioSummingJunction.h	2021-04-29 01:53:30 UTC (rev 276747)
@@ -25,7 +25,6 @@
 #pragma once
 
 #include "AudioBus.h"
-#include <iterator>
 #include <wtf/HashSet.h>
 #include <wtf/Vector.h>
 #include <wtf/WeakPtr.h>
@@ -50,76 +49,11 @@
     // This must be called when we own the context's graph lock in the audio thread at the very start or end of the render quantum.
     void updateRenderingState();
 
-    class RenderingOutputCollection {
-    public:
-        RenderingOutputCollection() = default;
-        RenderingOutputCollection(const HashSet<AudioNodeOutput*>& outputs)
-            : m_outputs(copyToVectorOf<RenderingOutput>(outputs))
-        {
-        }
-
-        bool isEmpty() const { return begin() == end(); }
-        size_t size() const { return std::distance(begin(), end()); }
-
-        void append(AudioNodeOutput& output) { m_outputs.append(&output); }
-        bool remove(AudioNodeOutput& output) { return m_outputs.removeFirst(&output); }
-        void updatedEnabledState(AudioNodeOutput&);
-
-        struct RenderingOutput {
-            RenderingOutput(AudioNodeOutput*);
-            bool operator==(const AudioNodeOutput* other) const { return output == other; }
-            AudioNodeOutput* output;
-            bool isEnabled;
-        };
-
-        class ConstIterator {
-        public:
-            using difference_type = size_t;
-            using value_type = AudioNodeOutput*;
-            using pointer = AudioNodeOutput**;
-            using reference = AudioNodeOutput*&;
-            using iterator_category = std::forward_iterator_tag;
-
-            ConstIterator(const Vector<RenderingOutput>& outputs, Vector<RenderingOutput>::const_iterator position)
-                : m_position(position)
-                , m_end(outputs.end())
-            {
-                skipDisabledOutputs();
-            }
-
-            AudioNodeOutput* operator*() const { return m_position->output; }
-            constexpr bool operator==(const ConstIterator& other) const { return m_position == other.m_position; }
-            constexpr bool operator!=(const ConstIterator& other) const { return !(*this == other); }
-            ConstIterator& operator++()
-            {
-                ASSERT(m_position != m_end);
-                ++m_position;
-                skipDisabledOutputs();
-                return *this;
-            }
-
-        private:
-            void skipDisabledOutputs()
-            {
-                while (m_position != m_end && !m_position->isEnabled)
-                    ++m_position;
-            }
-
-            Vector<RenderingOutput>::const_iterator m_position;
-            Vector<RenderingOutput>::const_iterator m_end;
-        };
-
-        ConstIterator begin() const { return ConstIterator { m_outputs, m_outputs.begin() }; }
-        ConstIterator end() const { return ConstIterator { m_outputs, m_outputs.end() }; }
-
-    private:
-        Vector<RenderingOutput> m_outputs;
-    };
-
     // Rendering code accesses its version of the current connections here.
-    const RenderingOutputCollection& renderingOutputs() const { return m_renderingOutputs; }
     unsigned numberOfRenderingConnections() const { return m_renderingOutputs.size(); }
-    bool isConnected() const { return !m_renderingOutputs.isEmpty(); }
+    AudioNodeOutput* renderingOutput(unsigned i) { return m_renderingOutputs[i]; }
+    const AudioNodeOutput* renderingOutput(unsigned i) const { return m_renderingOutputs[i]; }
+    bool isConnected() const { return numberOfRenderingConnections() > 0; }
 
     virtual bool canUpdateState() = 0;
     virtual void didUpdate() = 0;
@@ -129,27 +63,29 @@
 
     void markRenderingStateAsDirty();
 
-    virtual void outputEnabledStateChanged(AudioNodeOutput&);
+protected:
+    WeakPtr<BaseAudioContext> m_context;
 
-protected:
+    // numberOfConnections() should never be called from the audio rendering thread.
+    // Instead numberOfRenderingConnections() and renderingOutput() should be used.
+    unsigned numberOfConnections() const { return m_outputs.size(); }
+
     unsigned maximumNumberOfChannels() const;
 
-private:
-    WeakPtr<BaseAudioContext> m_context;
-
     // m_renderingOutputs is a copy of m_outputs which will never be modified during the graph rendering on the audio thread.
     // This is the list which is used by the rendering code.
     // Whenever m_outputs is modified, the context is told so it can later update m_renderingOutputs from m_outputs at a safe time.
     // Most of the time, m_renderingOutputs is identical to m_outputs.
-    RenderingOutputCollection m_renderingOutputs;
+    Vector<AudioNodeOutput*> m_renderingOutputs;
 
     // m_renderingStateNeedUpdating keeps track if m_outputs is modified.
     bool m_renderingStateNeedUpdating { false };
 
+private:
     // m_outputs contains the AudioNodeOutputs representing current connections which are not disabled.
     // The rendering code should never use this directly, but instead uses m_renderingOutputs.
     HashSet<AudioNodeOutput*> m_outputs;
-    Optional<RenderingOutputCollection> m_pendingRenderingOutputs;
+    Vector<AudioNodeOutput*> m_pendingRenderingOutputs;
 };
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/Modules/webaudio/WebKitAudioPannerNode.cpp (276746 => 276747)


--- trunk/Source/WebCore/Modules/webaudio/WebKitAudioPannerNode.cpp	2021-04-29 01:36:32 UTC (rev 276746)
+++ trunk/Source/WebCore/Modules/webaudio/WebKitAudioPannerNode.cpp	2021-04-29 01:53:30 UTC (rev 276747)
@@ -319,7 +319,8 @@
             AudioNodeInput* input = node->input(i);
 
             // For each input, go through all of its connections, looking for AudioBufferSourceNodes.
-            for (auto* connectedOutput : input->renderingOutputs()) {
+            for (unsigned j = 0; j < input->numberOfRenderingConnections(); ++j) {
+                AudioNodeOutput* connectedOutput = input->renderingOutput(j);
                 AudioNode* connectedNode = connectedOutput->node();
                 if (visitedNodes.contains(connectedNode))
                     continue;
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to