Bug 1121669 - Add a mutex around mFlushTimer to deal with write appearing to other threads in an inconsistent order. r?jseward.
MozReview-Commit-ID: 56r9PsEf8Jv
--- a/parser/html/nsHtml5StreamParser.cpp
+++ b/parser/html/nsHtml5StreamParser.cpp
@@ -161,16 +161,17 @@ nsHtml5StreamParser::nsHtml5StreamParser
, mTokenizerMutex("nsHtml5StreamParser mTokenizerMutex")
, mOwner(aOwner)
, mSpeculationMutex("nsHtml5StreamParser mSpeculationMutex")
, mTerminatedMutex("nsHtml5StreamParser mTerminatedMutex")
, mThread(nsHtml5Module::GetStreamParserThread())
, mExecutorFlusher(new nsHtml5ExecutorFlusher(aExecutor))
, mLoadFlusher(new nsHtml5LoadFlusher(aExecutor))
, mFlushTimer(do_CreateInstance("@mozilla.org/timer;1"))
+ , mFlushTimerMutex("nsHtml5StreamParser mFlushTimerMutex")
, mMode(aMode)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
mFlushTimer->SetTarget(mThread);
#ifdef DEBUG
mAtomTable.SetPermittedLookupThread(mThread);
#endif
mTokenizer->setInterner(&mAtomTable);
@@ -201,18 +202,21 @@ nsHtml5StreamParser::nsHtml5StreamParser
// There's a zeroing operator new for everything else
}
nsHtml5StreamParser::~nsHtml5StreamParser()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
mTokenizer->end();
- NS_ASSERTION(!mFlushTimer, "Flush timer was not dropped before dtor!");
#ifdef DEBUG
+ {
+ mozilla::MutexAutoLock flushTimerLock(mFlushTimerMutex);
+ MOZ_ASSERT(!mFlushTimer, "Flush timer was not dropped before dtor!");
+ }
mRequest = nullptr;
mObserver = nullptr;
mUnicodeDecoder = nullptr;
mSniffingBuffer = nullptr;
mMetaScanner = nullptr;
mFirstBuffer = nullptr;
mExecutor = nullptr;
mTreeBuilder = nullptr;
@@ -1114,22 +1118,25 @@ nsHtml5StreamParser::DoDataAvailable(con
}
ParseAvailableData();
if (mFlushTimerArmed || mSpeculating) {
return;
}
- mFlushTimer->InitWithFuncCallback(nsHtml5StreamParser::TimerCallback,
- static_cast<void*> (this),
- mFlushTimerEverFired ?
- sTimerInitialDelay :
- sTimerSubsequentDelay,
- nsITimer::TYPE_ONE_SHOT);
+ {
+ mozilla::MutexAutoLock flushTimerLock(mFlushTimerMutex);
+ mFlushTimer->InitWithFuncCallback(nsHtml5StreamParser::TimerCallback,
+ static_cast<void*> (this),
+ mFlushTimerEverFired ?
+ sTimerInitialDelay :
+ sTimerSubsequentDelay,
+ nsITimer::TYPE_ONE_SHOT);
+ }
mFlushTimerArmed = true;
}
class nsHtml5DataAvailable : public Runnable
{
private:
nsHtml5RefPtr<nsHtml5StreamParser> mStreamParser;
UniquePtr<uint8_t[]> mData;
@@ -1309,17 +1316,20 @@ nsHtml5StreamParser::internalEncodingDec
void
nsHtml5StreamParser::FlushTreeOpsAndDisarmTimer()
{
NS_ASSERTION(IsParserThread(), "Wrong thread!");
if (mFlushTimerArmed) {
// avoid calling Cancel if the flush timer isn't armed to avoid acquiring
// a mutex
- mFlushTimer->Cancel();
+ {
+ mozilla::MutexAutoLock flushTimerLock(mFlushTimerMutex);
+ mFlushTimer->Cancel();
+ }
mFlushTimerArmed = false;
}
if (mMode == VIEW_SOURCE_HTML || mMode == VIEW_SOURCE_XML) {
mTokenizer->FlushViewSource();
}
mTreeBuilder->Flush();
nsCOMPtr<nsIRunnable> runnable(mExecutorFlusher);
if (NS_FAILED(mExecutor->GetDocument()->Dispatch("FlushTreeOpsAndDisarmTimer",
@@ -1624,16 +1634,17 @@ class nsHtml5TimerKungFu : public Runnab
private:
nsHtml5RefPtr<nsHtml5StreamParser> mStreamParser;
public:
explicit nsHtml5TimerKungFu(nsHtml5StreamParser* aStreamParser)
: mStreamParser(aStreamParser)
{}
NS_IMETHOD Run() override
{
+ mozilla::MutexAutoLock flushTimerLock(mStreamParser->mFlushTimerMutex);
if (mStreamParser->mFlushTimer) {
mStreamParser->mFlushTimer->Cancel();
mStreamParser->mFlushTimer = nullptr;
}
return NS_OK;
}
};
@@ -1654,16 +1665,17 @@ nsHtml5StreamParser::DropTimer()
* This DropTimer method addresses these issues. This method must be called
* on the main thread before the destructor of this class is reached.
* The nsHtml5TimerKungFu object has an nsHtml5RefPtr that addrefs this
* stream parser object to keep it alive until the runnable is done.
* The runnable cancels the timer on the parser thread, drops the timer
* and lets nsHtml5RefPtr send a runnable back to the main thread to
* release the stream parser.
*/
+ mozilla::MutexAutoLock flushTimerLock(mFlushTimerMutex);
if (mFlushTimer) {
nsCOMPtr<nsIRunnable> event = new nsHtml5TimerKungFu(this);
if (NS_FAILED(mThread->Dispatch(event, nsIThread::DISPATCH_NORMAL))) {
NS_WARNING("Failed to dispatch TimerKungFu event");
}
}
}
--- a/parser/html/nsHtml5StreamParser.h
+++ b/parser/html/nsHtml5StreamParser.h
@@ -541,16 +541,22 @@ class nsHtml5StreamParser : public nsICh
bool mInitialEncodingWasFromParentFrame;
/**
* Timer for flushing tree ops once in a while when not speculating.
*/
nsCOMPtr<nsITimer> mFlushTimer;
/**
+ * Mutex for protecting access to mFlushTimer (but not for the two
+ * mFlushTimerFoo booleans below).
+ */
+ mozilla::Mutex mFlushTimerMutex;
+
+ /**
* Keeps track whether mFlushTimer has been armed. Unfortunately,
* nsITimer doesn't enable querying this from the timer itself.
*/
bool mFlushTimerArmed;
/**
* False initially and true after the timer has fired at least once.
*/