Bug 1652126: Obtain an OffThreadToken immediately so parse tasks can be canceled anytime, and clean up dangling Runnables during cancellation. r=smaug

Differential Revision: https://phabricator.services.mozilla.com/D89465
This commit is contained in:
Denis Palmeiro
2020-09-11 15:28:04 +00:00
parent 550b941b20
commit 274ab31942
3 changed files with 53 additions and 15 deletions

View File

@@ -230,9 +230,7 @@ ScriptLoader::~ScriptLoader() {
mPendingChildLoaders[j]->RemoveParserBlockingScriptExecutionBlocker();
}
// Cancel any unused preload requests
for (size_t i = 0; i < mPreloads.Length(); i++) {
mPreloads[i].mRequest->Cancel();
AccumulateCategorical(LABELS_DOM_SCRIPT_PRELOAD_RESULT::NotUsed);
}
}
@@ -2200,6 +2198,9 @@ NotifyOffThreadScriptLoadCompletedRunnable::Run() {
// function.
RefPtr<ScriptLoadRequest> request = std::move(mRequest);
// Runnable pointer should have been cleared in the offthread callback.
MOZ_ASSERT(!request->mRunnable);
#ifdef MOZ_GECKO_PROFILER
if (profiler_is_active()) {
const char* scriptSourceString;
@@ -2223,16 +2224,19 @@ NotifyOffThreadScriptLoadCompletedRunnable::Run() {
RefPtr<ScriptLoader> loader = std::move(mLoader);
request->mOffThreadToken = mToken;
nsresult rv = loader->ProcessOffThreadRequest(request);
// Request was already cancelled at some earlier point.
if (!request->mOffThreadToken) {
return NS_OK;
}
return rv;
return loader->ProcessOffThreadRequest(request);
}
static void OffThreadScriptLoaderCallback(JS::OffThreadToken* aToken,
void* aCallbackData) {
RefPtr<NotifyOffThreadScriptLoadCompletedRunnable> aRunnable = dont_AddRef(
static_cast<NotifyOffThreadScriptLoadCompletedRunnable*>(aCallbackData));
MOZ_ASSERT(aRunnable.get() == aRunnable->GetScriptLoadRequest()->mRunnable);
#ifdef MOZ_GECKO_PROFILER
aRunnable->GetScriptLoadRequest()->mOffThreadParseStopTime =
@@ -2242,6 +2246,12 @@ static void OffThreadScriptLoaderCallback(JS::OffThreadToken* aToken,
LogRunnable::Run run(aRunnable);
aRunnable->SetToken(aToken);
// If mRunnable was cleared then request was canceled so do nothing.
if (!aRunnable->GetScriptLoadRequest()->mRunnable.exchange(nullptr)) {
return;
}
NotifyOffThreadScriptLoadCompletedRunnable::Dispatch(aRunnable.forget());
}
@@ -2302,6 +2312,9 @@ nsresult ScriptLoader::AttemptAsyncScriptCompile(ScriptLoadRequest* aRequest,
aRequest->mOffThreadParseStartTime = TimeStamp::NowUnfuzzed();
#endif
// Save the runnable so it can be properly cleared during cancellation.
aRequest->mRunnable = runnable.get();
if (aRequest->IsModuleRequest()) {
MOZ_ASSERT(aRequest->IsTextSource());
MaybeSourceText maybeSource;
@@ -2311,17 +2324,19 @@ nsresult ScriptLoader::AttemptAsyncScriptCompile(ScriptLoadRequest* aRequest,
if (maybeSource.constructed<SourceText<char16_t>>()
? !JS::CompileOffThreadModule(
cx, options, maybeSource.ref<SourceText<char16_t>>(),
OffThreadScriptLoaderCallback, static_cast<void*>(runnable))
OffThreadScriptLoaderCallback, static_cast<void*>(runnable),
&aRequest->mOffThreadToken)
: !JS::CompileOffThreadModule(
cx, options, maybeSource.ref<SourceText<Utf8Unit>>(),
OffThreadScriptLoaderCallback,
static_cast<void*>(runnable))) {
OffThreadScriptLoaderCallback, static_cast<void*>(runnable),
&aRequest->mOffThreadToken)) {
return NS_ERROR_OUT_OF_MEMORY;
}
} else if (aRequest->IsBytecode()) {
if (!JS::DecodeOffThreadScript(
cx, options, aRequest->mScriptBytecode, aRequest->mBytecodeOffset,
OffThreadScriptLoaderCallback, static_cast<void*>(runnable))) {
OffThreadScriptLoaderCallback, static_cast<void*>(runnable),
&aRequest->mOffThreadToken)) {
return NS_ERROR_OUT_OF_MEMORY;
}
} else {
@@ -2333,11 +2348,12 @@ nsresult ScriptLoader::AttemptAsyncScriptCompile(ScriptLoadRequest* aRequest,
if (maybeSource.constructed<SourceText<char16_t>>()
? !JS::CompileOffThread(
cx, options, maybeSource.ref<SourceText<char16_t>>(),
OffThreadScriptLoaderCallback, static_cast<void*>(runnable))
: !JS::CompileOffThread(cx, options,
maybeSource.ref<SourceText<Utf8Unit>>(),
OffThreadScriptLoaderCallback,
static_cast<void*>(runnable))) {
OffThreadScriptLoaderCallback, static_cast<void*>(runnable),
&aRequest->mOffThreadToken)
: !JS::CompileOffThread(
cx, options, maybeSource.ref<SourceText<Utf8Unit>>(),
OffThreadScriptLoaderCallback, static_cast<void*>(runnable),
&aRequest->mOffThreadToken)) {
return NS_ERROR_OUT_OF_MEMORY;
}
}
@@ -2358,7 +2374,7 @@ nsresult ScriptLoader::CompileOffThreadOrProcessRequest(
NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
"Processing requests when running scripts is unsafe.");
if (!aRequest->mOffThreadToken && !aRequest->mWasCompiledOMT) {
if (!aRequest->mOffThreadToken && !aRequest->InCompilingStage()) {
bool couldCompile = false;
nsresult rv = AttemptAsyncScriptCompile(aRequest, &couldCompile);
if (NS_FAILED(rv)) {
@@ -3937,6 +3953,11 @@ void ScriptLoader::ParsingComplete(bool aTerminated) {
mParserBlockingRequest = nullptr;
}
// Cancel any unused scripts that were compiled speculatively
for (size_t i = 0; i < mPreloads.Length(); i++) {
mPreloads[i].mRequest->MaybeCancelOffThreadScript();
}
// Have to call this even if aTerminated so we'll correctly unblock
// onload and all.
DeferCheckpointReached();