Bug 1264694: [MSE] P2. Clear mTaskQueue early when no longer required. r?jwwang draft
authorJean-Yves Avenard <jyavenard@mozilla.com>
Mon, 16 May 2016 18:30:19 +0800
changeset 368118 e9173964a9fd55c80c30923264ccc49094c0bbde
parent 368117 b1c5ac279be43013a68135a65597c69742a75f88
child 368119 3d399336c4534f705a53c8fdbf4301f8f7120d92
push id18433
push userbmo:jyavenard@mozilla.com
push dateWed, 18 May 2016 02:57:01 +0000
reviewersjwwang
bugs1264694
milestone49.0a1
Bug 1264694: [MSE] P2. Clear mTaskQueue early when no longer required. r?jwwang We need to ensure that the MSE TaskQueue gets shutdown as soon as possible and not wait for the MediaSource parent to be destroyed by the cycle collector. XPCOM shutdown will deadlock if any SharedThreadPool are still in use, and it possible for the cycle collector to only occur after xpcom has shutdown. So it's important to ensure mTaskQueue is cleared when the MediaSourceDecoder has been shutdown. This is done by queueing a new DetachTask that will clear mTaskQueue when run. MozReview-Commit-ID: C3FXcRtq1wy
dom/media/mediasource/SourceBufferTask.h
dom/media/mediasource/TrackBuffersManager.cpp
dom/media/mediasource/TrackBuffersManager.h
--- a/dom/media/mediasource/SourceBufferTask.h
+++ b/dom/media/mediasource/SourceBufferTask.h
@@ -18,17 +18,18 @@ namespace mozilla {
 class SourceBufferTask {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SourceBufferTask);
   enum class Type  {
     AppendBuffer,
     Abort,
     Reset,
     RangeRemoval,
-    EvictData
+    EvictData,
+    Detach
   };
 
   typedef Pair<bool, SourceBufferAttributes> AppendBufferResult;
   typedef MozPromise<AppendBufferResult, nsresult, /* IsExclusive = */ true> AppendPromise;
   typedef MozPromise<bool, nsresult, /* IsExclusive = */ true> RangeRemovalPromise;
 
   virtual Type GetType() const = 0;
 
@@ -93,11 +94,17 @@ public:
 
   static const Type sType = Type::EvictData;
   Type GetType() const override { return Type::EvictData; }
 
   media::TimeUnit mPlaybackTime;
   int64_t mSizeToEvict;
 };
 
+class DetachTask : public SourceBufferTask {
+public:
+  static const Type sType = Type::Detach;
+  Type GetType() const override { return Type::Detach; }
+};
+
 } // end mozilla namespace
 
 #endif
\ No newline at end of file
--- a/dom/media/mediasource/TrackBuffersManager.cpp
+++ b/dom/media/mediasource/TrackBuffersManager.cpp
@@ -94,17 +94,16 @@ TrackBuffersManager::TrackBuffersManager
   , mNewMediaSegmentStarted(false)
   , mActiveTrack(false)
   , mType(aType)
   , mParser(ContainerParser::CreateForMIMEType(aType))
   , mProcessedInput(0)
   , mTaskQueue(aParentDecoder->GetDemuxer()->GetTaskQueue())
   , mParentDecoder(new nsMainThreadPtrHolder<MediaSourceDecoder>(aParentDecoder, false /* strict */))
   , mEnded(false)
