Title: [93818] branches/chromium/835/Source/WebCore/webaudio
Revision
93818
Author
[email protected]
Date
2011-08-25 13:37:12 -0700 (Thu, 25 Aug 2011)

Log Message

Merge 92658 - Fix thread-safety of AudioNode deletion
https://bugs.webkit.org/show_bug.cgi?id=65888

BUG=94278
Review URL: http://codereview.chromium.org/7749016

Modified Paths

Diff

Modified: branches/chromium/835/Source/WebCore/webaudio/AudioContext.cpp (93817 => 93818)


--- branches/chromium/835/Source/WebCore/webaudio/AudioContext.cpp	2011-08-25 20:29:30 UTC (rev 93817)
+++ branches/chromium/835/Source/WebCore/webaudio/AudioContext.cpp	2011-08-25 20:37:12 UTC (rev 93818)
@@ -119,6 +119,7 @@
     , m_isAudioThreadFinished(false)
     , m_document(document)
     , m_destinationNode(0)
+    , m_isDeletionScheduled(false)
     , m_connectionCount(0)
     , m_audioThread(0)
     , m_graphOwnerThread(UndefinedThreadIdentifier)
@@ -159,10 +160,6 @@
 
 void AudioContext::constructCommon()
 {
-    // Note: because adoptRef() won't be called until we leave this constructor, but code in this constructor needs to reference this context,
-    // relax the check.
-    relaxAdoptionRequirement();
-    
     FFTFrame::initialize();
     
     m_listener = AudioListener::create();
@@ -174,7 +171,7 @@
 {
 #if DEBUG_AUDIONODE_REFERENCES
     printf("%p: AudioContext::~AudioContext()\n", this);
-#endif    
+#endif
     // AudioNodes keep a reference to their context, so there should be no way to be in the destructor if there are still AudioNodes around.
     ASSERT(!m_nodesToDelete.size());
     ASSERT(!m_referencedNodes.size());
@@ -226,7 +223,9 @@
         
         // Get rid of the sources which may still be playing.
         derefUnfinishedSourceNodes();
-        
+
+        deleteMarkedNodes();
+
         // Because the AudioBuffers are garbage collected, we can't delete them here.
         // Instead, at least release the potentially large amount of allocated memory for the audio data.
         // Note that we do this *after* the context is uninitialized and stops processing audio.
@@ -565,8 +564,9 @@
         // Dynamically clean up nodes which are no longer needed.
         derefFinishedSourceNodes();
 
-        // Finally actually delete.
-        deleteMarkedNodes();
+        // Don't delete in the real-time thread. Let the main thread do it.
+        // Ref-counted objects held by certain AudioNodes may not be thread-safe.
+        scheduleNodeDeletion();
 
         // Fixup the state of any dirty AudioNodeInputs and AudioNodeOutputs.
         handleDirtyAudioNodeInputs();
@@ -595,12 +595,42 @@
     m_nodesToDelete.append(node);
 }
 
+void AudioContext::scheduleNodeDeletion()
+{
+    bool isGood = m_isInitialized && isGraphOwner();
+    ASSERT(isGood);
+    if (!isGood)
+        return;
+
+    // Make sure to call deleteMarkedNodes() on main thread.    
+    if (m_nodesToDelete.size() && !m_isDeletionScheduled) {
+        m_isDeletionScheduled = true;
+
+        // Don't let ourself get deleted before the callback.
+        // See matching deref() in deleteMarkedNodesDispatch().
+        ref();
+        callOnMainThread(deleteMarkedNodesDispatch, this);
+    }
+}
+
+void AudioContext::deleteMarkedNodesDispatch(void* userData)
+{
+    AudioContext* context = reinterpret_cast<AudioContext*>(userData);
+    ASSERT(context);
+    if (!context)
+        return;
+
+    context->deleteMarkedNodes();
+    context->deref();
+}
+
 void AudioContext::deleteMarkedNodes()
 {
-    ASSERT(isGraphOwner() || isAudioThreadFinished());
+    ASSERT(isMainThread());
 
+    AutoLocker locker(this);
+    
     // Note: deleting an AudioNode can cause m_nodesToDelete to grow.
-    size_t nodesDeleted = 0;
     while (size_t n = m_nodesToDelete.size()) {
         AudioNode* node = m_nodesToDelete[n - 1];
         m_nodesToDelete.removeLast();
@@ -617,11 +647,9 @@
 
         // Finally, delete it.
         delete node;
-
-        // Don't delete too many nodes per render quantum since we don't want to do too much work in the realtime audio thread.
-        if (++nodesDeleted > MaxNodesToDeletePerQuantum)
-            break;
     }
+    
+    m_isDeletionScheduled = false;
 }
 
 void AudioContext::markAudioNodeInputDirty(AudioNodeInput* input)

Modified: branches/chromium/835/Source/WebCore/webaudio/AudioContext.h (93817 => 93818)


--- branches/chromium/835/Source/WebCore/webaudio/AudioContext.h	2011-08-25 20:29:30 UTC (rev 93817)
+++ branches/chromium/835/Source/WebCore/webaudio/AudioContext.h	2011-08-25 20:37:12 UTC (rev 93818)
@@ -37,6 +37,7 @@
 #include <wtf/PassRefPtr.h>
 #include <wtf/RefCounted.h>
 #include <wtf/RefPtr.h>
+#include <wtf/ThreadSafeRefCounted.h>
 #include <wtf/Threading.h>
 #include <wtf/Vector.h>
 #include <wtf/text/AtomicStringHash.h>
@@ -66,7 +67,7 @@
 // AudioContext is the cornerstone of the web audio API and all AudioNodes are created from it.
 // For thread safety between the audio thread and the main thread, it has a rendering graph locking mechanism. 
 
-class AudioContext : public ActiveDOMObject, public RefCounted<AudioContext>, public EventTarget {
+class AudioContext : public ActiveDOMObject, public ThreadSafeRefCounted<AudioContext>, public EventTarget {
 public:
     // Create an AudioContext for rendering to the audio hardware.
     static PassRefPtr<AudioContext> create(Document*);
@@ -135,10 +136,10 @@
     // Called periodically at the end of each render quantum to dereference finished source nodes.
     void derefFinishedSourceNodes();
 
-    // We reap all marked nodes at the end of each realtime render quantum in deleteMarkedNodes().
+    // We schedule deletion of all marked nodes at the end of each realtime render quantum.
     void markForDeletion(AudioNode*);
     void deleteMarkedNodes();
-
+    
     // Keeps track of the number of connections made.
     void incrementConnectionCount()
     {
@@ -208,9 +209,9 @@
 
     DEFINE_ATTRIBUTE_EVENT_LISTENER(complete);
 
-    // Reconcile ref/deref which are defined both in AudioNode and EventTarget.
-    using RefCounted<AudioContext>::ref;
-    using RefCounted<AudioContext>::deref;
+    // Reconcile ref/deref which are defined both in ThreadSafeRefCounted and EventTarget.
+    using ThreadSafeRefCounted<AudioContext>::ref;
+    using ThreadSafeRefCounted<AudioContext>::deref;
 
     void startRendering();
     void fireCompletionEvent();
@@ -224,6 +225,9 @@
 
     void lazyInitialize();
     void uninitialize();
+
+    void scheduleNodeDeletion();
+    static void deleteMarkedNodesDispatch(void* userData);
     
     bool m_isInitialized;
     bool m_isAudioThreadFinished;
@@ -256,8 +260,10 @@
     // Either accessed when the graph lock is held, or on the main thread when the audio thread has finished.
     Vector<AudioNode*> m_referencedNodes;
 
-    // Accumulate nodes which need to be deleted at the end of a render cycle (in realtime thread) here.
+    // Accumulate nodes which need to be deleted here.
+    // They will be scheduled for deletion (on the main thread) at the end of a render cycle (in realtime thread).
     Vector<AudioNode*> m_nodesToDelete;
+    bool m_isDeletionScheduled;
 
     // Only accessed when the graph lock is held.
     HashSet<AudioNodeInput*> m_dirtyAudioNodeInputs;
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to