Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(479)

Side by Side Diff: Source/WebCore/webaudio/AudioContext.cpp

Issue 7749016: Merge 92658 - Fix thread-safety of AudioNode deletion (Closed) Base URL: http://svn.webkit.org/repository/webkit/branches/chromium/835/
Patch Set: Created 9 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « Source/WebCore/webaudio/AudioContext.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2010, Google Inc. All rights reserved. 2 * Copyright (C) 2010, Google Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions 5 * modification, are permitted provided that the following conditions
6 * are met: 6 * are met:
7 * 1. Redistributions of source code must retain the above copyright 7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer. 8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright 9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the 10 * notice, this list of conditions and the following disclaimer in the
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after
112 return adoptRef(new AudioContext(document, numberOfChannels, numberOfFrames, sampleRate)); 112 return adoptRef(new AudioContext(document, numberOfChannels, numberOfFrames, sampleRate));
113 } 113 }
114 114
115 // Constructor for rendering to the audio hardware. 115 // Constructor for rendering to the audio hardware.
116 AudioContext::AudioContext(Document* document) 116 AudioContext::AudioContext(Document* document)
117 : ActiveDOMObject(document, this) 117 : ActiveDOMObject(document, this)
118 , m_isInitialized(false) 118 , m_isInitialized(false)
119 , m_isAudioThreadFinished(false) 119 , m_isAudioThreadFinished(false)
120 , m_document(document) 120 , m_document(document)
121 , m_destinationNode(0) 121 , m_destinationNode(0)
122 , m_isDeletionScheduled(false)
122 , m_connectionCount(0) 123 , m_connectionCount(0)
123 , m_audioThread(0) 124 , m_audioThread(0)
124 , m_graphOwnerThread(UndefinedThreadIdentifier) 125 , m_graphOwnerThread(UndefinedThreadIdentifier)
125 , m_isOfflineContext(false) 126 , m_isOfflineContext(false)
126 { 127 {
127 constructCommon(); 128 constructCommon();
128 129
129 m_destinationNode = DefaultAudioDestinationNode::create(this); 130 m_destinationNode = DefaultAudioDestinationNode::create(this);
130 131
131 // This sets in motion an asynchronous loading mechanism on another thread. 132 // This sets in motion an asynchronous loading mechanism on another thread.
(...skipping 20 matching lines...) Expand all
152 // FIXME: the passed in sampleRate MUST match the hardware sample-rate since HRTFDatabaseLoader is a singleton. 153 // FIXME: the passed in sampleRate MUST match the hardware sample-rate since HRTFDatabaseLoader is a singleton.
153 m_hrtfDatabaseLoader = HRTFDatabaseLoader::createAndLoadAsynchronouslyIfNece ssary(sampleRate); 154 m_hrtfDatabaseLoader = HRTFDatabaseLoader::createAndLoadAsynchronouslyIfNece ssary(sampleRate);
154 155
155 // Create a new destination for offline rendering. 156 // Create a new destination for offline rendering.
156 m_renderTarget = AudioBuffer::create(numberOfChannels, numberOfFrames, sampl eRate); 157 m_renderTarget = AudioBuffer::create(numberOfChannels, numberOfFrames, sampl eRate);
157 m_destinationNode = OfflineAudioDestinationNode::create(this, m_renderTarget .get()); 158 m_destinationNode = OfflineAudioDestinationNode::create(this, m_renderTarget .get());
158 } 159 }
159 160
160 void AudioContext::constructCommon() 161 void AudioContext::constructCommon()
161 { 162 {
162 // Note: because adoptRef() won't be called until we leave this constructor, but code in this constructor needs to reference this context,
163 // relax the check.
164 relaxAdoptionRequirement();
165
166 FFTFrame::initialize(); 163 FFTFrame::initialize();
167 164
168 m_listener = AudioListener::create(); 165 m_listener = AudioListener::create();
169 m_temporaryMonoBus = adoptPtr(new AudioBus(1, AudioNode::ProcessingSizeInFra mes)); 166 m_temporaryMonoBus = adoptPtr(new AudioBus(1, AudioNode::ProcessingSizeInFra mes));
170 m_temporaryStereoBus = adoptPtr(new AudioBus(2, AudioNode::ProcessingSizeInF rames)); 167 m_temporaryStereoBus = adoptPtr(new AudioBus(2, AudioNode::ProcessingSizeInF rames));
171 } 168 }
172 169
173 AudioContext::~AudioContext() 170 AudioContext::~AudioContext()
174 { 171 {
175 #if DEBUG_AUDIONODE_REFERENCES 172 #if DEBUG_AUDIONODE_REFERENCES
176 printf("%p: AudioContext::~AudioContext()\n", this); 173 printf("%p: AudioContext::~AudioContext()\n", this);
177 #endif 174 #endif
178 // AudioNodes keep a reference to their context, so there should be no way t o be in the destructor if there are still AudioNodes around. 175 // AudioNodes keep a reference to their context, so there should be no way t o be in the destructor if there are still AudioNodes around.
179 ASSERT(!m_nodesToDelete.size()); 176 ASSERT(!m_nodesToDelete.size());
180 ASSERT(!m_referencedNodes.size()); 177 ASSERT(!m_referencedNodes.size());
181 ASSERT(!m_finishedNodes.size()); 178 ASSERT(!m_finishedNodes.size());
182 } 179 }
183 180
184 void AudioContext::lazyInitialize() 181 void AudioContext::lazyInitialize()
185 { 182 {
186 if (!m_isInitialized) { 183 if (!m_isInitialized) {
187 // Don't allow the context to initialize a second time after it's alread y been explicitly uninitialized. 184 // Don't allow the context to initialize a second time after it's alread y been explicitly uninitialized.
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
219 // We have to release our reference to the destination node before the c ontext will ever be deleted since the destination node holds a reference to the context. 216 // We have to release our reference to the destination node before the c ontext will ever be deleted since the destination node holds a reference to the context.
220 m_destinationNode.clear(); 217 m_destinationNode.clear();
221 218
222 if (!isOfflineContext()) { 219 if (!isOfflineContext()) {
223 ASSERT(s_hardwareContextCount); 220 ASSERT(s_hardwareContextCount);
224 --s_hardwareContextCount; 221 --s_hardwareContextCount;
225 } 222 }
226 223
227 // Get rid of the sources which may still be playing. 224 // Get rid of the sources which may still be playing.
228 derefUnfinishedSourceNodes(); 225 derefUnfinishedSourceNodes();
229 226
227 deleteMarkedNodes();
228
230 // Because the AudioBuffers are garbage collected, we can't delete them here. 229 // Because the AudioBuffers are garbage collected, we can't delete them here.
231 // Instead, at least release the potentially large amount of allocated m emory for the audio data. 230 // Instead, at least release the potentially large amount of allocated m emory for the audio data.
232 // Note that we do this *after* the context is uninitialized and stops p rocessing audio. 231 // Note that we do this *after* the context is uninitialized and stops p rocessing audio.
233 for (unsigned i = 0; i < m_allocatedBuffers.size(); ++i) 232 for (unsigned i = 0; i < m_allocatedBuffers.size(); ++i)
234 m_allocatedBuffers[i]->releaseMemory(); 233 m_allocatedBuffers[i]->releaseMemory();
235 m_allocatedBuffers.clear(); 234 m_allocatedBuffers.clear();
236 235
237 m_isInitialized = false; 236 m_isInitialized = false;
238 } 237 }
239 } 238 }
(...skipping 318 matching lines...) Expand 10 before | Expand all | Expand 10 after
558 // The worst that can happen is that there will be some nodes which will tak e slightly longer than usual to be deleted or removed 557 // The worst that can happen is that there will be some nodes which will tak e slightly longer than usual to be deleted or removed
559 // from the render graph (in which case they'll render silence). 558 // from the render graph (in which case they'll render silence).
560 bool mustReleaseLock; 559 bool mustReleaseLock;
561 if (tryLock(mustReleaseLock)) { 560 if (tryLock(mustReleaseLock)) {
562 // Take care of finishing any derefs where the tryLock() failed previous ly. 561 // Take care of finishing any derefs where the tryLock() failed previous ly.
563 handleDeferredFinishDerefs(); 562 handleDeferredFinishDerefs();
564 563
565 // Dynamically clean up nodes which are no longer needed. 564 // Dynamically clean up nodes which are no longer needed.
566 derefFinishedSourceNodes(); 565 derefFinishedSourceNodes();
567 566
568 // Finally actually delete. 567 // Don't delete in the real-time thread. Let the main thread do it.
569 deleteMarkedNodes(); 568 // Ref-counted objects held by certain AudioNodes may not be thread-safe .
569 scheduleNodeDeletion();
570 570
571 // Fixup the state of any dirty AudioNodeInputs and AudioNodeOutputs. 571 // Fixup the state of any dirty AudioNodeInputs and AudioNodeOutputs.
572 handleDirtyAudioNodeInputs(); 572 handleDirtyAudioNodeInputs();
573 handleDirtyAudioNodeOutputs(); 573 handleDirtyAudioNodeOutputs();
574 574
575 if (mustReleaseLock) 575 if (mustReleaseLock)
576 unlock(); 576 unlock();
577 } 577 }
578 } 578 }
579 579
580 void AudioContext::handleDeferredFinishDerefs() 580 void AudioContext::handleDeferredFinishDerefs()
581 { 581 {
582 ASSERT(isAudioThread() && isGraphOwner()); 582 ASSERT(isAudioThread() && isGraphOwner());
583 for (unsigned i = 0; i < m_deferredFinishDerefList.size(); ++i) { 583 for (unsigned i = 0; i < m_deferredFinishDerefList.size(); ++i) {
584 AudioNode* node = m_deferredFinishDerefList[i].m_node; 584 AudioNode* node = m_deferredFinishDerefList[i].m_node;
585 AudioNode::RefType refType = m_deferredFinishDerefList[i].m_refType; 585 AudioNode::RefType refType = m_deferredFinishDerefList[i].m_refType;
586 node->finishDeref(refType); 586 node->finishDeref(refType);
587 } 587 }
588 588
589 m_deferredFinishDerefList.clear(); 589 m_deferredFinishDerefList.clear();
590 } 590 }
591 591
592 void AudioContext::markForDeletion(AudioNode* node) 592 void AudioContext::markForDeletion(AudioNode* node)
593 { 593 {
594 ASSERT(isGraphOwner()); 594 ASSERT(isGraphOwner());
595 m_nodesToDelete.append(node); 595 m_nodesToDelete.append(node);
596 } 596 }
597 597
598 void AudioContext::scheduleNodeDeletion()
599 {
600 bool isGood = m_isInitialized && isGraphOwner();
601 ASSERT(isGood);
602 if (!isGood)
603 return;
604
605 // Make sure to call deleteMarkedNodes() on main thread.
606 if (m_nodesToDelete.size() && !m_isDeletionScheduled) {
607 m_isDeletionScheduled = true;
608
609 // Don't let ourself get deleted before the callback.
610 // See matching deref() in deleteMarkedNodesDispatch().
611 ref();
612 callOnMainThread(deleteMarkedNodesDispatch, this);
613 }
614 }
615
616 void AudioContext::deleteMarkedNodesDispatch(void* userData)
617 {
618 AudioContext* context = reinterpret_cast<AudioContext*>(userData);
619 ASSERT(context);
620 if (!context)
621 return;
622
623 context->deleteMarkedNodes();
624 context->deref();
625 }
626
598 void AudioContext::deleteMarkedNodes() 627 void AudioContext::deleteMarkedNodes()
599 { 628 {
600 ASSERT(isGraphOwner() || isAudioThreadFinished()); 629 ASSERT(isMainThread());
601 630
631 AutoLocker locker(this);
632
602 // Note: deleting an AudioNode can cause m_nodesToDelete to grow. 633 // Note: deleting an AudioNode can cause m_nodesToDelete to grow.
603 size_t nodesDeleted = 0;
604 while (size_t n = m_nodesToDelete.size()) { 634 while (size_t n = m_nodesToDelete.size()) {
605 AudioNode* node = m_nodesToDelete[n - 1]; 635 AudioNode* node = m_nodesToDelete[n - 1];
606 m_nodesToDelete.removeLast(); 636 m_nodesToDelete.removeLast();
607 637
608 // Before deleting the node, clear out any AudioNodeInputs from m_dirtyA udioNodeInputs. 638 // Before deleting the node, clear out any AudioNodeInputs from m_dirtyA udioNodeInputs.
609 unsigned numberOfInputs = node->numberOfInputs(); 639 unsigned numberOfInputs = node->numberOfInputs();
610 for (unsigned i = 0; i < numberOfInputs; ++i) 640 for (unsigned i = 0; i < numberOfInputs; ++i)
611 m_dirtyAudioNodeInputs.remove(node->input(i)); 641 m_dirtyAudioNodeInputs.remove(node->input(i));
612 642
613 // Before deleting the node, clear out any AudioNodeOutputs from m_dirty AudioNodeOutputs. 643 // Before deleting the node, clear out any AudioNodeOutputs from m_dirty AudioNodeOutputs.
614 unsigned numberOfOutputs = node->numberOfOutputs(); 644 unsigned numberOfOutputs = node->numberOfOutputs();
615 for (unsigned i = 0; i < numberOfOutputs; ++i) 645 for (unsigned i = 0; i < numberOfOutputs; ++i)
616 m_dirtyAudioNodeOutputs.remove(node->output(i)); 646 m_dirtyAudioNodeOutputs.remove(node->output(i));
617 647
618 // Finally, delete it. 648 // Finally, delete it.
619 delete node; 649 delete node;
620
621 // Don't delete too many nodes per render quantum since we don't want to do too much work in the realtime audio thread.
622 if (++nodesDeleted > MaxNodesToDeletePerQuantum)
623 break;
624 } 650 }
651
652 m_isDeletionScheduled = false;
625 } 653 }
626 654
627 void AudioContext::markAudioNodeInputDirty(AudioNodeInput* input) 655 void AudioContext::markAudioNodeInputDirty(AudioNodeInput* input)
628 { 656 {
629 ASSERT(isGraphOwner()); 657 ASSERT(isGraphOwner());
630 m_dirtyAudioNodeInputs.add(input); 658 m_dirtyAudioNodeInputs.add(input);
631 } 659 }
632 660
633 void AudioContext::markAudioNodeOutputDirty(AudioNodeOutput* output) 661 void AudioContext::markAudioNodeOutputDirty(AudioNodeOutput* output)
634 { 662 {
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
686 // Avoid firing the event if the document has already gone away. 714 // Avoid firing the event if the document has already gone away.
687 if (hasDocument()) { 715 if (hasDocument()) {
688 // Call the offline rendering completion event listener. 716 // Call the offline rendering completion event listener.
689 dispatchEvent(OfflineAudioCompletionEvent::create(renderedBuffer)); 717 dispatchEvent(OfflineAudioCompletionEvent::create(renderedBuffer));
690 } 718 }
691 } 719 }
692 720
693 } // namespace WebCore 721 } // namespace WebCore
694 722
695 #endif // ENABLE(WEB_AUDIO) 723 #endif // ENABLE(WEB_AUDIO)
OLDNEW
« no previous file with comments | « Source/WebCore/webaudio/AudioContext.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698