Bug 1178938 - Mirror shutdown-ness from the MDSM to the MD. r=jww
authorBobby Holley <bobbyholley@gmail.com>
Mon, 29 Jun 2015 14:27:16 -0700
changeset 251159 4272c902d349c7ca8972398b4d7490ba6cbe06fe
parent 251158 44d9a47dfefdfa33fc28a7d5c6a96951de364925
child 251160 26e369c2f34fcf90e46957e0ecf84b6b8ecdf548
push id61784
push userbobbyholley@gmail.com
push dateThu, 02 Jul 2015 19:38:54 +0000
treeherdermozilla-inbound@4272c902d349 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjww
bugs1178938
milestone42.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 1178938 - Mirror shutdown-ness from the MDSM to the MD. r=jww
dom/media/AbstractMediaDecoder.h
dom/media/MediaDecoder.cpp
dom/media/MediaDecoder.h
dom/media/MediaDecoderStateMachine.cpp
dom/media/MediaDecoderStateMachine.h
dom/media/mediasource/MediaSourceReader.cpp
dom/media/mediasource/MediaSourceReader.h
dom/media/ogg/OggDecoder.h
dom/media/ogg/OggReader.cpp
dom/media/ogg/OggReader.h
--- a/dom/media/AbstractMediaDecoder.h
+++ b/dom/media/AbstractMediaDecoder.h
@@ -49,16 +49,20 @@ class AbstractMediaDecoder : public nsIO
 public:
   // Returns the monitor for other threads to synchronise access to
   // state.
   virtual ReentrantMonitor& GetReentrantMonitor() = 0;
 
   // Returns true if the decoder is shut down.
   virtual bool IsShutdown() const = 0;
 
+  // A special version of the above for the ogg decoder that is allowed to be
+  // called cross-thread.
+  virtual bool IsOggDecoderShutdown() { return false; }
+
   virtual bool OnStateMachineTaskQueue() const = 0;
 
   virtual bool OnDecodeTaskQueue() const = 0;
 
   // Get the current MediaResource being used. Its URI will be returned
   // by currentSrc. Returns what was passed to Load(), if Load() has been called.
   virtual MediaResource* GetResource() const = 0;
 
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -330,16 +330,18 @@ bool MediaDecoder::IsInfinite()
 {
   MOZ_ASSERT(NS_IsMainThread());
   return mInfiniteStream;
 }
 
 MediaDecoder::MediaDecoder() :
   mWatchManager(this, AbstractThread::MainThread()),
   mBuffered(AbstractThread::MainThread(), TimeIntervals(), "MediaDecoder::mBuffered (Mirror)"),
+  mStateMachineIsShutdown(AbstractThread::MainThread(), true,
+                          "MediaDecoder::mStateMachineIsShutdown (Mirror)"),
   mNextFrameStatus(AbstractThread::MainThread(),
                    MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED,
                    "MediaDecoder::mNextFrameStatus (Mirror)"),
   mDormantSupported(false),
   mDecoderPosition(0),
   mPlaybackPosition(0),
   mLogicalPosition(0.0),
   mCurrentPosition(AbstractThread::MainThread(), 0, "MediaDecoder::mCurrentPosition (Mirror)"),
@@ -388,16 +390,19 @@ MediaDecoder::MediaDecoder() :
 
   //
   // Initialize watchers.
   //
 
   // mDuration
   mWatchManager.Watch(mStateMachineDuration, &MediaDecoder::DurationChanged);
 
+  // mStateMachineIsShutdown
+  mWatchManager.Watch(mStateMachineIsShutdown, &MediaDecoder::ShutdownBitChanged);
+
   // readyState
   mWatchManager.Watch(mPlayState, &MediaDecoder::UpdateReadyState);
   mWatchManager.Watch(mNextFrameStatus, &MediaDecoder::UpdateReadyState);
 
   // mLogicalPosition
   mWatchManager.Watch(mCurrentPosition, &MediaDecoder::UpdateLogicalPosition);
   mWatchManager.Watch(mPlayState, &MediaDecoder::UpdateLogicalPosition);
   mWatchManager.Watch(mLogicallySeeking, &MediaDecoder::UpdateLogicalPosition);
