Bug 462878. Ensure that nsMediaStream::Close is only called on the main thread, and prevent reentrant Stop calls. r+sr=roc,a=beltzner

This commit is contained in:
Chris Pearce 2008-11-18 21:19:32 +13:00
Родитель 92008e034e
Коммит 0a768b1b83
4 изменённых файлов: 58 добавлений и 48 удалений

Просмотреть файл

@ -333,7 +333,7 @@ class nsOggDecoder : public nsMediaDecoder
// Called if the media file encounters a network error.
// Call on the main thread only.
void NetworkError();
virtual void NetworkError();
// Call from any thread safely. Return PR_TRUE if we are currently
// seeking in the media resource.
@ -348,21 +348,20 @@ class nsOggDecoder : public nsMediaDecoder
// Return PR_TRUE if seeking is supported.
virtual PRBool GetSeekable();
// Returns the channel reader.
nsChannelReader* GetReader() { return mReader; }
protected:
// Change to a new play state. This updates the mState variable and
// notifies any thread blocking on this objects monitor of the
// change. Can be called on any thread.
void ChangeState(PlayState aState);
// Returns the monitor for other threads to synchronise access to
// state
// state.
PRMonitor* GetMonitor()
{
return mMonitor;
}
// Return the current state. The caller must have entered the
// monitor.
// Return the current state. Can be called on any thread. If called from
// a non-main thread, the decoder monitor must be held.
PlayState GetState()
{
return mPlayState;
@ -373,6 +372,11 @@ protected:
* thread.
******/
// Change to a new play state. This updates the mState variable and
// notifies any thread blocking on this object's monitor of the
// change. Call on the main thread only.
void ChangeState(PlayState aState);
// Called when the metadata from the Ogg file has been read.
// Call on the main thread only.
void MetadataLoaded();
@ -486,7 +490,7 @@ private:
// the decoder thread, and the state machine for that thread keeps
// a pointer to this reader. This is safe as the only methods called
// are threadsafe (via the threadsafe nsMediaStream).
nsChannelReader* mReader;
nsAutoPtr<nsChannelReader> mReader;
// Monitor for detecting when the video play state changes. A call
// to Wait on this monitor will block the thread until the next
@ -499,11 +503,16 @@ private:
// must call NotifyAll on the monitor so the decode thread can wake up.
PlayState mPlayState;
// The state to change to after a seek or load operation. It is
// protected by the monitor mMonitor. This monitor must be acquired
// when reading or writing the state. Any change to the state must
// call NotifyAll on the monitor.
PlayState mNextState;
// The state to change to after a seek or load operation. It must only
// be changed from the main thread. The decoder monitor must be acquired
// when writing to the state, or when reading from a non-main thread.
// Any change to the state must call NotifyAll on the monitor.
PlayState mNextState;
// Flags if we've called Stop(). Prevents multiple events being
// sent to call Shutdown(). Accessed on the main thread
// only.
PRPackedBool mIsStopping;
};
#endif

Просмотреть файл

@ -71,7 +71,6 @@ OggPlayErrorCode nsChannelReader::initialise(int aBlock)
OggPlayErrorCode nsChannelReader::destroy()
{
mStream.Close();
return E_OGGPLAY_OK;
}
@ -113,9 +112,7 @@ static OggPlayErrorCode oggplay_channel_reader_initialise(OggPlayReader* aReader
static OggPlayErrorCode oggplay_channel_reader_destroy(OggPlayReader* aReader)
{
nsChannelReader* me = static_cast<nsChannelReader*>(aReader);
OggPlayErrorCode result = me->destroy();
delete me;
return result;
return me->destroy();
}
static size_t oggplay_channel_reader_io_read(void* aReader, void* aBuffer, size_t aCount)

Просмотреть файл

@ -128,13 +128,12 @@ nsresult nsDefaultStreamStrategy::Close()
if (mChannel) {
mChannel->Cancel(NS_BINDING_ABORTED);
mChannel = nsnull;
}
if (mPipeInput) {
mPipeInput->Close();
mPipeInput = nsnull;
mListener = nsnull;
}
mListener = nsnull;
return NS_OK;
}
@ -466,13 +465,12 @@ nsresult nsHttpStreamStrategy::Close()
if (mChannel) {
mChannel->Cancel(NS_BINDING_ABORTED);
mChannel = nsnull;
}
if (mPipeInput) {
mPipeInput->Close();
mPipeInput = nsnull;
mListener = nsnull;
}
mListener = nsnull;
return NS_OK;
}

