Bug 1341200. Part 10 - streamline DecoderData::ShutdownDecoder() so it feels like a sync function and MFR doesn't need to explicitly wait for flush/shutdown to complete before creating new decoders. r=jya
authorJW Wang <jwwang@mozilla.com>
Mon, 27 Feb 2017 09:56:36 +0800
changeset 374862 bcc6b0e45020b1f2fe6b160ace1ecc5b24e3cf3e
parent 374861 28e9aedd34dc5f25e9131e7d508454c449c1f1e0
child 374863 06464864d84f4400bd0ab6e37a89a820747f34ec
push id10863
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 23:02:23 +0000
treeherdermozilla-aurora@0931190cd725 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjya
bugs1341200
milestone54.0a1
Bug 1341200. Part 10 - streamline DecoderData::ShutdownDecoder() so it feels like a sync function and MFR doesn't need to explicitly wait for flush/shutdown to complete before creating new decoders. r=jya MozReview-Commit-ID: F4tf1f7O89U
dom/media/MediaFormatReader.cpp
dom/media/MediaFormatReader.h
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -381,26 +381,24 @@ MediaFormatReader::DecoderData::Shutdown
 
   if (!mDecoder) {
     // No decoder to shut down.
     return;
   }
 
   if (mFlushing) {
     // Flush is is in action. Shutdown will be initiated after flush completes.
-    if (mShutdownPromise.IsEmpty()) {
-      mOwner->mShutdownPromisePool->Track(mShutdownPromise.Ensure(__func__));
-    }
-    return;
-  }
-
-  if (!mShutdownPromise.IsEmpty()) {
-    // This is called from the resolve/reject function of Flush.
-    // Let's continue shutdown.
-    mDecoder->Shutdown()->ChainTo(mShutdownPromise.Steal(), __func__);
+    MOZ_DIAGNOSTIC_ASSERT(mShutdownPromise);
+    mOwner->mShutdownPromisePool->Track(mShutdownPromise->Ensure(__func__));
+    // The order of decoder creation and shutdown is handled by LocalAllocPolicy
+    // and ShutdownPromisePool. MFR can now reset these members to a fresh state
+    // and be ready to create new decoders again without explicitly waiting for
+    // flush/shutdown to complete.
+    mShutdownPromise = nullptr;
+    mFlushing = false;
   } else {
     // No flush is in action. We can shut down the decoder now.
     mOwner->mShutdownPromisePool->Track(mDecoder->Shutdown());
   }
 
   // mShutdownPromisePool will handle the order of decoder shutdown so
   // we can forget mDecoder and be ready to create a new one.
   mDecoder = nullptr;
@@ -421,38 +419,46 @@ MediaFormatReader::DecoderData::Flush()
   mDrainRequest.DisconnectIfExists();
   mDrainState = DrainState::None;
   CancelWaitingForKey();
   mOutput.Clear();
   mNumSamplesInput = 0;
   mNumSamplesOutput = 0;
   mSizeOfQueue = 0;
   if (mDecoder) {
-    RefPtr<MediaFormatReader> owner = mOwner;
     TrackType type = mType == MediaData::AUDIO_DATA
                      ? TrackType::kAudioTrack
                      : TrackType::kVideoTrack;
     mFlushing = true;
+    MOZ_DIAGNOSTIC_ASSERT(!mShutdownPromise);
+    mShutdownPromise = new SharedShutdownPromiseHolder();
+    RefPtr<SharedShutdownPromiseHolder> p = mShutdownPromise;
+    RefPtr<MediaDataDecoder> d = mDecoder;
     mDecoder->Flush()
       ->Then(mOwner->OwnerThread(), __func__,
-             [owner, type, this]() {
-               mFlushing = false;
-               if (!mShutdownPromise.IsEmpty()) {
-                 ShutdownDecoder();
+             [type, this, p, d]() {
+               if (!p->IsEmpty()) {
+                 // Shutdown happened before flush completes. Let's continue to
+                 // shut down the decoder. Note we don't access |this| because
+                 // this decoder is no longer managed by MFR::DecoderData.
+                 d->Shutdown()->ChainTo(p->Steal(), __func__);
                  return;
                }
-               owner->ScheduleUpdate(type);
+               mFlushing = false;
+               mShutdownPromise = nullptr;
+               mOwner->ScheduleUpdate(type);
              },
-             [owner, type, this](const MediaResult& aError) {
-               mFlushing = false;
-               if (!mShutdownPromise.IsEmpty()) {
-                 ShutdownDecoder();
+             [type, this, p, d](const MediaResult& aError) {
+               if (!p->IsEmpty()) {
+                 d->Shutdown()->ChainTo(p->Steal(), __func__);
                  return;
                }
-               owner->NotifyError(type, aError);
+               mFlushing = false;
+               mShutdownPromise = nullptr;
+               mOwner->NotifyError(type, aError);
              });
   }
   mFlushed = true;
 }
 
 class MediaFormatReader::DecoderFactory
 {
   using InitPromise = MediaDataDecoder::InitPromise;
--- a/dom/media/MediaFormatReader.h
+++ b/dom/media/MediaFormatReader.h
@@ -166,16 +166,23 @@ private:
     None,
     DrainRequested,
     Draining,
     PartialDrainPending,
     DrainCompleted,
     DrainAborted,
   };
 
+  class SharedShutdownPromiseHolder : public MozPromiseHolder<ShutdownPromise>
+  {
+    NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedShutdownPromiseHolder)
+  private:
+    ~SharedShutdownPromiseHolder() { }
+  };
+
   struct DecoderData
   {
     DecoderData(MediaFormatReader* aOwner,
                 MediaData::Type aType,
                 uint32_t aNumOfMaxError)
       : mOwner(aOwner)
       , mType(aType)
       , mMutex("DecoderData")
@@ -243,17 +250,17 @@ private:
       return mWaitingForData || mWaitingForKey;
     }
 
     // MediaDataDecoder handler's variables.
     MozPromiseRequestHolder<MediaDataDecoder::DecodePromise> mDecodeRequest;
     bool mFlushing; // True if flush is in action.
     // Set to true if the last operation run on the decoder was a flush.
     bool mFlushed;
-    MozPromiseHolder<ShutdownPromise> mShutdownPromise;
+    RefPtr<SharedShutdownPromiseHolder> mShutdownPromise;
 
     MozPromiseRequestHolder<MediaDataDecoder::DecodePromise> mDrainRequest;
     DrainState mDrainState;
     bool HasPendingDrain() const
     {
       return mDrainState != DrainState::None;
     }
     void RequestDrain()