Bug 1382329: Part 3 - Wait for pending parse tasks to finish before freeing scripts. r=erahm,nbp
Off-thread parse tasks depend on the memory allocated by the main-thread script preloader, so that memory needs to be kept alive until they finish. In normal use cases, this is guaranteed, but when the browser shuts down very quickly (as sometimes happens in tests), or we invalidate the startup caches in the middle of the startup process, they can sometimes be freed too early. MozReview-Commit-ID: GQmkVbWgTH9
This commit is contained in:
@@ -266,6 +266,14 @@ ScriptPreloader::Cleanup()
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for any pending parses to finish before clearing the mScripts
|
||||
// hashtable, since the parse tasks depend on memory allocated by those
|
||||
// scripts.
|
||||
{
|
||||
MonitorAutoLock mal(mMonitor);
|
||||
FinishPendingParses(mal);
|
||||
}
|
||||
|
||||
mScripts.Clear();
|
||||
|
||||
AutoSafeJSAPI jsapi;
|
||||
@@ -282,9 +290,17 @@ ScriptPreloader::InvalidateCache()
|
||||
|
||||
mCacheInvalidated = true;
|
||||
|
||||
mParsingScripts.clearAndFree();
|
||||
while (auto script = mPendingScripts.getFirst())
|
||||
script->remove();
|
||||
// Wait for pending off-thread parses to finish, since they depend on the
|
||||
// memory allocated by our CachedScripts, and can't be canceled
|
||||
// asynchronously.
|
||||
FinishPendingParses(mal);
|
||||
|
||||
// Pending scripts should have been cleared by the above, and new parses
|
||||
// should not have been queued.
|
||||
MOZ_ASSERT(mParsingScripts.empty());
|
||||
MOZ_ASSERT(mParsingSources.empty());
|
||||
MOZ_ASSERT(mPendingScripts.isEmpty());
|
||||
|
||||
for (auto& script : IterHash(mScripts))
|
||||
script.Remove();
|
||||
|
||||
@@ -795,7 +811,7 @@ ScriptPreloader::WaitForCachedScript(JSContext* cx, CachedScript* script)
|
||||
// ready. If we wait until we hit an unfinished script, we wind up having at
|
||||
// most one batch of buffered scripts, and occasionally under-running that
|
||||
// buffer.
|
||||
FinishOffThreadDecode();
|
||||
MaybeFinishOffThreadDecode();
|
||||
|
||||
if (!script->mReadyToExecute) {
|
||||
LOG(Info, "Must wait for async script load: %s\n", script->mURL.get());
|
||||
@@ -807,7 +823,7 @@ ScriptPreloader::WaitForCachedScript(JSContext* cx, CachedScript* script)
|
||||
// Check for finished operations again *after* locking, or we may race
|
||||
// against mToken being set between our last check and the time we
|
||||
// entered the mutex.
|
||||
FinishOffThreadDecode();
|
||||
MaybeFinishOffThreadDecode();
|
||||
|
||||
if (!script->mReadyToExecute && script->mSize < MAX_MAINTHREAD_DECODE_SIZE) {
|
||||
LOG(Info, "Script is small enough to recompile on main thread\n");
|
||||
@@ -818,7 +834,7 @@ ScriptPreloader::WaitForCachedScript(JSContext* cx, CachedScript* script)
|
||||
mal.Wait();
|
||||
|
||||
MonitorAutoUnlock mau(mMonitor);
|
||||
FinishOffThreadDecode();
|
||||
MaybeFinishOffThreadDecode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -856,14 +872,30 @@ ScriptPreloader::OffThreadDecodeCallback(void* token, void* context)
|
||||
}
|
||||
|
||||
void
|
||||
ScriptPreloader::DoFinishOffThreadDecode()
|
||||
ScriptPreloader::FinishPendingParses(MonitorAutoLock& aMal)
|
||||
{
|
||||
mFinishDecodeRunnablePending = false;
|
||||
FinishOffThreadDecode();
|
||||
mMonitor.AssertCurrentThreadOwns();
|
||||
|
||||
mPendingScripts.clear();
|
||||
|
||||
MaybeFinishOffThreadDecode();
|
||||
|
||||
// Loop until all pending decode operations finish.
|
||||
while (!mParsingScripts.empty()) {
|
||||
aMal.Wait();
|
||||
MaybeFinishOffThreadDecode();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ScriptPreloader::FinishOffThreadDecode()
|
||||
ScriptPreloader::DoFinishOffThreadDecode()
|
||||
{
|
||||
mFinishDecodeRunnablePending = false;
|
||||
MaybeFinishOffThreadDecode();
|
||||
}
|
||||
|
||||
void
|
||||
ScriptPreloader::MaybeFinishOffThreadDecode()
|
||||
{
|
||||
if (!mToken) {
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user