Backed out changeset 18c302f80bce (bug 1108707) for nsTArray_base leaks.
authorRyan VanderMeulen <ryanvm@gmail.com>
Tue, 09 Dec 2014 15:49:38 -0500
changeset 244735 38478b0b00fbd37439bed25344b6246303f3759c
parent 244734 817f770bff547fc9c0109d4476ddca867000fd8a
child 244736 9a7e59858dc68ea4063a8daa76cc00bd64327bfd
push id4489
push userraliiev@mozilla.com
push dateMon, 23 Feb 2015 15:17:55 +0000
treeherdermozilla-beta@fd7c3dc24146 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1108707
milestone37.0a1
backs out18c302f80bce6eef56d888ea14ae81f65a265cbb
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
Backed out changeset 18c302f80bce (bug 1108707) for nsTArray_base leaks. CLOSED TREE
dom/media/MediaDecoderReader.cpp
dom/media/MediaDecoderReader.h
dom/media/MediaDecoderStateMachine.cpp
dom/media/MediaDecoderStateMachine.h
dom/media/MediaTaskQueue.cpp
dom/media/MediaTaskQueue.h
dom/media/android/AndroidMediaReader.cpp
dom/media/android/AndroidMediaReader.h
dom/media/fmp4/MP4Reader.cpp
dom/media/fmp4/MP4Reader.h
dom/media/mediasource/MediaSourceDecoder.h
dom/media/mediasource/MediaSourceReader.cpp
dom/media/mediasource/MediaSourceReader.h
dom/media/mediasource/TrackBuffer.cpp
dom/media/mediasource/TrackBuffer.h
dom/media/mediasource/moz.build
dom/media/omx/MediaCodecReader.cpp
dom/media/omx/MediaCodecReader.h
dom/media/omx/MediaOmxReader.cpp
dom/media/omx/MediaOmxReader.h
dom/media/webm/WebMReader.cpp
dom/media/webm/WebMReader.h
--- a/dom/media/MediaDecoderReader.cpp
+++ b/dom/media/MediaDecoderReader.cpp
@@ -278,38 +278,28 @@ MediaDecoderReader::BreakCycles()
 {
   if (mSampleDecodedCallback) {
     mSampleDecodedCallback->BreakCycles();
     mSampleDecodedCallback = nullptr;
   }
   mTaskQueue = nullptr;
 }
 
-nsRefPtr<ShutdownPromise>
+void
 MediaDecoderReader::Shutdown()
 {
   MOZ_ASSERT(OnDecodeThread());
   mShutdown = true;
   ReleaseMediaResources();
-  nsRefPtr<ShutdownPromise> p;
-
-  // Spin down the task queue if necessary. We wait until BreakCycles to null
-  // out mTaskQueue, since otherwise any remaining tasks could crash when they
-  // invoke GetTaskQueue()->IsCurrentThreadIn().
   if (mTaskQueue && !mTaskQueueIsBorrowed) {
-    // If we own our task queue, shutdown ends when the task queue is done.
-    p = mTaskQueue->BeginShutdown();
-  } else {
-    // If we don't own our task queue, we resolve immediately (though
-    // asynchronously).
-    p = new ShutdownPromise(__func__);
-    p->Resolve(true, __func__);
+    // We may be running in the task queue ourselves, so we don't block this
+    // thread on task queue draining, since that would deadlock.
+    mTaskQueue->BeginShutdown();
   }
-
-  return p;
+  mTaskQueue = nullptr;
 }
 
 AudioDecodeRendezvous::AudioDecodeRendezvous()
   : mMonitor("AudioDecodeRendezvous")
   , mHaveResult(false)
 {
 }
 
--- a/dom/media/MediaDecoderReader.h
+++ b/dom/media/MediaDecoderReader.h
@@ -60,17 +60,17 @@ public:
   // WARNING: If you override this, you must call the base implementation
   // in your override.
   virtual void BreakCycles();
 
   // Destroys the decoding state. The reader cannot be made usable again.
   // This is different from ReleaseMediaResources() as it is irreversable,
   // whereas ReleaseMediaResources() is.  Must be called on the decode
   // thread.
-  virtual nsRefPtr<ShutdownPromise> Shutdown();
+  virtual void Shutdown();
 
   virtual void SetCallback(RequestSampleCallback* aDecodedSampleCallback);
   MediaTaskQueue* EnsureTaskQueue();
 
   virtual bool OnDecodeThread()
   {
     return !GetTaskQueue() || GetTaskQueue()->IsCurrentThreadIn();
   }
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -2465,87 +2465,73 @@ public:
                                                       mStateMachine.forget()));
     return NS_OK;
   }
 private:
   nsRefPtr<MediaDecoder> mDecoder;
   nsRefPtr<MediaDecoderStateMachine> mStateMachine;
 };
 