@@ -1249,21 +1254,23 @@ void
 MediaDecoder::SetStateMachine(MediaDecoderStateMachine* aStateMachine)
 {
   MOZ_ASSERT_IF(aStateMachine, !mDecoderStateMachine);
   mDecoderStateMachine = aStateMachine;
 
   if (mDecoderStateMachine) {
     mStateMachineDuration.Connect(mDecoderStateMachine->CanonicalDuration());
     mBuffered.Connect(mDecoderStateMachine->CanonicalBuffered());
+    mStateMachineIsShutdown.Connect(mDecoderStateMachine->CanonicalIsShutdown());
     mNextFrameStatus.Connect(mDecoderStateMachine->CanonicalNextFrameStatus());
     mCurrentPosition.Connect(mDecoderStateMachine->CanonicalCurrentPosition());
   } else {
     mStateMachineDuration.DisconnectIfConnected();
     mBuffered.DisconnectIfConnected();
+    mStateMachineIsShutdown.DisconnectIfConnected();
     mNextFrameStatus.DisconnectIfConnected();
     mCurrentPosition.DisconnectIfConnected();
   }
 }
 
 ReentrantMonitor& MediaDecoder::GetReentrantMonitor() {
   return mReentrantMonitor;
 }
@@ -1331,18 +1338,19 @@ MediaDecoder::NotifyWaitingForResourcesS
     RefPtr<nsRunnable> task =
       NS_NewRunnableMethod(mDecoderStateMachine,
                            &MediaDecoderStateMachine::NotifyWaitingForResourcesStatusChanged);
     mDecoderStateMachine->TaskQueue()->Dispatch(task.forget());
   }
 }
 
 bool MediaDecoder::IsShutdown() const {
+  MOZ_ASSERT(NS_IsMainThread());
   NS_ENSURE_TRUE(GetStateMachine(), true);
-  return GetStateMachine()->IsShutdown();
+  return mStateMachineIsShutdown;
 }
 
 // Drop reference to state machine.  Only called during shutdown dance.
 void MediaDecoder::BreakCycles() {
   SetStateMachine(nullptr);
 }
 
 MediaDecoderOwner* MediaDecoder::GetMediaOwner() const
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -890,16 +890,22 @@ protected:
   void DurationChanged();
 
   // State-watching manager.
   WatchManager<MediaDecoder> mWatchManager;
 
   // Buffered range, mirrored from the reader.
   Mirror<media::TimeIntervals> mBuffered;
 
+  // Whether the state machine is shut down.
+  Mirror<bool> mStateMachineIsShutdown;
+
+  // Used by the ogg decoder to watch mStateMachineIsShutdown.
+  virtual void ShutdownBitChanged() {}
+
   // NextFrameStatus, mirrored from the state machine.
   Mirror<MediaDecoderOwner::NextFrameStatus> mNextFrameStatus;
 
   /******
    * The following members should be accessed with the decoder lock held.
    ******/
 
   // Whether the decoder implementation supports dormant mode.
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -195,16 +195,18 @@ MediaDecoderStateMachine::MediaDecoderSt
                     "MediaDecoderStateMachine::mExplicitDuration (Mirror)"),
   mObservedDuration(TimeUnit(), "MediaDecoderStateMachine::mObservedDuration"),
   mPlayState(mTaskQueue, MediaDecoder::PLAY_STATE_LOADING,
              "MediaDecoderStateMachine::mPlayState (Mirror)"),
   mNextPlayState(mTaskQueue, MediaDecoder::PLAY_STATE_PAUSED,
                  "MediaDecoderStateMachine::mNextPlayState (Mirror)"),
   mLogicallySeeking(mTaskQueue, false,
              "MediaDecoderStateMachine::mLogicallySeeking (Mirror)"),
+  mIsShutdown(mTaskQueue, false,
+              "MediaDecoderStateMachine::mIsShutdown (Canonical)"),
   mNextFrameStatus(mTaskQueue, MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED,
                    "MediaDecoderStateMachine::mNextFrameStatus (Canonical)"),
   mFragmentEndTime(-1),
   mReader(aReader),
   mCurrentPosition(mTaskQueue, 0, "MediaDecoderStateMachine::mCurrentPosition (Canonical)"),
   mStreamStartTime(0),
   mAudioStartTime(0),
   mAudioEndTime(-1),
