Index: Source/WebCore/webaudio/AudioContext.cpp |
=================================================================== |
--- Source/WebCore/webaudio/AudioContext.cpp (revision 93817) |
+++ Source/WebCore/webaudio/AudioContext.cpp (working copy) |
@@ -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) |