-void
-MediaDecoderStateMachine::ShutdownReader()
-{
-  MOZ_ASSERT(OnDecodeThread());
-  mReader->Shutdown()->Then(GetStateMachineThread(), __func__, this,
-                            &MediaDecoderStateMachine::FinishShutdown,
-                            &MediaDecoderStateMachine::FinishShutdown);
-}
-
-void
-MediaDecoderStateMachine::FinishShutdown(bool aSuccess)
-{
-  MOZ_ASSERT(OnStateMachineThread());
-  MOZ_ASSERT(aSuccess);
-  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
-
-  // The reader's listeners hold references to the state machine,
-  // creating a cycle which keeps the state machine and its shared
-  // thread pools alive. So break it here.
-  AudioQueue().ClearListeners();
-  VideoQueue().ClearListeners();
-
-  // Now that those threads are stopped, there's no possibility of
-  // mPendingWakeDecoder being needed again. Revoke it.
-  mPendingWakeDecoder = nullptr;
-
-  MOZ_ASSERT(mState == DECODER_STATE_SHUTDOWN,
-             "How did we escape from the shutdown state?");
-  // We must daisy-chain these events to destroy the decoder. We must
-  // destroy the decoder on the main thread, but we can't destroy the
-  // decoder while this thread holds the decoder monitor. We can't
-  // dispatch an event to the main thread to destroy the decoder from
-  // here, as the event may run before the dispatch returns, and we
-  // hold the decoder monitor here. We also want to guarantee that the
-  // state machine is destroyed on the main thread, and so the
-  // event runner running this function (which holds a reference to the
-  // state machine) needs to finish and be released in order to allow
-  // that. So we dispatch an event to run after this event runner has
-  // finished and released its monitor/references. That event then will
-  // dispatch an event to the main thread to release the decoder and
-  // state machine.
-  GetStateMachineThread()->Dispatch(
-    new nsDispatchDisposeEvent(mDecoder, this), NS_DISPATCH_NORMAL);
-
-  DECODER_LOG("Dispose Event Dispatched");
-}
-
 nsresult MediaDecoderStateMachine::RunStateMachine()
 {
   AssertCurrentThreadInMonitor();
 
   MediaResource* resource = mDecoder->GetResource();
   NS_ENSURE_TRUE(resource, NS_ERROR_NULL_POINTER);
 
   switch (mState) {
     case DECODER_STATE_SHUTDOWN: {
       if (IsPlaying()) {
         StopPlayback();
       }
 
       StopAudioThread();
       FlushDecoding();
 
-      // Put a task in the decode queue to shutdown the reader.
+      // Put a task in the decode queue to shutdown the reader and wait for
       // the queue to spin down.
-      RefPtr<nsIRunnable> task;
-      task = NS_NewRunnableMethod(this, &MediaDecoderStateMachine::ShutdownReader);
-      DebugOnly<nsresult> rv = DecodeTaskQueue()->Dispatch(task);
-      MOZ_ASSERT(NS_SUCCEEDED(rv));
-
-      DECODER_LOG("Shutdown started");
+      {
+        RefPtr<nsIRunnable> task;
+        task = NS_NewRunnableMethod(mReader, &MediaDecoderReader::Shutdown);
+        nsRefPtr<MediaTaskQueue> queue = DecodeTaskQueue();
+        DebugOnly<nsresult> rv = queue->Dispatch(task);
+        MOZ_ASSERT(NS_SUCCEEDED(rv));
+        ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
+        queue->AwaitShutdownAndIdle();
+      }
+
+      // The reader's listeners hold references to the state machine,
+      // creating a cycle which keeps the state machine and its shared
+      // thread pools alive. So break it here.
+      AudioQueue().ClearListeners();
+      VideoQueue().ClearListeners();
+
+      // Now that those threads are stopped, there's no possibility of
+      // mPendingWakeDecoder being needed again. Revoke it.
+      mPendingWakeDecoder = nullptr;
+
+      MOZ_ASSERT(mState == DECODER_STATE_SHUTDOWN,
+                 "How did we escape from the shutdown state?");
+      // We must daisy-chain these events to destroy the decoder. We must
+      // destroy the decoder on the main thread, but we can't destroy the
+      // decoder while this thread holds the decoder monitor. We can't
+      // dispatch an event to the main thread to destroy the decoder from
+      // here, as the event may run before the dispatch returns, and we
+      // hold the decoder monitor here. We also want to guarantee that the
+      // state machine is destroyed on the main thread, and so the
+      // event runner running this function (which holds a reference to the
+      // state machine) needs to finish and be released in order to allow
+      // that. So we dispatch an event to run after this event runner has
+      // finished and released its monitor/references. That event then will
+      // dispatch an event to the main thread to release the decoder and
+      // state machine.
+      GetStateMachineThread()->Dispatch(
+        new nsDispatchDisposeEvent(mDecoder, this), NS_DISPATCH_NORMAL);
+
+      DECODER_LOG("SHUTDOWN OK");
       return NS_OK;
     }
 
     case DECODER_STATE_DORMANT: {
       if (IsPlaying()) {
         StopPlayback();
       }
       StopAudioThread();
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -157,18 +157,16 @@ public:
   void SetVolume(double aVolume);
   void SetAudioCaptured(bool aCapture);
 
   // Check if the decoder needs to become dormant state.
   bool IsDormantNeeded();
   // Set/Unset dormant state.
   void SetDormant(bool aDormant);
   void Shutdown();
-  void ShutdownReader();
-  void FinishShutdown(bool aSuccess);
 
   // Called from the main thread to get the duration. The decoder monitor
   // must be obtained before calling this. It is in units of microseconds.
   int64_t GetDuration();
 
   // Called from the main thread to set the duration of the media resource
   // if it is able to be obtained via HTTP headers. Called from the
   // state machine thread to set the duration if it is obtained from the
--- a/dom/media/MediaTaskQueue.cpp
+++ b/dom/media/MediaTaskQueue.cpp
@@ -129,27 +129,22 @@ MediaTaskQueue::AwaitShutdownAndIdle()
 {
   MonitorAutoLock mon(mQueueMonitor);
   while (!mIsShutdown) {
     mQueueMonitor.Wait();
   }
   AwaitIdleLocked();
 }
 
-nsRefPtr<ShutdownPromise>
+void
 MediaTaskQueue::BeginShutdown()
 {
   MonitorAutoLock mon(mQueueMonitor);
   mIsShutdown = true;
-  nsRefPtr<ShutdownPromise> p = mShutdownPromise.Ensure(__func__);
-  if (!mIsRunning) {
-    mShutdownPromise.Resolve(true, __func__);
-  }
   mon.NotifyAll();
-  return p;
 }
 
 nsresult
 MediaTaskQueue::FlushAndDispatch(TemporaryRef<nsIRunnable> aRunnable)
 {
   MonitorAutoLock mon(mQueueMonitor);
   AutoSetFlushing autoFlush(this);
   FlushLocked();
@@ -208,17 +203,16 @@ MediaTaskQueue::Runner::Run()
 {
   RefPtr<nsIRunnable> event;
   {
     MonitorAutoLock mon(mQueue->mQueueMonitor);
     MOZ_ASSERT(mQueue->mIsRunning);
     mQueue->mRunningThread = NS_GetCurrentThread();
     if (mQueue->mTasks.size() == 0) {
       mQueue->mIsRunning = false;
-      mQueue->mShutdownPromise.ResolveIfExists(true, __func__);
       mon.NotifyAll();
       return NS_OK;
     }
     event = mQueue->mTasks.front().mRunnable;
     mQueue->mTasks.pop();
   }
   MOZ_ASSERT(event);
 
@@ -236,17 +230,16 @@ MediaTaskQueue::Runner::Run()
   // can shutdown (like in the MediaDecoderStateMachine's SHUTDOWN case).
   event = nullptr;
 
   {
     MonitorAutoLock mon(mQueue->mQueueMonitor);
     if (mQueue->mTasks.size() == 0) {
       // No more events to run. Exit the task runner.
       mQueue->mIsRunning = false;
-      mQueue->mShutdownPromise.ResolveIfExists(true, __func__);
       mon.NotifyAll();
       mQueue->mRunningThread = nullptr;
       return NS_OK;
     }
   }
 
   // There's at least one more event that we can run. Dispatch this Runner
   // to the thread pool again to ensure it runs again. Note that we don't just
--- a/dom/media/MediaTaskQueue.h
+++ b/dom/media/MediaTaskQueue.h
@@ -7,26 +7,23 @@
 #ifndef MediaTaskQueue_h_
 #define MediaTaskQueue_h_
 
 #include <queue>
 #include "mozilla/RefPtr.h"
 #include "mozilla/Monitor.h"
 #include "SharedThreadPool.h"
 #include "nsThreadUtils.h"
-#include "MediaPromise.h"
 
 class nsIRunnable;
 
 namespace mozilla {
 
 class SharedThreadPool;
 
-typedef MediaPromise<bool, bool> ShutdownPromise;
-
 // Abstracts executing runnables in order in a thread pool. The runnables
 // dispatched to the MediaTaskQueue will be executed in the order in which
 // they're received, and are guaranteed to not be executed concurrently.
 // They may be executed on different threads, and a memory barrier is used
 // to make this threadsafe for objects that aren't already threadsafe.
 class MediaTaskQueue MOZ_FINAL {
   ~MediaTaskQueue();
 
@@ -48,19 +45,17 @@ public:
   // Removes all pending tasks from the task queue, and blocks until
   // the currently running task (if any) finishes.
   void Flush();
 
   // Puts the queue in a shutdown state and returns immediately. The queue will
   // remain alive at least until all the events are drained, because the Runners
   // hold a strong reference to the task queue, and one of them is always held
   // by the threadpool event queue when the task queue is non-empty.
-  //
-  // The returned promise is resolved when the queue goes empty.
-  nsRefPtr<ShutdownPromise> BeginShutdown();
+  void BeginShutdown();
 
   // Blocks until all task finish executing.
   void AwaitIdle();
 
   // Blocks until the queue is flagged for shutdown and all tasks have finished
   // executing.
   void AwaitShutdownAndIdle();
 
@@ -105,17 +100,16 @@ private:
   RefPtr<nsIThread> mRunningThread;
 
   // True if we've dispatched an event to the pool to execute events from
   // the queue.
   bool mIsRunning;
 
   // True if we've started our shutdown process.
   bool mIsShutdown;
-  MediaPromiseHolder<ShutdownPromise> mShutdownPromise;
 
   class MOZ_STACK_CLASS AutoSetFlushing
   {
   public:
     explicit AutoSetFlushing(MediaTaskQueue* aTaskQueue) : mTaskQueue(aTaskQueue)
     {
       mTaskQueue->mQueueMonitor.AssertCurrentThreadOwns();
       mTaskQueue->mIsFlushing = true;
--- a/dom/media/android/AndroidMediaReader.cpp
+++ b/dom/media/android/AndroidMediaReader.cpp
@@ -94,26 +94,25 @@ nsresult AndroidMediaReader::ReadMetadat
     mInfo.mAudio.mRate = sampleRate;
   }
 
  *aInfo = mInfo;
  *aTags = nullptr;
   return NS_OK;
 }
 
-nsRefPtr<ShutdownPromise>
-AndroidMediaReader::Shutdown()
+void AndroidMediaReader::Shutdown()
 {
   ResetDecode();
   if (mPlugin) {
     GetAndroidMediaPluginHost()->DestroyDecoder(mPlugin);
     mPlugin = nullptr;
   }
 
-  return MediaDecoderReader::Shutdown();
+  MediaDecoderReader::Shutdown();
 }
 
 // Resets all state related to decoding, emptying all buffers etc.
 nsresult AndroidMediaReader::ResetDecode()
 {
   if (mLastVideoFrame) {
     mLastVideoFrame = nullptr;
   }
--- a/dom/media/android/AndroidMediaReader.h
+++ b/dom/media/android/AndroidMediaReader.h
@@ -66,17 +66,17 @@ public:
     // not used
     return true;
   }
 
   virtual nsresult ReadMetadata(MediaInfo* aInfo,
                                 MetadataTags** aTags);
   virtual void Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime, int64_t aCurrentTime);
 
-  virtual nsRefPtr<ShutdownPromise> Shutdown() MOZ_OVERRIDE;
+  virtual void Shutdown() MOZ_OVERRIDE;
 
   class ImageBufferCallback : public MPAPI::BufferCallback {
     typedef mozilla::layers::Image Image;
 
   public:
     ImageBufferCallback(mozilla::layers::ImageContainer *aImageContainer);
     void *operator()(size_t aWidth, size_t aHeight,
                      MPAPI::ColorFormat aColorFormat) MOZ_OVERRIDE;
--- a/dom/media/fmp4/MP4Reader.cpp
+++ b/dom/media/fmp4/MP4Reader.cpp
@@ -134,17 +134,17 @@ MP4Reader::MP4Reader(AbstractMediaDecode
   MOZ_COUNT_CTOR(MP4Reader);
 }
 
 MP4Reader::~MP4Reader()
 {
   MOZ_COUNT_DTOR(MP4Reader);
 }
 
-nsRefPtr<ShutdownPromise>
+void
 MP4Reader::Shutdown()
 {
   MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn());
 
   if (mAudio.mDecoder) {
     Flush(kAudio);
     mAudio.mDecoder->Shutdown();
     mAudio.mDecoder = nullptr;
@@ -167,17 +167,17 @@ MP4Reader::Shutdown()
   // Dispose of the queued sample before shutting down the demuxer
   mQueuedVideoSample = nullptr;
 
   if (mPlatform) {
     mPlatform->Shutdown();
     mPlatform = nullptr;
   }
 
-  return MediaDecoderReader::Shutdown();
+  MediaDecoderReader::Shutdown();
 }
 
 void
 MP4Reader::InitLayersBackendType()
 {
   if (!IsVideoContentType(mDecoder->GetResource()->GetContentType())) {
     // Not playing video, we don't care about the layers backend type.
     return;
--- a/dom/media/fmp4/MP4Reader.h
+++ b/dom/media/fmp4/MP4Reader.h
@@ -66,17 +66,17 @@ public:
   virtual bool IsWaitingMediaResources() MOZ_OVERRIDE;
   virtual bool IsDormantNeeded() MOZ_OVERRIDE;
   virtual void ReleaseMediaResources() MOZ_OVERRIDE;
   virtual void SetSharedDecoderManager(SharedDecoderManager* aManager)
     MOZ_OVERRIDE;
 
   virtual nsresult ResetDecode() MOZ_OVERRIDE;
 
-  virtual nsRefPtr<ShutdownPromise> Shutdown() MOZ_OVERRIDE;
+  virtual void Shutdown() MOZ_OVERRIDE;
 
 private:
 
   void ReturnEOS(TrackType aTrack);
   void ReturnOutput(MediaData* aData, TrackType aTrack);
 
   // Sends input to decoder for aTrack, and output to the state machine,
   // if necessary.
--- a/dom/media/mediasource/MediaSourceDecoder.h
+++ b/dom/media/mediasource/MediaSourceDecoder.h
@@ -6,24 +6,24 @@
 
 #ifndef MOZILLA_MEDIASOURCEDECODER_H_
 #define MOZILLA_MEDIASOURCEDECODER_H_
 
 #include "mozilla/Attributes.h"
 #include "nsCOMPtr.h"
 #include "nsError.h"
 #include "MediaDecoder.h"
-#include "MediaSourceReader.h"
 
 class nsIStreamListener;
 
 namespace mozilla {
 
 class MediaResource;
 class MediaDecoderStateMachine;
+class MediaSourceReader;
 class SourceBufferDecoder;
 class TrackBuffer;
 
 namespace dom {
 
 class HTMLMediaElement;
 class MediaSource;
 
@@ -66,18 +66,16 @@ public:
   // Indicates the point in time at which the reader should consider
   // registered TrackBuffers essential for initialization.
   void PrepareReaderInitialization();
 
 #ifdef MOZ_EME
   virtual nsresult SetCDMProxy(CDMProxy* aProxy) MOZ_OVERRIDE;
 #endif
 
-  MediaSourceReader* GetReader() { return mReader; }
-
 private:
   // The owning MediaSource holds a strong reference to this decoder, and
   // calls Attach/DetachMediaSource on this decoder to set and clear
   // mMediaSource.
   dom::MediaSource* mMediaSource;
   nsRefPtr<MediaSourceReader> mReader;
 
   // Protected by GetReentrantMonitor()
--- a/dom/media/mediasource/MediaSourceReader.cpp
+++ b/dom/media/mediasource/MediaSourceReader.cpp
@@ -231,63 +231,44 @@ MediaSourceReader::OnNotDecoded(MediaDat
     return;
   }
 
   // We don't have the data the caller wants. Tell that we're waiting for JS to
   // give us more data.
   GetCallback()->OnNotDecoded(aType, WAITING_FOR_DATA);
 }
 
-nsRefPtr<ShutdownPromise>
+void
 MediaSourceReader::Shutdown()
 {
-  MOZ_ASSERT(mMediaSourceShutdownPromise.IsEmpty());
-  nsRefPtr<ShutdownPromise> p = mMediaSourceShutdownPromise.Ensure(__func__);
-
-  ContinueShutdown(true);
-  return p;
-}
-
-void
-MediaSourceReader::ContinueShutdown(bool aSuccess)
-{
-  MOZ_ASSERT(aSuccess);
-  if (mTrackBuffers.Length()) {
-    mTrackBuffers[0]->Shutdown()->Then(GetTaskQueue(), __func__, this,
-                                       &MediaSourceReader::ContinueShutdown,
-                                       &MediaSourceReader::ContinueShutdown);
-    mShutdownTrackBuffers.AppendElement(mTrackBuffers[0]);
-    mTrackBuffers.RemoveElementAt(0);
-    return;
+  MediaDecoderReader::Shutdown();
+  for (uint32_t i = 0; i < mTrackBuffers.Length(); ++i) {
+    mTrackBuffers[i]->Shutdown();
   }
-
   mAudioTrack = nullptr;
   mAudioReader = nullptr;
   mVideoTrack = nullptr;
   mVideoReader = nullptr;
-
-  MediaDecoderReader::Shutdown()->ChainTo(mMediaSourceShutdownPromise.Steal(), __func__);
 }
 
 void
 MediaSourceReader::BreakCycles()
 {
   MediaDecoderReader::BreakCycles();
 
   // These were cleared in Shutdown().
   MOZ_ASSERT(!mAudioTrack);
   MOZ_ASSERT(!mAudioReader);
   MOZ_ASSERT(!mVideoTrack);
   MOZ_ASSERT(!mVideoReader);
-  MOZ_ASSERT(!mTrackBuffers.Length());
 
-  for (uint32_t i = 0; i < mShutdownTrackBuffers.Length(); ++i) {
-    mShutdownTrackBuffers[i]->BreakCycles();
+  for (uint32_t i = 0; i < mTrackBuffers.Length(); ++i) {
+    mTrackBuffers[i]->BreakCycles();
   }
-  mShutdownTrackBuffers.Clear();
+  mTrackBuffers.Clear();
 }
 
 already_AddRefed<MediaDecoderReader>
 MediaSourceReader::SelectReader(int64_t aTarget,
                                 const nsTArray<nsRefPtr<SourceBufferDecoder>>& aTrackDecoders)
 {
   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
 
--- a/dom/media/mediasource/MediaSourceReader.h
+++ b/dom/media/mediasource/MediaSourceReader.h
@@ -92,17 +92,17 @@ public:
   nsresult GetBuffered(dom::TimeRanges* aBuffered) MOZ_OVERRIDE;
 
   already_AddRefed<SourceBufferDecoder> CreateSubDecoder(const nsACString& aType);
 
   void AddTrackBuffer(TrackBuffer* aTrackBuffer);
   void RemoveTrackBuffer(TrackBuffer* aTrackBuffer);
   void OnTrackBufferConfigured(TrackBuffer* aTrackBuffer, const MediaInfo& aInfo);
 
-  nsRefPtr<ShutdownPromise> Shutdown() MOZ_OVERRIDE;
+  void Shutdown();
 
   virtual void BreakCycles();
 
   bool IsShutdown()
   {
     ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor());
     return mDecoder->IsShutdown();
   }
@@ -130,17 +130,16 @@ private:
                                                     const nsTArray<nsRefPtr<SourceBufferDecoder>>& aTrackDecoders);
 
   void AttemptSeek();
 
   nsRefPtr<MediaDecoderReader> mAudioReader;
   nsRefPtr<MediaDecoderReader> mVideoReader;
 
   nsTArray<nsRefPtr<TrackBuffer>> mTrackBuffers;
-  nsTArray<nsRefPtr<TrackBuffer>> mShutdownTrackBuffers;
   nsTArray<nsRefPtr<TrackBuffer>> mEssentialTrackBuffers;
   nsRefPtr<TrackBuffer> mAudioTrack;
   nsRefPtr<TrackBuffer> mVideoTrack;
 
 #ifdef MOZ_EME
   nsRefPtr<CDMProxy> mCDMProxy;
 #endif
 
@@ -172,19 +171,16 @@ private:
   // the mDiscontinuity field set to true once we have the
   // first decoded sample. These flags are set during seeking
   // so we can detect when we have the first decoded sample
   // after a seek.
   bool mAudioIsSeeking;
   bool mVideoIsSeeking;
 
   bool mHasEssentialTrackBuffers;
-
-  void ContinueShutdown(bool aSuccess);
-  MediaPromiseHolder<ShutdownPromise> mMediaSourceShutdownPromise;
 #ifdef MOZ_FMP4
   nsRefPtr<SharedDecoderManager> mSharedDecoderManager;
 #endif
 };
 
 } // namespace mozilla
 
 #endif /* MOZILLA_MEDIASOURCEREADER_H_ */
--- a/dom/media/mediasource/TrackBuffer.cpp
+++ b/dom/media/mediasource/TrackBuffer.cpp
@@ -91,49 +91,33 @@ public:
     return true;
   }
 
 private:
   TrackBuffer* mOwner;
   nsAutoTArray<nsRefPtr<SourceBufferDecoder>,2> mDecoders;
 };
 
-nsRefPtr<ShutdownPromise>
+void
 TrackBuffer::Shutdown()
 {
-  MOZ_ASSERT(mShutdownPromise.IsEmpty());
-  nsRefPtr<ShutdownPromise> p = mShutdownPromise.Ensure(__func__);
-
-  RefPtr<MediaTaskQueue> queue = mTaskQueue;
+  // Finish any decoder initialization, which may add to mInitializedDecoders.
+  // Shutdown waits for any pending events, which may require the monitor,
+  // so we must not hold the monitor during this call.
+  mParentDecoder->GetReentrantMonitor().AssertNotCurrentThreadIn();
+  mTaskQueue->BeginShutdown();
+  mTaskQueue->AwaitShutdownAndIdle();
   mTaskQueue = nullptr;
-  queue->BeginShutdown()
-       ->Then(mParentDecoder->GetReader()->GetTaskQueue(), __func__, this,
-              &TrackBuffer::ContinueShutdown, &TrackBuffer::ContinueShutdown);
-
-  return p;
-}
 
-void
-TrackBuffer::ContinueShutdown(bool aSuccess)
-{
-  MOZ_ASSERT(aSuccess);
   ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
-  if (mDecoders.Length()) {
-    mDecoders[0]->GetReader()->Shutdown()
-                ->Then(mParentDecoder->GetReader()->GetTaskQueue(), __func__, this,
-                       &TrackBuffer::ContinueShutdown, &TrackBuffer::ContinueShutdown);
-    mShutdownDecoders.AppendElement(mDecoders[0]);
-    mDecoders.RemoveElementAt(0);
-    return;
+  for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
+    mDecoders[i]->GetReader()->Shutdown();
   }
-
   mInitializedDecoders.Clear();
   mParentDecoder = nullptr;
-
-  mShutdownPromise.Resolve(true, __func__);
 }
 
 bool
 TrackBuffer::AppendData(const uint8_t* aData, uint32_t aLength)
 {
   MOZ_ASSERT(NS_IsMainThread());
   DecodersToInitialize decoders(this);
   // TODO: Run more of the buffer append algorithm asynchronously.
@@ -444,23 +428,22 @@ TrackBuffer::ContainsTime(int64_t aTime)
   return false;
 }
 
 void
 TrackBuffer::BreakCycles()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  for (uint32_t i = 0; i < mShutdownDecoders.Length(); ++i) {
-    mShutdownDecoders[i]->BreakCycles();
+  for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
+    mDecoders[i]->BreakCycles();
   }
-  mShutdownDecoders.Clear();
+  mDecoders.Clear();
 
   // These are cleared in Shutdown()
-  MOZ_ASSERT(!mDecoders.Length());
   MOZ_ASSERT(mInitializedDecoders.IsEmpty());
   MOZ_ASSERT(!mParentDecoder);
 }
 
 void
 TrackBuffer::ResetDecode()
 {
   for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
--- a/dom/media/mediasource/TrackBuffer.h
+++ b/dom/media/mediasource/TrackBuffer.h
@@ -3,17 +3,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_TRACKBUFFER_H_
 #define MOZILLA_TRACKBUFFER_H_
 
 #include "SourceBufferDecoder.h"
-#include "MediaPromise.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/mozalloc.h"
 #include "mozilla/Maybe.h"
 #include "nsCOMPtr.h"
 #include "nsString.h"
 #include "nscore.h"
 
@@ -29,17 +28,17 @@ class TimeRanges;
 } // namespace dom
 
 class TrackBuffer MOZ_FINAL {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TrackBuffer);
 
   TrackBuffer(MediaSourceDecoder* aParentDecoder, const nsACString& aType);
 
-  nsRefPtr<ShutdownPromise> Shutdown();
+  void Shutdown();
 
   // Append data to the current decoder.  Also responsible for calling
   // NotifyDataArrived on the decoder to keep buffered range computation up
   // to date.  Returns false if the append failed.
   bool AppendData(const uint8_t* aData, uint32_t aLength);
   bool EvictData(uint32_t aThreshold);
   void EvictBefore(double aTime);
 
@@ -127,21 +126,16 @@ private:
   // initialize (i.e. call ReadMetadata on) decoders as they are created via
   // NewDecoder.
   RefPtr<MediaTaskQueue> mTaskQueue;
 
   // All of the decoders managed by this TrackBuffer.  Access protected by
   // mParentDecoder's monitor.
   nsTArray<nsRefPtr<SourceBufferDecoder>> mDecoders;
 
-  // During shutdown, we move decoders from mDecoders to mShutdownDecoders after
-  // invoking Shutdown. This is all so that we can avoid destroying the decoders
-  // off-main-thread. :-(
-  nsTArray<nsRefPtr<SourceBufferDecoder>> mShutdownDecoders;
-
   // Contains only the initialized decoders managed by this TrackBuffer.
   // Access protected by mParentDecoder's monitor.
   nsTArray<nsRefPtr<SourceBufferDecoder>> mInitializedDecoders;
 
   // Decoders which are waiting on a Content Decryption Module to be able to
   // finish ReadMetadata.
   nsTArray<nsRefPtr<SourceBufferDecoder>> mWaitingDecoders;
 
@@ -154,15 +148,12 @@ private:
   // The last start and end timestamps added to the TrackBuffer via
   // AppendData.  Accessed on the main thread only.
   int64_t mLastStartTimestamp;
   Maybe<int64_t> mLastEndTimestamp;
 
   // Set when the first decoder used by this TrackBuffer is initialized.
   // Protected by mParentDecoder's monitor.
   MediaInfo mInfo;
-
-  void ContinueShutdown(bool aSuccess);
-  MediaPromiseHolder<ShutdownPromise> mShutdownPromise;
 };
 
 } // namespace mozilla
 #endif /* MOZILLA_TRACKBUFFER_H_ */
--- a/dom/media/mediasource/moz.build
+++ b/dom/media/mediasource/moz.build
@@ -3,17 +3,16 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 MOCHITEST_MANIFESTS += ['test/mochitest.ini']
 
 EXPORTS += [
     'AsyncEventRunner.h',
     'MediaSourceDecoder.h',
-    'MediaSourceReader.h',
 ]
 
 EXPORTS.mozilla.dom += [
     'MediaSource.h',
     'SourceBuffer.h',
     'SourceBufferList.h',
 ]
 
--- a/dom/media/omx/MediaCodecReader.cpp
+++ b/dom/media/omx/MediaCodecReader.cpp
@@ -339,21 +339,21 @@ MediaCodecReader::ReleaseMediaResources(
   }
   if (mAudioTrack.mSource != nullptr && !mAudioTrack.mSourceIsStopped) {
     mAudioTrack.mSource->stop();
     mAudioTrack.mSourceIsStopped = true;
   }
   ReleaseCriticalResources();
 }
 
-nsRefPtr<ShutdownPromise>
+void
 MediaCodecReader::Shutdown()
 {
   ReleaseResources();
-  return MediaDecoderReader::Shutdown();
+  MediaDecoderReader::Shutdown();
 }
 
 void
 MediaCodecReader::DispatchAudioTask()
 {
   if (mAudioTrack.mTaskQueue && mAudioTrack.mTaskQueue->IsEmpty()) {
     RefPtr<nsIRunnable> task =
       NS_NewRunnableMethod(this,
--- a/dom/media/omx/MediaCodecReader.h
+++ b/dom/media/omx/MediaCodecReader.h
@@ -63,17 +63,17 @@ public:
   virtual bool IsDormantNeeded();
 
   // Release media resources they should be released in dormant state
   virtual void ReleaseMediaResources();
 
   // Destroys the decoding state. The reader cannot be made usable again.
   // This is different from ReleaseMediaResources() as Shutdown() is
   // irreversible, whereas ReleaseMediaResources() is reversible.
-  virtual nsRefPtr<ShutdownPromise> Shutdown();
+  virtual void Shutdown();
 
   // Used to retrieve some special information that can only be retrieved after
   // all contents have been continuously parsed. (ex. total duration of some
   // variable-bit-rate MP3 files.)
   virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset);
 
   // Flush the MediaTaskQueue, flush MediaCodec and raise the mDiscontinuity.
   virtual nsresult ResetDecode() MOZ_OVERRIDE;
--- a/dom/media/omx/MediaOmxReader.cpp
+++ b/dom/media/omx/MediaOmxReader.cpp
@@ -170,30 +170,27 @@ nsresult MediaOmxReader::Init(MediaDecod
 void MediaOmxReader::ReleaseDecoder()
 {
   if (mOmxDecoder.get()) {
     mOmxDecoder->ReleaseDecoder();
   }
   mOmxDecoder.clear();
 }
 
-nsRefPtr<ShutdownPromise>
-MediaOmxReader::Shutdown()
+void MediaOmxReader::Shutdown()
 {
   nsCOMPtr<nsIRunnable> cancelEvent =
     NS_NewRunnableMethod(this, &MediaOmxReader::CancelProcessCachedData);
   NS_DispatchToMainThread(cancelEvent);
 
-  nsRefPtr<ShutdownPromise> p = MediaDecoderReader::Shutdown();
+  MediaDecoderReader::Shutdown();
 
   nsCOMPtr<nsIRunnable> event =
     NS_NewRunnableMethod(this, &MediaOmxReader::ReleaseDecoder);
   NS_DispatchToMainThread(event);
-
-  return p;
 }
 
 bool MediaOmxReader::IsWaitingMediaResources()
 {
   return mIsWaitingResources;
 }
 
 void MediaOmxReader::UpdateIsWaitingMediaResources()
--- a/dom/media/omx/MediaOmxReader.h
+++ b/dom/media/omx/MediaOmxReader.h
@@ -99,17 +99,17 @@ public:
   virtual nsresult ReadMetadata(MediaInfo* aInfo,
                                 MetadataTags** aTags);
   virtual void Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime, int64_t aCurrentTime);
 
   virtual bool IsMediaSeekable() MOZ_OVERRIDE;
 
   virtual void SetIdle() MOZ_OVERRIDE;
 
-  virtual nsRefPtr<ShutdownPromise> Shutdown() MOZ_OVERRIDE;
+  virtual void Shutdown() MOZ_OVERRIDE;
 
   bool IsShutdown() {
     MutexAutoLock lock(mMutex);
     return mIsShutdown;
   }
 
   void ReleaseDecoder();
 
--- a/dom/media/webm/WebMReader.cpp
+++ b/dom/media/webm/WebMReader.cpp
@@ -213,32 +213,31 @@ WebMReader::~WebMReader()
   if (mOpusDecoder) {
     opus_multistream_decoder_destroy(mOpusDecoder);
     mOpusDecoder = nullptr;
   }
   MOZ_ASSERT(!mVideoDecoder);
   MOZ_COUNT_DTOR(WebMReader);
 }
 
-nsRefPtr<ShutdownPromise>
-WebMReader::Shutdown()
+void WebMReader::Shutdown()
 {
 #if defined(MOZ_PDM_VPX)
   if (mVideoTaskQueue) {
     mVideoTaskQueue->BeginShutdown();
     mVideoTaskQueue->AwaitShutdownAndIdle();
   }
 #endif
 
   if (mVideoDecoder) {
     mVideoDecoder->Shutdown();
     mVideoDecoder = nullptr;
   }
 
-  return MediaDecoderReader::Shutdown();
+  MediaDecoderReader::Shutdown();
 }
 
 nsresult WebMReader::Init(MediaDecoderReader* aCloneDonor)
 {
   vorbis_info_init(&mVorbisInfo);
   vorbis_comment_init(&mVorbisComment);
   memset(&mVorbisDsp, 0, sizeof(vorbis_dsp_state));
   memset(&mVorbisBlock, 0, sizeof(vorbis_block));
--- a/dom/media/webm/WebMReader.h
+++ b/dom/media/webm/WebMReader.h
@@ -128,17 +128,17 @@ class WebMReader : public MediaDecoderRe
 {
 public:
   explicit WebMReader(AbstractMediaDecoder* aDecoder);
 
 protected:
   ~WebMReader();
 
 public:
-  virtual nsRefPtr<ShutdownPromise> Shutdown() MOZ_OVERRIDE;
+  virtual void Shutdown() MOZ_OVERRIDE;
   virtual nsresult Init(MediaDecoderReader* aCloneDonor);
   virtual nsresult ResetDecode();
   virtual bool DecodeAudioData();
 
   virtual bool DecodeVideoFrame(bool &aKeyframeSkip,
                                 int64_t aTimeThreshold);
 
   virtual bool HasAudio()