@@ -1372,16 +1374,18 @@ void MediaDecoderStateMachine::SetState(
   if (mState == aState) {
     return;
   }
   DECODER_LOG("Change machine state from %s to %s",
               gMachineStateStr[mState], gMachineStateStr[aState]);
 
   mState = aState;
 
+  mIsShutdown = mState == DECODER_STATE_ERROR || mState == DECODER_STATE_SHUTDOWN;
+
   // Clear state-scoped state.
   mSentPlaybackEndedEvent = false;
 }
 
 void MediaDecoderStateMachine::VolumeChanged()
 {
   MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
@@ -2461,16 +2465,17 @@ MediaDecoderStateMachine::FinishShutdown
   mExplicitDuration.DisconnectIfConnected();
   mPlayState.DisconnectIfConnected();
   mNextPlayState.DisconnectIfConnected();
   mLogicallySeeking.DisconnectIfConnected();
   mVolume.DisconnectIfConnected();
   mLogicalPlaybackRate.DisconnectIfConnected();
   mPreservesPitch.DisconnectIfConnected();
   mDuration.DisconnectAll();
+  mIsShutdown.DisconnectAll();
   mNextFrameStatus.DisconnectAll();
   mCurrentPosition.DisconnectAll();
 
   // Shut down the watch manager before shutting down our task queue.
   mWatchManager.Shutdown();
 
   MOZ_ASSERT(mState == DECODER_STATE_SHUTDOWN,
              "How did we escape from the shutdown state?");
@@ -3264,18 +3269,18 @@ void MediaDecoderStateMachine::Preserves
 
   if (mAudioSink) {
     mAudioSink->SetPreservesPitch(mPreservesPitch);
   }
 }
 
 bool MediaDecoderStateMachine::IsShutdown()
 {
-  return mState == DECODER_STATE_ERROR ||
-         mState == DECODER_STATE_SHUTDOWN;
+  MOZ_ASSERT(OnTaskQueue());
+  return mIsShutdown;
 }
 
 void MediaDecoderStateMachine::QueueMetadata(int64_t aPublishTime,
                                              nsAutoPtr<MediaInfo> aInfo,
                                              nsAutoPtr<MetadataTags> aTags)
 {
   MOZ_ASSERT(OnDecodeTaskQueue());
   AssertCurrentThreadInMonitor();
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -977,16 +977,22 @@ protected:
   // The decoder monitor must be held.
   bool IsLogicallyPlaying()
   {
     MOZ_ASSERT(OnTaskQueue());
     return mPlayState == MediaDecoder::PLAY_STATE_PLAYING ||
            mNextPlayState == MediaDecoder::PLAY_STATE_PLAYING;
   }
 
+  // Whether we're currently in or transitioning to shutdown state.
+  Canonical<bool> mIsShutdown;
+public:
+  AbstractCanonical<bool>* CanonicalIsShutdown() { return &mIsShutdown; }
+protected:
+
   // The status of our next frame. Mirrored on the main thread and used to
   // compute ready state.
   Canonical<NextFrameStatus> mNextFrameStatus;
 public:
   AbstractCanonical<NextFrameStatus>* CanonicalNextFrameStatus() { return &mNextFrameStatus; }
 protected:
 
   struct SeekJob {
--- a/dom/media/mediasource/MediaSourceReader.cpp
+++ b/dom/media/mediasource/MediaSourceReader.cpp
@@ -717,17 +717,18 @@ CreateReaderForType(const nsACString& aT
 #endif
 
   return nullptr;
 }
 
 already_AddRefed<SourceBufferDecoder>
 MediaSourceReader::CreateSubDecoder(const nsACString& aType, int64_t aTimestampOffset)
 {
-  if (IsShutdown()) {
+  MOZ_ASSERT(NS_IsMainThread());
+  if (mDecoder->IsShutdown()) {
     return nullptr;
   }
 
   // The task queue borrowing is icky. It would be nicer to just give each subreader
   // its own task queue. Unfortunately though, Request{Audio,Video}Data implementations
   // currently assert that they're on "the decode thread", and so having
   // separate task queues makes MediaSource stuff unnecessarily cumbersome. We
   // should remove the need for these assertions (which probably involves making
@@ -832,23 +833,19 @@ nsRefPtr<MediaDecoderReader::SeekPromise
 MediaSourceReader::Seek(int64_t aTime, int64_t aIgnored /* Used only for ogg which is non-MSE */)
 {
   MSE_DEBUG("Seek(aTime=%lld, aEnd=%lld, aCurrent=%lld)",
             aTime);
 
   MOZ_DIAGNOSTIC_ASSERT(mSeekPromise.IsEmpty());
   MOZ_DIAGNOSTIC_ASSERT(mAudioPromise.IsEmpty());
   MOZ_DIAGNOSTIC_ASSERT(mVideoPromise.IsEmpty());
+  MOZ_ASSERT(!mShutdown);
   nsRefPtr<SeekPromise> p = mSeekPromise.Ensure(__func__);
 
-  if (IsShutdown()) {
-    mSeekPromise.Reject(NS_ERROR_FAILURE, __func__);
-    return p;
-  }
-
   // Store pending seek target in case the track buffers don't contain
   // the desired time and we delay doing the seek.
   mOriginalSeekTime = aTime;
   mPendingSeekTime = aTime;
 
   {
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     mWaitingForSeekData = true;
--- a/dom/media/mediasource/MediaSourceReader.h
+++ b/dom/media/mediasource/MediaSourceReader.h
@@ -119,22 +119,16 @@ public:
   void AddTrackBuffer(TrackBuffer* aTrackBuffer);
   void RemoveTrackBuffer(TrackBuffer* aTrackBuffer);
   void OnTrackBufferConfigured(TrackBuffer* aTrackBuffer, const MediaInfo& aInfo);
 
   nsRefPtr<ShutdownPromise> Shutdown() override;
 
   virtual void BreakCycles() override;
 
-  bool IsShutdown()
-  {
-    ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor());
-    return mDecoder->IsShutdown();
-  }
-
   // Return true if all of the active tracks contain data for the specified time.
   bool TrackBuffersContainTime(int64_t aTime);
 
   // Mark the reader to indicate that EndOfStream has been called on our MediaSource
   // and that the media element has all the media data.
   // If called with true, the reader will come out of ended mode.
   void Ended(bool aEnded);
   // Return true if reader has all of the media data.
--- a/dom/media/ogg/OggDecoder.h
+++ b/dom/media/ogg/OggDecoder.h
@@ -8,20 +8,45 @@
 
 #include "MediaDecoder.h"
 
 namespace mozilla {
 
 class OggDecoder : public MediaDecoder
 {
 public:
+  OggDecoder()
+    : mShutdownBitMonitor("mShutdownBitMonitor")
+    , mShutdownBit(false)
+  {}
+
   virtual MediaDecoder* Clone() {
     if (!IsOggEnabled()) {
       return nullptr;
     }
     return new OggDecoder();
   }
   virtual MediaDecoderStateMachine* CreateStateMachine();
+
+  // For yucky legacy reasons, the ogg decoder needs to do a cross-thread read
+  // to check for shutdown while it hogs its own task queue. We don't want to
+  // protect the general state with a lock, so we make a special copy and a
+  // special-purpose lock. This method may be called on any thread.
+  bool IsOggDecoderShutdown() override
+  {
+    MonitorAutoLock lock(mShutdownBitMonitor);
+    return mShutdownBit;
+  }
+
+protected:
+  void ShutdownBitChanged() override
+  {
+    MonitorAutoLock lock(mShutdownBitMonitor);
+    mShutdownBit = mStateMachineIsShutdown;
+  }
+
+  Monitor mShutdownBitMonitor;
+  bool mShutdownBit;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/ogg/OggReader.cpp
+++ b/dom/media/ogg/OggReader.cpp
@@ -469,17 +469,17 @@ nsresult OggReader::ReadMetadata(MediaIn
 
   SetupTargetSkeleton(mSkeletonState);
   SetupMediaTracksInfo(serials);
 
   if (HasAudio() || HasVideo()) {
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
 
     MediaResource* resource = mDecoder->GetResource();
-    if (mInfo.mMetadataDuration.isNothing() && !mDecoder->IsShutdown() &&
+    if (mInfo.mMetadataDuration.isNothing() && !mDecoder->IsOggDecoderShutdown() &&
         resource->GetLength() >= 0 && mDecoder->IsMediaSeekable())
     {
       // We didn't get a duration from the index or a Content-Duration header.
       // Seek to the end of file to find the end time.
       int64_t length = resource->GetLength();
 
       NS_ASSERTION(length > 0, "Must have a content length to get end time");
 
@@ -1353,21 +1353,18 @@ nsresult OggReader::SeekInBufferedRange(
     }
 
     // We have an active Theora bitstream. Decode the next Theora frame, and
     // extract its keyframe's time.
     bool eof;
     do {
       bool skip = false;
       eof = !DecodeVideoFrame(skip, 0);
-      {
-        ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
-        if (mDecoder->IsShutdown()) {
-          return NS_ERROR_FAILURE;
-        }
+      if (mDecoder->IsOggDecoderShutdown()) {
+        return NS_ERROR_FAILURE;
       }
     } while (!eof &&
              mVideoQueue.GetSize() == 0);
 
     VideoData* video = mVideoQueue.PeekFront();
     if (video && !video->mKeyframe) {
       // First decoded frame isn't a keyframe, seek back to previous keyframe,
       // otherwise we'll get visual artifacts.
@@ -1505,18 +1502,17 @@ nsresult OggReader::SeekInternal(int64_t
     while ((v = mVideoQueue.PeekFront()) && !v->mKeyframe) {
       nsRefPtr<VideoData> releaseMe = mVideoQueue.PopFront();
     }
     if (mVideoQueue.GetSize() == 0) {
       // We didn't find a keyframe in the frames already here, so decode
       // forwards until we find a keyframe.
       bool skip = true;
       while (DecodeVideoFrame(skip, 0) && skip) {
-        ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
-        if (mDecoder->IsShutdown()) {
+        if (mDecoder->IsOggDecoderShutdown()) {
           return NS_ERROR_FAILURE;
         }
       }
     }
 #ifdef DEBUG
     v = mVideoQueue.PeekFront();
     if (!v || !v->mKeyframe) {
       NS_WARNING("Ogg seek didn't end up before a key frame!");
@@ -2002,40 +1998,34 @@ VideoData* OggReader::FindStartTime(int6
 
   return videoData;
 }
 
 AudioData* OggReader::SyncDecodeToFirstAudioData()
 {
   bool eof = false;
   while (!eof && AudioQueue().GetSize() == 0) {
-    {
-      ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor());
-      if (mDecoder->IsShutdown()) {
-        return nullptr;
-      }
+    if (mDecoder->IsOggDecoderShutdown()) {
+      return nullptr;
     }
     eof = !DecodeAudioData();
   }
   if (eof) {
     AudioQueue().Finish();
   }
   AudioData* d = nullptr;
   return (d = AudioQueue().PeekFront()) ? d : nullptr;
 }
 
 VideoData* OggReader::SyncDecodeToFirstVideoData()
 {
   bool eof = false;
   while (!eof && VideoQueue().GetSize() == 0) {
-    {
-      ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor());
-      if (mDecoder->IsShutdown()) {
-        return nullptr;
-      }
+    if (mDecoder->IsOggDecoderShutdown()) {
+      return nullptr;
     }
     bool keyframeSkip = false;
     eof = !DecodeVideoFrame(keyframeSkip, 0);
   }
   if (eof) {
     VideoQueue().Finish();
   }
   VideoData* d = nullptr;
--- a/dom/media/ogg/OggReader.h
+++ b/dom/media/ogg/OggReader.h
@@ -12,16 +12,17 @@
 #include <tremor/ivorbiscodec.h>
 #else
 #include <vorbis/codec.h>
 #endif
 #include "MediaDecoderReader.h"
 #include "OggCodecState.h"
 #include "VideoUtils.h"
 #include "mozilla/Monitor.h"
+#include "OggDecoder.h"
 
 namespace mozilla {
 
 // Thread safe container to store the codec information and the serial for each
 // streams.
 class OggCodecStore
 {
   public: