Bug 1181097 - Properly handle the case of resuming an AudioContext with an MSG that is already switching to an AudioCallbackDriver. r=roc, a=ritu
authorPaul Adenot <paul@paul.cx>
Mon, 13 Jul 2015 19:16:53 +0200
changeset 268876 1721ccff5c5580cedbab764c3d48a405e0ef5f59
parent 268875 d87fc5660f5b167cf6b948ead7ac7c836698be01
child 268877 4cc8a471f7ea408a2d1fb74995b3c4314be41652
push id4932
push userjlund@mozilla.com
push dateMon, 10 Aug 2015 18:23:06 +0000
treeherdermozilla-esr52@6dd5a4f5f745 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc, ritu
bugs1181097
milestone41.0a2
Bug 1181097 - Properly handle the case of resuming an AudioContext with an MSG that is already switching to an AudioCallbackDriver. r=roc, a=ritu
dom/media/GraphDriver.h
dom/media/MediaStreamGraph.cpp
dom/media/test/crashtests/audiocontext-double-suspend.html
dom/media/test/crashtests/crashtests.list
--- a/dom/media/GraphDriver.h
+++ b/dom/media/GraphDriver.h
@@ -114,16 +114,21 @@ public:
    * latency, we will get a callback approximately every 10ms. */
   virtual uint32_t IterationDuration() = 0;
 
   /* Return whether we are switching or not. */
   bool Switching() {
     return mNextDriver || mPreviousDriver;
   }
 
+  GraphDriver* NextDriver()
+  {
+    return mNextDriver;
+  }
+
   /**
    * If we are running a real time graph, get the current time stamp to schedule
    * video frames. This has to be reimplemented by real time drivers.
    */
   virtual TimeStamp GetCurrentTimeStamp() {
     return mCurrentTimeStamp;
   }
 
--- a/dom/media/MediaStreamGraph.cpp
+++ b/dom/media/MediaStreamGraph.cpp
@@ -3415,25 +3415,31 @@ MediaStreamGraphImpl::ApplyAudioContextO
 
   MoveStreams(aOperation, streamSet);
   MOZ_ASSERT(!streamSet.getFirst(),
       "Streams should be removed from the list after having been moved.");
 
   // If we have suspended the last AudioContext, and we don't have other
   // streams that have audio, this graph will automatically switch to a
   // SystemCallbackDriver, because it can't find a MediaStream that has an audio
-  // track. When resuming, force switching to an AudioCallbackDriver. It would
-  // have happened at the next iteration anyways, but doing this now save
-  // some time.
+  // track. When resuming, force switching to an AudioCallbackDriver (if we're
+  // not already switching). It would have happened at the next iteration
+  // anyways, but doing this now save some time.
   if (aOperation == AudioContextOperation::Resume) {
     if (!CurrentDriver()->AsAudioCallbackDriver()) {
-      AudioCallbackDriver* driver = new AudioCallbackDriver(this);
+      AudioCallbackDriver* driver;
+      if (CurrentDriver()->Switching()) {
+        MOZ_ASSERT(CurrentDriver()->NextDriver()->AsAudioCallbackDriver());
+        driver = CurrentDriver()->NextDriver()->AsAudioCallbackDriver();
+      } else {
+        driver = new AudioCallbackDriver(this);
+        mMixer.AddCallback(driver);
+        CurrentDriver()->SwitchAtNextIteration(driver);
+      }
       driver->EnqueueStreamAndPromiseForOperation(aStream, aPromise, aOperation);
-      mMixer.AddCallback(driver);
-      CurrentDriver()->SwitchAtNextIteration(driver);
     } else {
       // We are resuming a context, but we are already using an
       // AudioCallbackDriver, we can resolve the promise now.
       AudioContextOperationCompleted(aStream, aPromise, aOperation);
     }
   }
   // Close, suspend: check if we are going to switch to a
   // SystemAudioCallbackDriver, and pass the promise to the AudioCallbackDriver
@@ -3451,19 +3457,24 @@ MediaStreamGraphImpl::ApplyAudioContextO
           !tracks.IsEnded(); tracks.Next()) {
         audioTrackPresent = true;
       }
     }
     if (!audioTrackPresent && CurrentDriver()->AsAudioCallbackDriver()) {
       CurrentDriver()->AsAudioCallbackDriver()->
         EnqueueStreamAndPromiseForOperation(aStream, aPromise, aOperation);
 
-      SystemClockDriver* driver = new SystemClockDriver(this);
-      mMixer.RemoveCallback(CurrentDriver()->AsAudioCallbackDriver());
-      CurrentDriver()->SwitchAtNextIteration(driver);
+      SystemClockDriver* driver;
+      if (CurrentDriver()->NextDriver()) {
+        MOZ_ASSERT(!CurrentDriver()->NextDriver()->AsAudioCallbackDriver());
+      } else {
+        driver = new SystemClockDriver(this);
+        mMixer.RemoveCallback(CurrentDriver()->AsAudioCallbackDriver());
+        CurrentDriver()->SwitchAtNextIteration(driver);
+      }
     } else {
       // We are closing or suspending an AudioContext, but something else is
       // using the audio stream, we can resolve the promise now.
       AudioContextOperationCompleted(aStream, aPromise, aOperation);
     }
   }
 }
 
new file mode 100644
--- /dev/null
+++ b/dom/media/test/crashtests/audiocontext-double-suspend.html
@@ -0,0 +1,5 @@
+<script>
+var ac = new AudioContext();
+ac.resume();
+ac.resume();
+</script>
--- a/dom/media/test/crashtests/crashtests.list
+++ b/dom/media/test/crashtests/crashtests.list
@@ -75,12 +75,13 @@ HTTP load media-element-source-seek-1.ht
 load offline-buffer-source-ended-1.html
 load oscillator-ended-1.html
 load oscillator-ended-2.html
 load 1080986.html
 load 1158427.html
 load 1157994.html
 load 1122218.html
 load 1127188.html
+load audiocontext-double-suspend.html
 include ../../mediasource/test/crashtests/crashtests.list
 
 # This needs to run at the end to avoid leaking busted state into other tests.
 skip-if(B2G||winWidget||OSX==1006||OSX==1010&&isDebugBuild) load 691096-1.html