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
--- 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.