Closed Bug 906371 Opened 11 years ago Closed 11 years ago

Use off thread JS parsing when loading <script async> scripts from HTML documents

Categories

(Core :: JavaScript Engine, defect)

x86
macOS
defect
Not set
normal

Tracking

()

RESOLVED FIXED
mozilla26

People

(Reporter: bhackett1024, Assigned: bhackett1024)

References

(Blocks 2 open bugs)

Details

(Whiteboard: [games])

Attachments

(3 files)

This should be doable using the machinery that was added in bug 897655.  I'm not sure yet whether we should do this for all <script> tags, or just <script async> and/or <script defer>.
\o/
Depends on: 908294
Depends on: 908301
Depends on: 908699
Fix some leftover stuff from bug 908699.  AtomToPrintableString would just return NULL on an ExclusiveContext, which caused error reporting to crash in some cases.
Attachment #797562 - Flags: review?(wmccloskey)
With bug 908301, we can deadlock when a worker thread tries to wait on another worker compressing source and that other thread is paused, as the first thread itself will never pause and no progress will be made.
Attachment #797563 - Flags: review?(wmccloskey)
Attached patch patchSplinter Review
DOM changes (mainly nsScriptLoader) to allow <script async> scripts to be parsed off the main thread.
Assignee: general → bhackett1024
Attachment #797564 - Flags: review?(bzbarsky)
Morphing the title, this bug will just be about handling <script async> asynchronously.
Summary: Use off thread JS parsing when loading scripts from HTML documents → Use off thread JS parsing when loading <script async> scripts from HTML documents
Attachment #797562 - Flags: review?(wmccloskey) → review+
Comment on attachment 797563 [details] [diff] [review]
bug 908301 leftovers

Review of attachment 797563 [details] [diff] [review]:
-----------------------------------------------------------------

Man, this is getting complicated. Can you remind me why we pause workers during GC? It seems like we might be able to get away without it (although doing so might be more work than this patch).

