Bug 153317 - Worker terminated by GC after calling importScripts
Summary: Worker terminated by GC after calling importScripts
Status: NEW
Alias: None
Product: WebKit
Classification: Unclassified
Component: WebCore JavaScript (show other bugs)
Version: WebKit Nightly Build
Hardware: Unspecified OS X 10.11
: P2 Normal
Assignee: Nobody
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2016-01-21 13:17 PST by Rob Wu
Modified: 2024-01-29 19:02 PST (History)
4 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Rob Wu 2016-01-21 13:17:30 PST
After calling importScripts, the worker object (on the main thread) is unexpectedly garbage-collected and the worker thread is terminated.
This is because DedicatedWorkerGlobalScope::importScripts calls reportPendingActivity, which notifies the main thread that the worker is idle, regardless of whether there is active code after the importScripts call from JavaScript.

Steps to reproduce:
1. Start WebKit Nightly and open the Inspector (e.g. on example.com)
2. Run the following snippet:

;(function() {
    // setTimeout because the bug only occurs when importScripts is called after the
    // worker script has completely run (in JSC).
    var code = 'setTimeout(' + function() {

        importScripts("data:,");

        // V8 immediately terminates a thread when the worker object is GC'd,
        // while JSC continues running the worker script until the tab is closed,
        // so schedule the busy loop after GC on the main thread has run.
        setTimeout(function() {
            // Busy loop so we can observe whether the worker script is active.
            while (true) {}
        }, 1000);
    } + ');';

    // Create worker without saving a reference.
    new Worker(URL.createObjectURL(new Blob([code])));

    // Trigger GC
    setTimeout(function() {
        for (var i = 0; i < 25; ++i)
            new Array(Math.pow(2, i));
    }, 500);
})();
3. Look at the Activity Monitor to watch the CPU usage of Safari.
4. Open a new tab and close the tab from step 3 (to force termination of the worker if not already done).
5. Quit Safari and repeat the above, but with importScripts commented out or removed.

Expected result:
- Step 3 and 5 should give the same results, i.e. 100% usage of a CPU core (caused by the busy loop).

Actual result:
- After step 3, the CPU usage of Safari is negligible. This shows that the scheduled busy loop never executes, i.e. the worker thread is terminated.
- After step 5, the CPU usage of Safari is 100% (as expected).
This difference in behavior shows that importScripts affects the garbage collection behavior.


More info:
- Similar bug in Blink, with work-around for web devs, and patch for Blink: https://crbug.com/572225
- The above example seems contrived, but it was a reduction from a bug that affected real-world code that uses RequireJS in a worker. The worker object was GC'd even though there were message listeners on the Worker instance.
Comment 1 Alexey Proskuryakov 2016-01-25 15:42:41 PST
Thank you for the great bug report!
Comment 2 Kaiido 2023-02-08 23:01:42 PST
Things might have changed since this was first reported, but I'd like to point out that if the snippet still does reproduce in current builds it's because `"data:,"` does not contain a proper JS mimeType. As far as I can see, this is actually in accordance with the specs, and it's a bit weird that other browsers do not throw here (e.g they'd throw with a non "data:" URL resulting in the same mimeType).  
But anyway, what happens currently has nothing to do with GC, and it's normal the worker is terminated before entering the busy loop (an error is even displayed).