Просмотреть файл

@ -215,7 +215,7 @@ public:
DECODER_STATE_SHUTDOWN
};
nsOggDecodeStateMachine(nsOggDecoder* aDecoder, nsChannelReader* aReader);
nsOggDecodeStateMachine(nsOggDecoder* aDecoder);
~nsOggDecodeStateMachine();
// Cause state transitions. These methods obtain the decoder monitor
@ -389,13 +389,6 @@ private:
PRInt32 mAudioChannels;
PRInt32 mAudioTrack;
// Channel Reader. Originally created by the mDecoder object, it is
// destroyed when we close the mPlayer handle in the
// destructor. Used to obtain download and playback rate information
// for buffering. Synchronisation for those methods are handled by
// nsMediaStream.
nsChannelReader* mReader;
// Time that buffering started. Used for buffering timeout and only
// accessed in the decoder thread.
PRIntervalTime mBufferingStart;
@ -461,7 +454,7 @@ private:
PRPackedBool mPositionChangeQueued;
};
nsOggDecodeStateMachine::nsOggDecodeStateMachine(nsOggDecoder* aDecoder, nsChannelReader* aReader) :
nsOggDecodeStateMachine::nsOggDecodeStateMachine(nsOggDecoder* aDecoder) :
mDecoder(aDecoder),
mPlayer(0),
mPlayStartTime(0),
@ -474,7 +467,6 @@ nsOggDecodeStateMachine::nsOggDecodeStateMachine(nsOggDecoder* aDecoder, nsChann
mAudioRate(0),
mAudioChannels(0),
mAudioTrack(-1),
mReader(aReader),
mBufferingStart(0),
mBufferingBytes(0),
mLastFrameTime(0),
@ -494,11 +486,9 @@ nsOggDecodeStateMachine::~nsOggDecodeStateMachine()
while (!mDecodedFrames.IsEmpty()) {
delete mDecodedFrames.Pop();
}
oggplay_close(mPlayer);
}
OggPlayErrorCode nsOggDecodeStateMachine::DecodeFrame()
{
NS_ASSERTION(mState > DECODER_STATE_DECODING_METADATA, "DecodeFrame() called during invalid state");
@ -853,6 +843,8 @@ void nsOggDecodeStateMachine::Seek(float aTime)
nsresult nsOggDecodeStateMachine::Run()
{
nsChannelReader* reader = mDecoder->GetReader();
NS_ENSURE_TRUE(reader, NS_ERROR_NULL_POINTER);
while (PR_TRUE) {
nsAutoMonitor mon(mDecoder->GetMonitor());
switch(mState) {
@ -902,8 +894,8 @@ nsresult nsOggDecodeStateMachine::Run()
case DECODER_STATE_DECODING:
{
// Before decoding check if we should buffer more data
if (mReader->DownloadRate() >= 0 &&
mReader->Available() < mReader->PlaybackRate() * BUFFERING_SECONDS_LOW_WATER_MARK) {
if (reader->DownloadRate() >= 0 &&
reader->Available() < reader->PlaybackRate() * BUFFERING_SECONDS_LOW_WATER_MARK) {
if (mDecoder->GetState() == nsOggDecoder::PLAY_STATE_PLAYING) {
if (mPlaying) {
StopPlayback();
@ -911,7 +903,7 @@ nsresult nsOggDecodeStateMachine::Run()
}
mBufferingStart = PR_IntervalNow();
mBufferingBytes = PRUint32(BUFFERING_RATE(mReader->PlaybackRate()) * BUFFERING_WAIT);
mBufferingBytes = PRUint32(BUFFERING_RATE(reader->PlaybackRate()) * BUFFERING_WAIT);
mState = DECODER_STATE_BUFFERING;
nsCOMPtr<nsIRunnable> event =
@ -1009,11 +1001,11 @@ nsresult nsOggDecodeStateMachine::Run()
case DECODER_STATE_BUFFERING:
if ((PR_IntervalToMilliseconds(PR_IntervalNow() - mBufferingStart) < BUFFERING_WAIT*1000) &&
mReader->DownloadRate() >= 0 &&
mReader->Available() < mBufferingBytes) {
reader->DownloadRate() >= 0 &&
reader->Available() < mBufferingBytes) {
LOG(PR_LOG_DEBUG,
("Buffering data until %d bytes available or %d milliseconds",
mBufferingBytes - mReader->Available(),
mBufferingBytes - reader->Available(),
BUFFERING_WAIT*1000 - (PR_IntervalToMilliseconds(PR_IntervalNow() - mBufferingStart))));
mon.Wait(PR_MillisecondsToInterval(1000));
if (mState == DECODER_STATE_SHUTDOWN)
@ -1069,7 +1061,7 @@ nsresult nsOggDecodeStateMachine::Run()
void nsOggDecodeStateMachine::LoadOggHeaders()
{
LOG(PR_LOG_DEBUG, ("Loading Ogg Headers"));
mPlayer = oggplay_open_with_reader(mReader);
mPlayer = oggplay_open_with_reader(mDecoder->GetReader());
if (mPlayer) {
LOG(PR_LOG_DEBUG, ("There are %d tracks", oggplay_get_num_tracks(mPlayer)));
@ -1184,7 +1176,8 @@ nsOggDecoder::nsOggDecoder() :
mReader(0),
mMonitor(0),
mPlayState(PLAY_STATE_PAUSED),
mNextState(PLAY_STATE_PAUSED)
mNextState(PLAY_STATE_PAUSED),
mIsStopping(PR_FALSE)
{
MOZ_COUNT_CTOR(nsOggDecoder);
}
@ -1239,6 +1232,10 @@ nsOggDecoder::~nsOggDecoder()
nsresult nsOggDecoder::Load(nsIURI* aURI, nsIChannel* aChannel,
nsIStreamListener** aStreamListener)
{
// Reset Stop guard flag flag, else shutdown won't occur properly when
// reusing decoder.
mIsStopping = PR_FALSE;
if (aStreamListener) {
*aStreamListener = nsnull;
}
@ -1270,7 +1267,7 @@ nsresult nsOggDecoder::Load(nsIURI* aURI, nsIChannel* aChannel,
rv = NS_NewThread(getter_AddRefs(mDecodeThread));
NS_ENSURE_SUCCESS(rv, rv);
mDecodeStateMachine = new nsOggDecodeStateMachine(this, mReader);
mDecodeStateMachine = new nsOggDecodeStateMachine(this);
{
nsAutoMonitor mon(mMonitor);
mDecodeStateMachine->SetContentLength(mContentLength);
@ -1326,6 +1323,13 @@ nsresult nsOggDecoder::PlaybackRateChanged()
void nsOggDecoder::Stop()
{
NS_ASSERTION(NS_IsMainThread(),
"nsOggDecoder::Stop called on non-main thread");
if (mIsStopping)
return;
mIsStopping = PR_TRUE;
ChangeState(PLAY_STATE_ENDED);
StopProgress();
@ -1334,7 +1338,6 @@ void nsOggDecoder::Stop()
// to prevent shutdown from deadlocking.
if (mReader) {
mReader->Cancel();
mReader = nsnull;
}
// Shutdown must be on called the mDecodeStateMachine before deleting.
@ -1353,6 +1356,7 @@ void nsOggDecoder::Stop()
}
mDecodeStateMachine = nsnull;
mReader = nsnull;
UnregisterShutdownObserver();
}
@ -1549,6 +1553,8 @@ void nsOggDecoder::UnregisterShutdownObserver()
void nsOggDecoder::ChangeState(PlayState aState)
{
NS_ASSERTION(NS_IsMainThread(),
"nsOggDecoder::ChangeState called on non-main thread");
nsAutoMonitor mon(mMonitor);
if (mNextState == aState) {