mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1125776: Part10. Handle concurrent aborts. r=mattwoodrow
Under some circumstances, it was possible for a cancelled queued task to run and the following one would have been cancelled. Also add more robust handling in cancelling the trackbuffer initialization task.
This commit is contained in:
parent
882199f026
commit
00bc1aec0d
@ -248,11 +248,11 @@ MediaSource::RemoveSourceBuffer(SourceBuffer& aSourceBuffer, ErrorResult& aRv)
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
|
||||
return;
|
||||
}
|
||||
if (sourceBuffer->Updating()) {
|
||||
// TODO:
|
||||
// abort stream append loop (if running)
|
||||
sourceBuffer->Abort();
|
||||
}
|
||||
|
||||
sourceBuffer->AbortBufferAppend();
|
||||
// TODO:
|
||||
// abort stream append loop (if running)
|
||||
|
||||
// TODO:
|
||||
// For all sourceBuffer audioTracks, videoTracks, textTracks:
|
||||
// set sourceBuffer to null
|
||||
|
@ -46,16 +46,18 @@ class AppendDataRunnable : public nsRunnable {
|
||||
public:
|
||||
AppendDataRunnable(SourceBuffer* aSourceBuffer,
|
||||
LargeDataBuffer* aData,
|
||||
double aTimestampOffset)
|
||||
double aTimestampOffset,
|
||||
uint32_t aUpdateID)
|
||||
: mSourceBuffer(aSourceBuffer)
|
||||
, mData(aData)
|
||||
, mTimestampOffset(aTimestampOffset)
|
||||
, mUpdateID(aUpdateID)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() MOZ_OVERRIDE MOZ_FINAL {
|
||||
|
||||
mSourceBuffer->AppendData(mData, mTimestampOffset);
|
||||
mSourceBuffer->AppendData(mData, mTimestampOffset, mUpdateID);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
@ -64,6 +66,7 @@ private:
|
||||
nsRefPtr<SourceBuffer> mSourceBuffer;
|
||||
nsRefPtr<LargeDataBuffer> mData;
|
||||
double mTimestampOffset;
|
||||
uint32_t mUpdateID;
|
||||
};
|
||||
|
||||
class RangeRemovalRunnable : public nsRunnable {
|
||||
@ -214,22 +217,23 @@ SourceBuffer::Abort(ErrorResult& aRv)
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
Abort();
|
||||
AbortBufferAppend();
|
||||
mTrackBuffer->ResetParserState();
|
||||
mAppendWindowStart = 0;
|
||||
mAppendWindowEnd = PositiveInfinity<double>();
|
||||
|
||||
// Discard the current decoder so no new data will be added to it.
|
||||
MSE_DEBUG("SourceBuffer(%p)::Abort() Discarding decoder", this);
|
||||
mTrackBuffer->DiscardDecoder();
|
||||
mTrackBuffer->DiscardCurrentDecoder();
|
||||
}
|
||||
|
||||
void
|
||||
SourceBuffer::Abort()
|
||||
SourceBuffer::AbortBufferAppend()
|
||||
{
|
||||
if (mUpdating) {
|
||||
// abort any pending buffer append.
|
||||
mTrackBuffer->Abort();
|
||||
mPendingAppend.DisconnectIfExists();
|
||||
// TODO: Abort segment parser loop, and stream append loop algorithms.
|
||||
// cancel any pending buffer append.
|
||||
mTrackBuffer->AbortAppendData();
|
||||
AbortUpdating();
|
||||
}
|
||||
}
|
||||
@ -291,7 +295,7 @@ SourceBuffer::Detach()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MSE_DEBUG("SourceBuffer(%p)::Detach", this);
|
||||
Abort();
|
||||
AbortBufferAppend();
|
||||
if (mTrackBuffer) {
|
||||
mTrackBuffer->Detach();
|
||||
}
|
||||
@ -316,6 +320,7 @@ SourceBuffer::SourceBuffer(MediaSource* aMediaSource, const nsACString& aType)
|
||||
, mTimestampOffset(0)
|
||||
, mAppendMode(SourceBufferAppendMode::Segments)
|
||||
, mUpdating(false)
|
||||
, mUpdateID(0)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aMediaSource);
|
||||
@ -367,6 +372,7 @@ SourceBuffer::StartUpdating()
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(!mUpdating);
|
||||
mUpdating = true;
|
||||
mUpdateID++;
|
||||
QueueAsyncSimpleEvent("updatestart");
|
||||
}
|
||||
|
||||
@ -424,14 +430,15 @@ SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aR
|
||||
MOZ_ASSERT(mAppendMode == SourceBufferAppendMode::Segments,
|
||||
"We don't handle timestampOffset for sequence mode yet");
|
||||
nsRefPtr<nsIRunnable> task =
|
||||
new AppendDataRunnable(this, data, mTimestampOffset);
|
||||
new AppendDataRunnable(this, data, mTimestampOffset, mUpdateID);
|
||||
NS_DispatchToMainThread(task);
|
||||
}
|
||||
|
||||
void
|
||||
SourceBuffer::AppendData(LargeDataBuffer* aData, double aTimestampOffset)
|
||||
SourceBuffer::AppendData(LargeDataBuffer* aData, double aTimestampOffset,
|
||||
uint32_t aUpdateID)
|
||||
{
|
||||
if (!mUpdating) {
|
||||
if (!mUpdating || aUpdateID != mUpdateID) {
|
||||
// The buffer append algorithm has been interrupted by abort().
|
||||
//
|
||||
// If the sequence appendBuffer(), abort(), appendBuffer() occurs before
|
||||
@ -442,21 +449,23 @@ SourceBuffer::AppendData(LargeDataBuffer* aData, double aTimestampOffset)
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mMediaSource);
|
||||
MOZ_ASSERT(!mPendingAppend.Exists());
|
||||
|
||||
if (!aData->Length()) {
|
||||
StopUpdating();
|
||||
return;
|
||||
}
|
||||
|
||||
mTrackBuffer->AppendData(aData, aTimestampOffset * USECS_PER_S)
|
||||
->Then(NS_GetCurrentThread(), __func__, this,
|
||||
&SourceBuffer::AppendDataCompletedWithSuccess,
|
||||
&SourceBuffer::AppendDataErrored);
|
||||
mPendingAppend.Begin(mTrackBuffer->AppendData(aData, aTimestampOffset * USECS_PER_S)
|
||||
->RefableThen(NS_GetCurrentThread(), __func__, this,
|
||||
&SourceBuffer::AppendDataCompletedWithSuccess,
|
||||
&SourceBuffer::AppendDataErrored));
|
||||
}
|
||||
|
||||
void
|
||||
SourceBuffer::AppendDataCompletedWithSuccess(bool aGotMedia)
|
||||
{
|
||||
mPendingAppend.Complete();
|
||||
if (!mUpdating) {
|
||||
// The buffer append algorithm has been interrupted by abort().
|
||||
return;
|
||||
@ -476,10 +485,11 @@ SourceBuffer::AppendDataCompletedWithSuccess(bool aGotMedia)
|
||||
void
|
||||
SourceBuffer::AppendDataErrored(nsresult aError)
|
||||
{
|
||||
mPendingAppend.Complete();
|
||||
switch (aError) {
|
||||
case NS_ERROR_ABORT:
|
||||
// Nothing further to do as the handling of the events is being done
|
||||
// by Abort().
|
||||
// Nothing further to do as the trackbuffer has been shutdown.
|
||||
// or append was aborted and abort() has handled all the events.
|
||||
break;
|
||||
default:
|
||||
AppendError(true);
|
||||
|
@ -7,6 +7,7 @@
|
||||
#ifndef mozilla_dom_SourceBuffer_h_
|
||||
#define mozilla_dom_SourceBuffer_h_
|
||||
|
||||
#include "MediaPromise.h"
|
||||
#include "MediaSource.h"
|
||||
#include "js/RootingAPI.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
@ -32,6 +33,7 @@ class ErrorResult;
|
||||
class LargeDataBuffer;
|
||||
class TrackBuffer;
|
||||
template <typename T> class AsyncEventRunner;
|
||||
typedef MediaPromise<bool, nsresult, /* IsExclusive = */ true> TrackBufferAppendPromise;
|
||||
|
||||
namespace dom {
|
||||
|
||||
@ -80,7 +82,7 @@ public:
|
||||
void AppendBuffer(const ArrayBufferView& aData, ErrorResult& aRv);
|
||||
|
||||
void Abort(ErrorResult& aRv);
|
||||
void Abort();
|
||||
void AbortBufferAppend();
|
||||
|
||||
void Remove(double aStart, double aEnd, ErrorResult& aRv);
|
||||
/** End WebIDL Methods. */
|
||||
@ -139,7 +141,8 @@ private:
|
||||
|
||||
// Shared implementation of AppendBuffer overloads.
|
||||
void AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv);
|
||||
void AppendData(LargeDataBuffer* aData, double aTimestampOffset);
|
||||
void AppendData(LargeDataBuffer* aData, double aTimestampOffset,
|
||||
uint32_t aAppendID);
|
||||
|
||||
// Implement the "Append Error Algorithm".
|
||||
// Will call endOfStream() with "decode" error if aDecodeError is true.
|
||||
@ -169,6 +172,13 @@ private:
|
||||
|
||||
SourceBufferAppendMode mAppendMode;
|
||||
bool mUpdating;
|
||||
|
||||
// Each time mUpdating is set to true, mUpdateID will be incremented.
|
||||
// This allows for a queued AppendData task to identify if it was earlier
|
||||
// aborted and another AppendData queued.
|
||||
uint32_t mUpdateID;
|
||||
|
||||
MediaPromiseConsumerHolder<TrackBufferAppendPromise> mPendingAppend;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
@ -122,6 +122,7 @@ TrackBuffer::Shutdown()
|
||||
{
|
||||
mParentDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
|
||||
mShutdown = true;
|
||||
mInitializationPromise.RejectIfExists(NS_ERROR_ABORT, __func__);
|
||||
|
||||
MOZ_ASSERT(mShutdownPromise.IsEmpty());
|
||||
nsRefPtr<ShutdownPromise> p = mShutdownPromise.Ensure(__func__);
|
||||
@ -148,18 +149,21 @@ TrackBuffer::ContinueShutdown()
|
||||
return;
|
||||
}
|
||||
|
||||
mCurrentDecoder = nullptr;
|
||||
mInitializedDecoders.Clear();
|
||||
mParentDecoder = nullptr;
|
||||
|
||||
mShutdownPromise.Resolve(true, __func__);
|
||||
}
|
||||
|
||||
nsRefPtr<TrackBuffer::InitializationPromise>
|
||||
nsRefPtr<TrackBufferAppendPromise>
|
||||
TrackBuffer::AppendData(LargeDataBuffer* aData, int64_t aTimestampOffset)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mInitializationPromise.IsEmpty());
|
||||
|
||||
DecodersToInitialize decoders(this);
|
||||
nsRefPtr<InitializationPromise> p = mInitializationPromise.Ensure(__func__);
|
||||
nsRefPtr<TrackBufferAppendPromise> p = mInitializationPromise.Ensure(__func__);
|
||||
bool hadInitData = mParser->HasInitData();
|
||||
bool hadCompleteInitData = mParser->HasCompleteInitData();
|
||||
nsRefPtr<LargeDataBuffer> oldInit = mParser->InitData();
|
||||
@ -439,7 +443,7 @@ TrackBuffer::NewDecoder(int64_t aTimestampOffset)
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mParentDecoder);
|
||||
|
||||
DiscardDecoder();
|
||||
DiscardCurrentDecoder();
|
||||
|
||||
nsRefPtr<SourceBufferDecoder> decoder = mParentDecoder->CreateSubDecoder(mType, aTimestampOffset);
|
||||
if (!decoder) {
|
||||
@ -488,13 +492,21 @@ TrackBuffer::InitializeDecoder(SourceBufferDecoder* aDecoder)
|
||||
mParentDecoder->GetReentrantMonitor().AssertNotCurrentThreadIn();
|
||||
ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
|
||||
|
||||
if (mCurrentDecoder != aDecoder) {
|
||||
MSE_DEBUG("TrackBuffer(%p) append was cancelled. Aborting initialization.",
|
||||
this);
|
||||
// If we reached this point, the SourceBuffer would have disconnected
|
||||
// the promise. So no need to reject it.
|
||||
return;
|
||||
}
|
||||
|
||||
// We may be shut down at any time by the reader on another thread. So we need
|
||||
// to check for this each time we acquire the monitor. If that happens, we
|
||||
// need to abort immediately, because the reader has forgotten about us, and
|
||||
// important pieces of our state (like mTaskQueue) have also been torn down.
|
||||
if (mShutdown) {
|
||||
MSE_DEBUG("TrackBuffer(%p) was shut down. Aborting initialization.", this);
|
||||
mInitializationPromise.RejectIfExists(NS_ERROR_ABORT, __func__);
|
||||
RemoveDecoder(aDecoder);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -532,7 +544,6 @@ TrackBuffer::InitializeDecoder(SourceBufferDecoder* aDecoder)
|
||||
reader->SetIdle();
|
||||
if (mShutdown) {
|
||||
MSE_DEBUG("TrackBuffer(%p) was shut down while reading metadata. Aborting initialization.", this);
|
||||
mInitializationPromise.RejectIfExists(NS_ERROR_ABORT, __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -577,10 +588,17 @@ void
|
||||
TrackBuffer::CompleteInitializeDecoder(SourceBufferDecoder* aDecoder)
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
|
||||
if (mCurrentDecoder != aDecoder) {
|
||||
MSE_DEBUG("TrackBuffer(%p) append was cancelled. Aborting initialization.",
|
||||
this);
|
||||
// If we reached this point, the SourceBuffer would have disconnected
|
||||
// the promise. So no need to reject it.
|
||||
return;
|
||||
}
|
||||
|
||||
if (mShutdown) {
|
||||
MSE_DEBUG("TrackBuffer(%p) was shut down while reading metadata. Aborting initialization.", this);
|
||||
MSE_DEBUG("TrackBuffer(%p) was shut down. Aborting initialization.", this);
|
||||
RemoveDecoder(aDecoder);
|
||||
mInitializationPromise.RejectIfExists(NS_ERROR_ABORT, __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -648,12 +666,10 @@ TrackBuffer::RegisterDecoder(SourceBufferDecoder* aDecoder)
|
||||
}
|
||||
|
||||
void
|
||||
TrackBuffer::DiscardDecoder()
|
||||
TrackBuffer::DiscardCurrentDecoder()
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
|
||||
if (mCurrentDecoder) {
|
||||
mCurrentDecoder->GetResource()->Ended();
|
||||
}
|
||||
EndCurrentDecoder();
|
||||
mCurrentDecoder = nullptr;
|
||||
}
|
||||
|
||||
@ -671,10 +687,8 @@ TrackBuffer::Detach()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (mCurrentDecoder) {
|
||||
DiscardDecoder();
|
||||
DiscardCurrentDecoder();
|
||||
}
|
||||
// Cancel the promise should the current decoder hadn't be initialized yet.
|
||||
mInitializationPromise.RejectIfExists(NS_ERROR_ABORT, __func__);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -741,13 +755,17 @@ TrackBuffer::ResetParserState()
|
||||
// We have an incomplete init segment pending. reset current parser and
|
||||
// discard the current decoder.
|
||||
mParser = ContainerParser::CreateForMIMEType(mType);
|
||||
DiscardDecoder();
|
||||
DiscardCurrentDecoder();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TrackBuffer::Abort()
|
||||
TrackBuffer::AbortAppendData()
|
||||
{
|
||||
DiscardCurrentDecoder();
|
||||
// The SourceBuffer would have disconnected its promise.
|
||||
// However we must ensure that the MediaPromiseHolder handle all pending
|
||||
// promises.
|
||||
mInitializationPromise.RejectIfExists(NS_ERROR_ABORT, __func__);
|
||||
}
|
||||
|
||||
@ -839,7 +857,7 @@ TrackBuffer::RemoveDecoder(SourceBufferDecoder* aDecoder)
|
||||
mDecoders.RemoveElement(aDecoder);
|
||||
|
||||
if (mCurrentDecoder == aDecoder) {
|
||||
DiscardDecoder();
|
||||
DiscardCurrentDecoder();
|
||||
}
|
||||
}
|
||||
aDecoder->GetReader()->GetTaskQueue()->Dispatch(task);
|
||||
|
@ -7,8 +7,8 @@
|
||||
#ifndef MOZILLA_TRACKBUFFER_H_
|
||||
#define MOZILLA_TRACKBUFFER_H_
|
||||
|
||||
#include "SourceBuffer.h"
|
||||
#include "SourceBufferDecoder.h"
|
||||
#include "MediaPromise.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/mozalloc.h"
|
||||
@ -33,8 +33,6 @@ class TrackBuffer MOZ_FINAL {
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TrackBuffer);
|
||||
|
||||
typedef MediaPromise<bool, nsresult, /* IsExclusive = */ false> InitializationPromise;
|
||||
|
||||
TrackBuffer(MediaSourceDecoder* aParentDecoder, const nsACString& aType);
|
||||
|
||||
nsRefPtr<ShutdownPromise> Shutdown();
|
||||
@ -42,8 +40,8 @@ public:
|
||||
// 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.
|
||||
nsRefPtr<InitializationPromise> AppendData(LargeDataBuffer* aData,
|
||||
int64_t aTimestampOffset /* microseconds */);
|
||||
nsRefPtr<TrackBufferAppendPromise> AppendData(LargeDataBuffer* aData,
|
||||
int64_t aTimestampOffset /* microseconds */);
|
||||
|
||||
// Evicts data held in the current decoders SourceBufferResource from the
|
||||
// start of the buffer through to aPlaybackTime. aThreshold is used to
|
||||
@ -64,7 +62,7 @@ public:
|
||||
|
||||
// Mark the current decoder's resource as ended, clear mCurrentDecoder and
|
||||
// reset mLast{Start,End}Timestamp.
|
||||
void DiscardDecoder();
|
||||
void DiscardCurrentDecoder();
|
||||
// Mark the current decoder's resource as ended.
|
||||
void EndCurrentDecoder();
|
||||
|
||||
@ -104,7 +102,7 @@ public:
|
||||
bool RangeRemoval(int64_t aStart, int64_t aEnd);
|
||||
|
||||
// Abort any pending appendBuffer by rejecting any pending promises.
|
||||
void Abort();
|
||||
void AbortAppendData();
|
||||
|
||||
#ifdef MOZ_EME
|
||||
nsresult SetCDMProxy(CDMProxy* aProxy);
|
||||
@ -205,7 +203,7 @@ private:
|
||||
bool mDecoderPerSegment;
|
||||
bool mShutdown;
|
||||
|
||||
MediaPromiseHolder<InitializationPromise> mInitializationPromise;
|
||||
MediaPromiseHolder<TrackBufferAppendPromise> mInitializationPromise;
|
||||
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user