::: js/src/jsworkers.cpp
@@ +781,5 @@
> +        // loop in AutoPauseWorkersForGC.
> +        if (cx->workerThread()) {
> +            state.numPaused++;
> +            if (state.numPaused == state.numThreads)
> +                state.notify(WorkerThreadState::MAIN);

Can we rename MAIN and WORKER to something like CONSUMER and PRODUCER? Otherwise, this code is pretty confusing to read.

@@ +788,2 @@
>          while (state.compressionInProgress(this))
>              state.wait(WorkerThreadState::MAIN);

It seems like multiple threads now can be waiting simultaneously on MAIN. Therefore, it seems like we should be using notifyAll(MAIN) in all the places where we're using notify(MAIN) now. Alternatively, we could use more condition variables or something.

@@ +790,5 @@
>  
> +        if (cx->workerThread()) {
> +            state.numPaused--;
> +            if (state.shouldPause)
> +                cx->workerThread()->pause();

Is the needed for correctness, or is it just an extra maybePause sort of check?
Attachment #797563 - Flags: review?(wmccloskey) → review+
I'm sorry this is taking so long...  I'm off tomorrow, but I plan to finish this review on Friday.
I don't think this should land while Debugger can't set breakpoints in these scripts.
Depends on: 912719
Comment on attachment 797564 [details] [diff] [review]
patch

>+  nsAutoCString mURL;

Please document that this is used to keep the "filename" used by the JS engine alive across a background compilation?

>+class NotifyOffThreadScriptLoadCompletedRunnable : public nsRunnable

Please put this in an anonymous namespace.

>+      JS::FinishOffThreadScript(NULL, rt, mToken);

nullptr, please.

>+nsScriptLoader::AttemptAsyncScriptParse(nsScriptLoadRequest* aRequest)

>+  if (!aRequest->mElement->GetScriptAsync() || aRequest->mIsInline)
>+    return NS_ERROR_FAILURE;

Curly braces around all blocks, please, here and elsewhere.

>+  if (!JS::CompileOffThread(cx, global, options,

Does this require cx to stay alive until OffThreadScriptLoaderCallback happens?

>+  AutoPushJSContext cx(context->GetNativeContext());

I _think_ this can GC, so unrootedGlobal is a gc hazard.  :(

You want to put the Rooted before this line.

Same thing in nsScriptLoader::EvaluateScript.

It may be worth a comment that which JSContext we use here matters, for error reporting.

>+  if (aRequest->mIsInline) {

Given the first line of this function, this will always test false.  Is it vestigial?  If so, please remove.

>+  mOffThreadScriptRequest = aRequest;

Why do we need this?  In particular, why only allow one off-thread compilation at a time?

You do need onload blocking here.  Right now, the unblock happens at the end of nsScriptLoader::ProcessPendingRequests if there is nothing pending.  So either that needs to not unblock if there are off-thread compilations going (but then you need to call back into that code after the off-thread thing is done or something) or the off-thread compilation needs to explicitly block/unblock onload.

>+++ b/dom/base/nsJSUtils.cpp
>+      *aOffThreadToken = NULL; // Mark the token as having been finished.

nullptr.

r=me with the above fixed.  Sorry for the lag.  :(
Attachment #797564 - Flags: review?(bzbarsky) → review+
Before landing, can we give this all a round of fuzzing specifically using finishOffThreadScript from bug 912321?
Gary and Christian, do you mind fuzzing this per comment 10?
In particular, it would be valuable if the fuzzers were able to pound on the usage of offThreadCompileScript+finishOffThreadScript in a threadsafe build with --ion-parallel-compile=on (generating the input analogous to what would be passed to eval/evaluate).
Do you mind quickly describing here how these two functions should be used? Is it just that you pass code to offThreadCompileScript and then just call finishOffThreadScript without any arguments?
IIUC,
  offThreadCompileScript(str);var result = finishOffThreadScript();
is basically the same as
  var result = evaluate(str);
(offThreadCompileScript doesn't accept any fancy options like evaluate, though.)

The two main interesting vectors to fuzz I see are (1) feeding a bunch of stuff into the string argument to offThreadCompileScript and (2) putting a bunch of JS between the calls to offThreadCompileScript and finishOffThreadScript.  Note: calling offThreadCompileScript() when there is already a compilation job pending appears to throw a runtime error.
Whiteboard: [games]
mOffThreadScriptRequest is there so that a reference is maintained on the nsScriptLoadRequest for the off thread parse, and so that it can be identified when the off thread parse finishes.  It would be nice, yeah, if this was improved so that a single script loader could have multiple off thread parses in flight at once.

https://hg.mozilla.org/integration/mozilla-inbound/rev/3ca22e239a1d
I see.  Mind following a followup to have the event hold references to both the script load request and the nsScriptLoader or something?  Then we'd be able to have more than one of these in flight at once.

Might also be worth adding telemetry to see how often we avoid going off-thread because mOffThreadScriptRequest is set.
Depends on: 915485
https://hg.mozilla.org/mozilla-central/rev/3ca22e239a1d
Status: NEW → RESOLVED
Closed: 11 years ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla26
Depends on: 915687
Depends on: 915625
I think we want to unblock onload _after_ we execute the script.  Otherwise we'll possibly fire the onload event before the script runs.
Flags: needinfo?(bhackett1024)
Blocks: 916255
(In reply to Boris Zbarsky [:bz] from comment #18)
> I think we want to unblock onload _after_ we execute the script.  Otherwise
> we'll possibly fire the onload event before the script runs.

OK, I added a fix for this to the patch in bug 916255.
Flags: needinfo?(bhackett1024)
Per comment #18 (and see https://bugzilla.mozilla.org/show_bug.cgi?id=916255) this basically breaks some assumptions about how the script tag loading works... Can we back this out and reland with the fix (and hopefully a test) we saw some serious breakage in gaia because of this change in behavior.
Flags: needinfo?(bhackett1024)
(In reply to James Lal [:lightsofapollo] from comment #20)
> Per comment #18 (and see
> https://bugzilla.mozilla.org/show_bug.cgi?id=916255) this basically breaks
> some assumptions about how the script tag loading works... Can we back this
> out and reland with the fix (and hopefully a test) we saw some serious
> breakage in gaia because of this change in behavior.

I'll try to land a fix with just the OnLoad movement later today.  It's going through try right now (https://tbpl.mozilla.org/?tree=Try&rev=1784cd49c8d0 as I don't know if it caused the tp5 timeouts in bug 916255 or not.  Also note that parallel script parsing can be disabled via the javascript.options.parallel_parsing config option.
Flags: needinfo?(bhackett1024)
Depends on: 916541
Patch moving UnblockOnload to the right place:

https://hg.mozilla.org/integration/mozilla-inbound/rev/26b48b6dbff4
Depends on: 916531
Blocks: gecko-games
Blocks: 931852
Depends on: 942984
May I understand the reason why <script defer> is not parsed off main thread? What are the concerns? Thanks.
Flags: needinfo?(bhackett1024)
(In reply to Ting-Yu Chou [:ting] from comment #24)
> May I understand the reason why <script defer> is not parsed off main
> thread? What are the concerns? Thanks.

I don't think there's any fundamental reason, it just hasn't been implemented.  The semantics of <script async> and <script defer> are not the same.
Flags: needinfo?(bhackett1024)
(In reply to Brian Hackett (:bhackett) from comment #25)
> (In reply to Ting-Yu Chou [:ting] from comment #24)
> > May I understand the reason why <script defer> is not parsed off main
> > thread? What are the concerns? Thanks.
> 
> I don't think there's any fundamental reason, it just hasn't been
> implemented.  The semantics of <script async> and <script defer> are not the
> same.
I took the liberty to file bug 1147048 then. I use @defer much more often than I use @async for the applications I deploy.
You need to log in before you can comment on or make changes to this bug.