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:
Kris Maglione
2017-07-25 15:23:12 -07:00
parent 36fa0f80a2
commit 1233d7ac3c
2 changed files with 44 additions and 11 deletions

View File

@@ -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;