-  , mDetached(false)
   , mVideoEvictionThreshold(Preferences::GetUint("media.mediasource.eviction_threshold.video",
                                                  100 * 1024 * 1024))
   , mAudioEvictionThreshold(Preferences::GetUint("media.mediasource.eviction_threshold.audio",
                                                  30 * 1024 * 1024))
   , mEvictionOccurred(false)
   , mMonitor("TrackBuffersManager")
 {
   MOZ_ASSERT(NS_IsMainThread(), "Must be instanciated on the main thread");
@@ -157,20 +156,16 @@ TrackBuffersManager::QueueTask(SourceBuf
 }
 
 void
 TrackBuffersManager::ProcessTasks()
 {
   MOZ_ASSERT(OnTaskQueue());
   typedef SourceBufferTask::Type Type;
 
-  if (mDetached) {
-    return;
-  }
-
   if (mCurrentTask) {
     // Already have a task pending. ProcessTask will be scheduled once the
     // current task complete.
     return;
   }
   RefPtr<SourceBufferTask> task = mQueue.Pop();
   if (!task) {
     // nothing to do.
@@ -203,32 +198,36 @@ TrackBuffersManager::ProcessTasks()
                   task->As<EvictDataTask>()->mSizeToEvict);
       break;
     case Type::Abort:
       // not handled yet, and probably never.
       break;
     case Type::Reset:
       CompleteResetParserState();
       break;
+    case Type::Detach:
+      mTaskQueue = nullptr;
+      MOZ_DIAGNOSTIC_ASSERT(mQueue.Length() == 0,
+                            "Detach task must be the last");
+      return;
     default:
       NS_WARNING("Invalid Task");
   }
   GetTaskQueue()->Dispatch(NewRunnableMethod(this, &TrackBuffersManager::ProcessTasks));
 }
 
 // A PromiseHolder will assert upon destruction if it has a pending promise
 // that hasn't been completed. It is possible that a task didn't get processed
 // due to the owning SourceBuffer having shutdown.
 // We resolve/reject all pending promises and remove all pending tasks from the
 // queue.
 void
 TrackBuffersManager::CancelAllTasks()
 {
   typedef SourceBufferTask::Type Type;
-  MOZ_DIAGNOSTIC_ASSERT(mDetached);
 
   if (mCurrentTask) {
     mQueue.Push(mCurrentTask);
     mCurrentTask = nullptr;
   }
 
   RefPtr<SourceBufferTask> task;
   while ((task = mQueue.Pop())) {
@@ -383,17 +382,17 @@ TrackBuffersManager::Ended()
   mEnded = true;
 }
 
 void
 TrackBuffersManager::Detach()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MSE_DEBUG("");
-  mDetached = true;
+  QueueTask(new DetachTask());
 }
 
 void
 TrackBuffersManager::CompleteResetParserState()
 {
   MOZ_ASSERT(OnTaskQueue());
   MSE_DEBUG("");
 
@@ -752,54 +751,43 @@ TrackBuffersManager::SegmentParserLoop()
     }
   }
 }
 
 void
 TrackBuffersManager::NeedMoreData()
 {
   MSE_DEBUG("");
-  if (mDetached) {
-    // We've been detached.
-    return;
-  }
   MOZ_DIAGNOSTIC_ASSERT(mCurrentTask && mCurrentTask->GetType() == SourceBufferTask::Type::AppendBuffer);
   MOZ_DIAGNOSTIC_ASSERT(mSourceBufferAttributes);
 
   mCurrentTask->As<AppendBufferTask>()->mPromise.Resolve(
     SourceBufferTask::AppendBufferResult(mActiveTrack,
                                          *mSourceBufferAttributes),
                                          __func__);
   mSourceBufferAttributes = nullptr;
   mCurrentTask = nullptr;
   ProcessTasks();
 }
 
 void
 TrackBuffersManager::RejectAppend(nsresult aRejectValue, const char* aName)
 {
   MSE_DEBUG("rv=%d", aRejectValue);
-  if (mDetached) {
-    // We've been detached.
-    return;
-  }
   MOZ_DIAGNOSTIC_ASSERT(mCurrentTask && mCurrentTask->GetType() == SourceBufferTask::Type::AppendBuffer);
 
   mCurrentTask->As<AppendBufferTask>()->mPromise.Reject(aRejectValue, __func__);
   mSourceBufferAttributes = nullptr;
   mCurrentTask = nullptr;
   ProcessTasks();
 }
 
 void
 TrackBuffersManager::ScheduleSegmentParserLoop()
 {
-  if (mDetached) {
-    return;
-  }
   GetTaskQueue()->Dispatch(NewRunnableMethod(this, &TrackBuffersManager::SegmentParserLoop));
 }
 
 void
 TrackBuffersManager::ShutdownDemuxers()
 {
   if (mVideoTracks.mDemuxer) {
     mVideoTracks.mDemuxer->BreakCycles();
--- a/dom/media/mediasource/TrackBuffersManager.h
+++ b/dom/media/mediasource/TrackBuffersManager.h
@@ -392,19 +392,16 @@ private:
   // mSourceBufferAttributes.mAppendWindowStart/End
   media::TimeInterval mAppendWindow;
 
   // Strong references to external objects.
   nsMainThreadPtrHandle<MediaSourceDecoder> mParentDecoder;
 
   // Set to true if mediasource state changed to ended.
   Atomic<bool> mEnded;
-  // Set to true if the parent SourceBuffer has shutdown.
-  // We will not reschedule or process new task once mDetached is set.
-  Atomic<bool> mDetached;
 
   // Global size of this source buffer content.
   Atomic<int64_t> mSizeSourceBuffer;
   const int64_t mVideoEvictionThreshold;
   const int64_t mAudioEvictionThreshold;
   Atomic<bool> mEvictionOccurred;
 
   // Monitor to protect following objects accessed across multipple threads.