Bug 867324 - Make it safe to stop source nodes when shutting down the AudioContext in case the nodes die right away; r=roc
authorEhsan Akhgari <ehsan@mozilla.com>
Tue, 30 Apr 2013 16:01:39 -0400
changeset 130448 c00516696e8c2e409f20bca466afb8467a1c60a1
parent 130447 0ad4e00d794536b440bd8cd3bfdf448755a558bf
child 130449 8035047350351664ea346c8123e40c89f99bd97b
push id24618
push userryanvm@gmail.com
push dateWed, 01 May 2013 14:49:43 +0000
treeherdermozilla-central@4ff1e574e509 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs867324
milestone23.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 867324 - Make it safe to stop source nodes when shutting down the AudioContext in case the nodes die right away; r=roc (This time with the correct bug #)
content/media/webaudio/AudioContext.cpp
--- a/content/media/webaudio/AudioContext.cpp
+++ b/content/media/webaudio/AudioContext.cpp
@@ -292,43 +292,57 @@ AudioContext::DestinationStream() const
 }
 
 double
 AudioContext::CurrentTime() const
 {
   return MediaTimeToSeconds(Destination()->Stream()->GetCurrentTime());
 }
 
+template <class T>
 static PLDHashOperator
-StopAudioBufferSourceNode(nsPtrHashKey<AudioBufferSourceNode>* aEntry, void* aData)
+GetHashtableEntry(nsPtrHashKey<T>* aEntry, void* aData)
 {
-  ErrorResult rv;
-  aEntry->GetKey()->Stop(0.0, rv);
+  nsTArray<T*>* array = static_cast<nsTArray<T*>*>(aData);
+  array->AppendElement(aEntry->GetKey());
   return PL_DHASH_NEXT;
 }
 
-static PLDHashOperator
-StopScriptProcessorNode(nsPtrHashKey<ScriptProcessorNode>* aEntry, void* aData)
+template <class T>
+static void
+GetHashtableElements(nsTHashtable<nsPtrHashKey<T> >& aHashtable, nsTArray<T*>& aArray)
 {
-  aEntry->GetKey()->Stop();
-  return PL_DHASH_NEXT;
+  aHashtable.EnumerateEntries(&GetHashtableEntry<T>, &aArray);
 }
 
 void
 AudioContext::Shutdown()
 {
   Suspend();
   mDecoder.Shutdown();
 
   // Stop all audio buffer source nodes, to make sure that they release
   // their self-references.
-  mAudioBufferSourceNodes.EnumerateEntries(StopAudioBufferSourceNode, nullptr);
+  // We first gather an array of the nodes and then call Stop on each one,
+  // since Stop may delete the object and therefore trigger a re-entrant
+  // hashtable call to remove the pointer from the hashtable, which is
+  // not safe.
+  nsTArray<AudioBufferSourceNode*> sourceNodes;
+  GetHashtableElements(mAudioBufferSourceNodes, sourceNodes);
+  for (uint32_t i = 0; i < sourceNodes.Length(); ++i) {
+    ErrorResult rv;
+    sourceNodes[i]->Stop(0.0, rv);
+  }
   // Stop all script processor nodes, to make sure that they release
   // their self-references.
-  mScriptProcessorNodes.EnumerateEntries(StopScriptProcessorNode, nullptr);
+  nsTArray<ScriptProcessorNode*> spNodes;
+  GetHashtableElements(mScriptProcessorNodes, spNodes);
+  for (uint32_t i = 0; i < spNodes.Length(); ++i) {
+    spNodes[i]->Stop();
+  }
 }
 
 void
 AudioContext::Suspend()
 {
   MediaStream* ds = DestinationStream();
   if (ds) {
     ds->ChangeExplicitBlockerCount(1);