Changeset 292799 in webkit


Ignore:
Timestamp:
Apr 12, 2022 9:27:16 PM (3 months ago)
Author:
commit-queue@webkit.org
Message:

Allow Wasm import from a JS Worker module
https://bugs.webkit.org/show_bug.cgi?id=238291

Patch by Asumu Takikawa <asumu@igalia.com> on 2022-04-12
Reviewed by Yusuke Suzuki.

LayoutTests/imported/w3c:

Fixed worker test and updated expectation.

  • web-platform-tests/wasm/webapi/esm-integration/resources/worker-helper.js:

(export.pm):

  • web-platform-tests/wasm/webapi/esm-integration/worker-import.tentative-expected.txt:
  • web-platform-tests/wasm/webapi/esm-integration/worker-import.tentative.html:

Source/WebCore:

Adds new source provider for Wasm module loading when imported via
a JS module running in a Worker. Also adjust how WorkerRunLoop
processes tasks for script module loading. In particular, during
the module parsing phase, Wasm modules schedule async tasks that
need to be run to produce a module record.

When a Wasm module is loaded (via the module loader's module parsing
step), the validation/compilation happens asynchronously and
completion callbacks are run via
DeferredWorkTimer::scheduleWorkSoon, which queues up microtasks
from the VM. These microtasks, however, don't get run in a Worker
run loop during module loading and therefore the Wasm module never
finishes loading.

This is because during non-default modes, the Worker run loop
does not install its set timer callback, and continues to wait for
a task to run (due to infinite timeout). This means the microtask
checkpoint is also not reached, so other microtasks cannot run.
In non-default modes, the run loop also ignores all tasks that were
not installed in that particular mode.

In addition, the Worker event loop cannot run either, as it posts
default mode tasks to the run loop to trigger its operation.

The current patch modifies the run loop to allow the run loop to time
out even in non-default modes if a useTimeout parameter is passed as
true (defaults to false). We set this to true for the Worker module
loading process. It also allows the timer notification callback to
post tasks to non-default run loops.

  • WebCore.xcodeproj/project.pbxproj:
  • bindings/js/ScriptBufferSourceProvider.h:

(WebCore::AbstractScriptBufferHolder::~AbstractScriptBufferHolder):

  • bindings/js/ScriptModuleLoader.cpp:

(WebCore::ScriptModuleLoader::notifyFinished):

  • bindings/js/WebAssemblyScriptBufferSourceProvider.h: Added.
  • bindings/js/WebAssemblyScriptSourceCode.h:

(WebCore::WebAssemblyScriptSourceCode::WebAssemblyScriptSourceCode):

  • workers/ScriptBuffer.cpp:

(WebCore::ScriptBuffer::append):

  • workers/ScriptBuffer.h:
  • workers/WorkerEventLoop.cpp:

(WebCore::WorkerEventLoop::scheduleToRun):
(WebCore::WorkerEventLoop::taskMode):

  • workers/WorkerEventLoop.h:
  • workers/WorkerOrWorkletGlobalScope.cpp:

(WebCore::WorkerOrWorkletGlobalScope::postTaskForMode):

  • workers/WorkerOrWorkletGlobalScope.h:
  • workers/WorkerOrWorkletScriptController.cpp:

(WebCore::WorkerOrWorkletScriptController::loadModuleSynchronously):

  • workers/WorkerRunLoop.cpp:

(WebCore::ModePredicate::ModePredicate):
(WebCore::ModePredicate::mode const):
(WebCore::ModePredicate::operator() const):
(WebCore::WorkerDedicatedRunLoop::run):
(WebCore::WorkerDedicatedRunLoop::runInDebuggerMode):
(WebCore::WorkerDedicatedRunLoop::runInMode):
(WebCore::WorkerMainRunLoop::runInMode):

  • workers/WorkerRunLoop.h:
  • workers/WorkerScriptLoader.cpp:

(WebCore::WorkerScriptLoader::didReceiveData):

Location:
trunk
Files:
1 added
19 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/imported/w3c/ChangeLog

    r292759 r292799  
     12022-04-12  Asumu Takikawa  <asumu@igalia.com>
     2
     3        Allow Wasm import from a JS Worker module
     4        https://bugs.webkit.org/show_bug.cgi?id=238291
     5
     6        Reviewed by Yusuke Suzuki.
     7
     8        Fixed worker test and updated expectation.
     9
     10        * web-platform-tests/wasm/webapi/esm-integration/resources/worker-helper.js:
     11        (export.pm):
     12        * web-platform-tests/wasm/webapi/esm-integration/worker-import.tentative-expected.txt:
     13        * web-platform-tests/wasm/webapi/esm-integration/worker-import.tentative.html:
     14
    1152022-04-11  Antti Koivisto  <antti@apple.com>
    216
  • trunk/LayoutTests/imported/w3c/web-platform-tests/wasm/webapi/esm-integration/resources/worker-helper.js

    r290300 r292799  
    1 export const pm = DedicatedWorkerGlobalScope.postMessage;
     1export function pm(x) { postMessage(x); }
  • trunk/LayoutTests/imported/w3c/web-platform-tests/wasm/webapi/esm-integration/worker-import.tentative-expected.txt

    r290300 r292799  
    1 CONSOLE MESSAGE: TypeError: WebAssembly modules are not supported in workers yet.
    21
    3 FAIL Testing import of WebAssembly from JavaScript worker Script error.
     2PASS Testing import of WebAssembly from JavaScript worker
    43
  • trunk/LayoutTests/imported/w3c/web-platform-tests/wasm/webapi/esm-integration/worker-import.tentative.html

    r290300 r292799  
    88const worker = new Worker("resources/worker.js", { type: "module" });
    99worker.onmessage = (msg) => {
    10   assert_equals(msg, 42);
     10  assert_equals(msg.data, 42);
    1111  done();
    1212}
  • trunk/Source/WebCore/ChangeLog

    r292797 r292799  
     12022-04-12  Asumu Takikawa  <asumu@igalia.com>
     2
     3        Allow Wasm import from a JS Worker module
     4        https://bugs.webkit.org/show_bug.cgi?id=238291
     5
     6        Reviewed by Yusuke Suzuki.
     7
     8        Adds new source provider for Wasm module loading when imported via
     9        a JS module running in a Worker. Also adjust how WorkerRunLoop
     10        processes tasks for script module loading. In particular, during
     11        the module parsing phase, Wasm modules schedule async tasks that
     12        need to be run to produce a module record.
     13
     14        When a Wasm module is loaded (via the module loader's module parsing
     15        step), the validation/compilation happens asynchronously and
     16        completion callbacks are run via
     17        DeferredWorkTimer::scheduleWorkSoon, which queues up microtasks
     18        from the VM. These microtasks, however, don't get run in a Worker
     19        run loop during module loading and therefore the Wasm module never
     20        finishes loading.
     21
     22        This is because during non-default modes, the Worker run loop
     23        does not install its set timer callback, and continues to wait for
     24        a task to run (due to infinite timeout). This means the microtask
     25        checkpoint is also not reached, so other microtasks cannot run.
     26        In non-default modes, the run loop also ignores all tasks that were
     27        not installed in that particular mode.
     28
     29        In addition, the Worker event loop cannot run either, as it posts
     30        default mode tasks to the run loop to trigger its operation.
     31
     32        The current patch modifies the run loop to allow the run loop to time
     33        out even in non-default modes if a `useTimeout` parameter is passed as
     34        true (defaults to false). We set this to true for the Worker module
     35        loading process. It also allows the timer notification callback to
     36        post tasks to non-default run loops.
     37
     38        * WebCore.xcodeproj/project.pbxproj:
     39        * bindings/js/ScriptBufferSourceProvider.h:
     40        (WebCore::AbstractScriptBufferHolder::~AbstractScriptBufferHolder):
     41        * bindings/js/ScriptModuleLoader.cpp:
     42        (WebCore::ScriptModuleLoader::notifyFinished):
     43        * bindings/js/WebAssemblyScriptBufferSourceProvider.h: Added.
     44        * bindings/js/WebAssemblyScriptSourceCode.h:
     45        (WebCore::WebAssemblyScriptSourceCode::WebAssemblyScriptSourceCode):
     46        * workers/ScriptBuffer.cpp:
     47        (WebCore::ScriptBuffer::append):
     48        * workers/ScriptBuffer.h:
     49        * workers/WorkerEventLoop.cpp:
     50        (WebCore::WorkerEventLoop::scheduleToRun):
     51        (WebCore::WorkerEventLoop::taskMode):
     52        * workers/WorkerEventLoop.h:
     53        * workers/WorkerOrWorkletGlobalScope.cpp:
     54        (WebCore::WorkerOrWorkletGlobalScope::postTaskForMode):
     55        * workers/WorkerOrWorkletGlobalScope.h:
     56        * workers/WorkerOrWorkletScriptController.cpp:
     57        (WebCore::WorkerOrWorkletScriptController::loadModuleSynchronously):
     58        * workers/WorkerRunLoop.cpp:
     59        (WebCore::ModePredicate::ModePredicate):
     60        (WebCore::ModePredicate::mode const):
     61        (WebCore::ModePredicate::operator() const):
     62        (WebCore::WorkerDedicatedRunLoop::run):
     63        (WebCore::WorkerDedicatedRunLoop::runInDebuggerMode):
     64        (WebCore::WorkerDedicatedRunLoop::runInMode):
     65        (WebCore::WorkerMainRunLoop::runInMode):
     66        * workers/WorkerRunLoop.h:
     67        * workers/WorkerScriptLoader.cpp:
     68        (WebCore::WorkerScriptLoader::didReceiveData):
     69
    1702022-04-12  Elliott Williams  <emw@apple.com>
    271
  • trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj

    r292780 r292799  
    1026110261                550F664522CA89BD000A3417 /* SVGSharedPrimitiveProperty.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SVGSharedPrimitiveProperty.h; sourceTree = "<group>"; };
    1026210262                55137B2C20379E550001004B /* SVGMarkerTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SVGMarkerTypes.h; sourceTree = "<group>"; };
     10263                5523B98B27ED194800ECA746 /* WebAssemblyScriptBufferSourceProvider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = WebAssemblyScriptBufferSourceProvider.h; path = bindings/js/WebAssemblyScriptBufferSourceProvider.h; sourceTree = "<group>"; };
    1026310264                554675771FD1FC1A003B10B0 /* ImageSource.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ImageSource.cpp; sourceTree = "<group>"; };
    1026410265                554675781FD1FC1A003B10B0 /* ImageSource.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ImageSource.h; sourceTree = "<group>"; };
     
    1902819029                        isa = PBXGroup;
    1902919030                        children = (
     19031                                5523B98B27ED194800ECA746 /* WebAssemblyScriptBufferSourceProvider.h */,
    1903019032                                DDB04F37278E5527008D3678 /* libWTF.a */,
    1903119033                                1C09D04B1E31C32800725F18 /* PAL.xcodeproj */,
  • trunk/Source/WebCore/bindings/js/ScriptBufferSourceProvider.h

    r287021 r292799  
    3232namespace WebCore {
    3333
    34 class ScriptBufferSourceProvider final : public JSC::SourceProvider, public CanMakeWeakPtr<ScriptBufferSourceProvider> {
     34class AbstractScriptBufferHolder : public CanMakeWeakPtr<AbstractScriptBufferHolder> {
     35public:
     36    virtual void clearDecodedData() = 0;
     37    virtual void tryReplaceScriptBuffer(const ScriptBuffer&) = 0;
     38
     39    virtual ~AbstractScriptBufferHolder() { }
     40};
     41
     42class ScriptBufferSourceProvider final : public JSC::SourceProvider, public AbstractScriptBufferHolder {
    3543    WTF_MAKE_FAST_ALLOCATED;
    3644public:
     
    7179    }
    7280
    73     void clearDecodedData()
     81    void clearDecodedData() final
    7482    {
    7583        m_cachedScriptString = String();
    7684    }
    7785
    78     void tryReplaceScriptBuffer(const ScriptBuffer& scriptBuffer)
     86    void tryReplaceScriptBuffer(const ScriptBuffer& scriptBuffer) final
    7987    {
    8088        // If this new file-mapped script buffer is identical to the one we have, then replace
  • trunk/Source/WebCore/bindings/js/ScriptModuleLoader.cpp

    r292118 r292799  
    483483            type = ModuleType::JavaScript;
    484484#if ENABLE(WEBASSEMBLY)
    485         else if (context().settingsValues().webAssemblyESMIntegrationEnabled && MIMETypeRegistry::isSupportedWebAssemblyMIMEType(loader.responseMIMEType())) {
     485        else if (context().settingsValues().webAssemblyESMIntegrationEnabled && MIMETypeRegistry::isSupportedWebAssemblyMIMEType(loader.responseMIMEType()))
    486486            type = ModuleType::WebAssembly;
    487             // FIXME: add worker support for Wasm/ESM integration.
    488             rejectWithFetchError(promise.get(), TypeError, makeString("WebAssembly modules are not supported in workers yet."));
    489             return;
    490         }
    491487#endif
    492488        else {
     
    519515                    JSC::SourceCode { ScriptSourceCode { loader.script(), WTFMove(responseURL), { }, JSC::SourceProviderSourceType::Module, loader.scriptFetcher() }.jsSourceCode() });
    520516                break;
     517#if ENABLE(WEBASSEMBLY)
    521518            case ModuleType::WebAssembly:
     519                return JSC::JSSourceCode::create(jsGlobalObject.vm(),
     520                    JSC::SourceCode { WebAssemblyScriptSourceCode { loader.script(), WTFMove(responseURL), loader.scriptFetcher() }.jsSourceCode() });
     521                break;
     522#endif
    522523            default:
    523524                RELEASE_ASSERT_NOT_REACHED();
  • trunk/Source/WebCore/bindings/js/WebAssemblyScriptSourceCode.h

    r290300 r292799  
    3333#include "SharedBuffer.h"
    3434#include "WebAssemblyCachedScriptSourceProvider.h"
     35#include "WebAssemblyScriptBufferSourceProvider.h"
    3536#include <JavaScriptCore/SourceCode.h>
    3637#include <JavaScriptCore/SourceProvider.h>
     
    4950    }
    5051
     52    WebAssemblyScriptSourceCode(const ScriptBuffer& source, URL&& url, Ref<JSC::ScriptFetcher>&& scriptFetcher)
     53        : m_provider(WebAssemblyScriptBufferSourceProvider::create(source, WTFMove(url), WTFMove(scriptFetcher)))
     54        , m_code(m_provider.copyRef())
     55    {
     56    }
     57
    5158    const JSC::SourceCode& jsSourceCode() const { return m_code; }
    5259
  • trunk/Source/WebCore/workers/ScriptBuffer.cpp

    r286983 r292799  
    7070}
    7171
     72void ScriptBuffer::append(const FragmentedSharedBuffer& buffer)
     73{
     74    m_buffer.append(buffer);
     75}
     76
    7277bool operator==(const ScriptBuffer& a, const ScriptBuffer& b)
    7378{
  • trunk/Source/WebCore/workers/ScriptBuffer.h

    r287021 r292799  
    5757    WEBCORE_EXPORT bool containsSingleFileMappedSegment() const;
    5858    void append(const String&);
     59    void append(const FragmentedSharedBuffer&);
    5960
    6061private:
  • trunk/Source/WebCore/workers/WorkerEventLoop.cpp

    r282755 r292799  
    4848void WorkerEventLoop::scheduleToRun()
    4949{
    50     ASSERT(scriptExecutionContext());
    51     scriptExecutionContext()->postTask([eventLoop = Ref { *this }] (ScriptExecutionContext&) {
     50    auto* globalScope = downcast<WorkerOrWorkletGlobalScope>(scriptExecutionContext());
     51    ASSERT(globalScope);
     52    // Post this task with a special event mode, so that it can be separated from other
     53    // kinds of tasks so that queued microtasks can run even if other tasks are ignored.
     54    globalScope->postTaskForMode([eventLoop = Ref { *this }] (ScriptExecutionContext&) {
    5255        eventLoop->run();
    53     });
     56    }, WorkerEventLoop::taskMode());
    5457}
    5558
     
    7275}
    7376
     77const String WorkerEventLoop::taskMode()
     78{
     79    return "workerEventLoopTaskMode"_s;
     80}
     81
    7482} // namespace WebCore
    75 
  • trunk/Source/WebCore/workers/WorkerEventLoop.h

    r268822 r292799  
    3939    virtual ~WorkerEventLoop();
    4040
     41    static const String taskMode();
    4142    // FIXME: This should be removed once MicrotaskQueue is integrated with EventLoopTaskGroup.
    4243    void clearMicrotaskQueue();
  • trunk/Source/WebCore/workers/WorkerOrWorkletGlobalScope.cpp

    r284226 r292799  
    121121}
    122122
     123void WorkerOrWorkletGlobalScope::postTaskForMode(Task&& task, const String& mode)
     124{
     125    ASSERT(workerOrWorkletThread());
     126    workerOrWorkletThread()->runLoop().postTaskForMode(WTFMove(task), mode);
     127}
     128
    123129} // namespace WebCore
  • trunk/Source/WebCore/workers/WorkerOrWorkletGlobalScope.h

    r289721 r292799  
    6666    void postTask(Task&&) final; // Executes the task on context's thread asynchronously.
    6767
     68    // Defined specifcially for WorkerOrWorkletGlobalScope for cooperation with
     69    // WorkerEventLoop and WorkerRunLoop, not part of ScriptExecutionContext.
     70    void postTaskForMode(Task&&, const String&);
     71
    6872    virtual void prepareForDestruction();
    6973
  • trunk/Source/WebCore/workers/WorkerOrWorkletScriptController.cpp

    r290300 r292799  
    365365    // in the following driving of the RunLoop which mainly attempt to collect initial load of module scripts.
    366366    String taskMode = WorkerModuleScriptLoader::taskMode();
     367
     368    // Allow tasks scheduled from the WorkerEventLoop.
     369    constexpr bool allowEventLoopTasks = true;
     370
    367371    bool success = true;
    368372    while ((!protector->isLoaded() && !protector->wasCanceled()) && success) {
    369         success = runLoop.runInMode(m_globalScope, taskMode);
     373        success = runLoop.runInMode(m_globalScope, taskMode, allowEventLoopTasks);
    370374        if (success)
    371375            m_globalScope->eventLoop().performMicrotaskCheckpoint();
  • trunk/Source/WebCore/workers/WorkerRunLoop.cpp

    r284857 r292799  
    3737#include "ThreadGlobalData.h"
    3838#include "ThreadTimers.h"
     39#include "WorkerEventLoop.h"
    3940#include "WorkerOrWorkletGlobalScope.h"
    4041#include "WorkerOrWorkletScriptController.h"
     
    6667class ModePredicate {
    6768public:
    68     explicit ModePredicate(String&& mode)
     69    explicit ModePredicate(String&& mode, bool allowEventLoopTasks)
    6970        : m_mode(WTFMove(mode))
    7071        , m_defaultMode(m_mode == WorkerRunLoop::defaultMode())
    71     {
     72        , m_allowEventLoopTasks(allowEventLoopTasks)
     73    {
     74    }
     75
     76    const String mode() const
     77    {
     78        return m_mode;
    7279    }
    7380
     
    7986    bool operator()(const WorkerDedicatedRunLoop::Task& task) const
    8087    {
    81         return m_defaultMode || m_mode == task.mode();
     88        return m_defaultMode || m_mode == task.mode() || (m_allowEventLoopTasks && task.mode() == WorkerEventLoop::taskMode());
    8289    }
    8390
     
    8592    String m_mode;
    8693    bool m_defaultMode;
     94    bool m_allowEventLoopTasks;
    8795};
    8896
     
    138146{
    139147    RunLoopSetup setup(*this, RunLoopSetup::IsForDebugging::No);
    140     ModePredicate modePredicate(defaultMode());
     148    ModePredicate modePredicate(defaultMode(), false);
    141149    MessageQueueWaitResult result;
    142150    do {
     
    149157{
    150158    RunLoopSetup setup(*this, RunLoopSetup::IsForDebugging::Yes);
    151     return runInMode(&context, ModePredicate { debuggerMode() });
    152 }
    153 
    154 bool WorkerDedicatedRunLoop::runInMode(WorkerOrWorkletGlobalScope* context, const String& mode)
     159    return runInMode(&context, ModePredicate { debuggerMode(), false });
     160}
     161
     162bool WorkerDedicatedRunLoop::runInMode(WorkerOrWorkletGlobalScope* context, const String& mode, bool allowEventLoopTasks)
    155163{
    156164    ASSERT(mode != debuggerMode());
    157165    RunLoopSetup setup(*this, RunLoopSetup::IsForDebugging::No);
    158     ModePredicate modePredicate(String { mode });
     166    ModePredicate modePredicate(String { mode }, allowEventLoopTasks);
    159167    return runInMode(context, modePredicate) != MessageQueueWaitResult::MessageQueueTerminated;
    160168}
     
    165173    ASSERT(context->workerOrWorkletThread()->thread() == &Thread::current());
    166174
    167     JSC::JSRunLoopTimer::TimerNotificationCallback timerAddedTask = createSharedTask<JSC::JSRunLoopTimer::TimerNotificationType>([this] {
     175    const String predicateMode = predicate.mode();
     176    JSC::JSRunLoopTimer::TimerNotificationCallback timerAddedTask = createSharedTask<JSC::JSRunLoopTimer::TimerNotificationType>([this, predicateMode] {
    168177        // We don't actually do anything here, we just want to loop around runInMode
    169178        // to both recalculate our deadline and to potentially run the run loop.
    170         this->postTask([](ScriptExecutionContext&) { });
     179        this->postTaskForMode([predicateMode](ScriptExecutionContext&) { }, predicateMode);
    171180    });
    172181
     
    312321}
    313322
    314 bool WorkerMainRunLoop::runInMode(WorkerOrWorkletGlobalScope*, const String&)
     323bool WorkerMainRunLoop::runInMode(WorkerOrWorkletGlobalScope*, const String&, bool)
    315324{
    316325    RunLoop::main().cycle();
  • trunk/Source/WebCore/workers/WorkerRunLoop.h

    r283295 r292799  
    4949    virtual ~WorkerRunLoop() = default;
    5050
    51     virtual bool runInMode(WorkerOrWorkletGlobalScope*, const String& mode) = 0;
     51    virtual bool runInMode(WorkerOrWorkletGlobalScope*, const String& mode, bool allowEventLoopTasks = false) = 0;
    5252    virtual void postTaskAndTerminate(ScriptExecutionContext::Task&&) = 0;
    5353    virtual void postTaskForMode(ScriptExecutionContext::Task&&, const String& mode) = 0;
     
    7676
    7777    // Waits for a single task and returns.
    78     bool runInMode(WorkerOrWorkletGlobalScope*, const String& mode) final;
     78    bool runInMode(WorkerOrWorkletGlobalScope*, const String& mode, bool) final;
    7979    MessageQueueWaitResult runInDebuggerMode(WorkerOrWorkletGlobalScope&);
    8080
     
    126126    bool terminated() const final { return m_terminated; }
    127127
    128     bool runInMode(WorkerOrWorkletGlobalScope*, const String& mode);
     128    bool runInMode(WorkerOrWorkletGlobalScope*, const String& mode, bool);
    129129    void postTaskAndTerminate(ScriptExecutionContext::Task&&) final;
    130130    void postTaskForMode(ScriptExecutionContext::Task&&, const String& mode) final;
  • trunk/Source/WebCore/workers/WorkerScriptLoader.cpp

    r291992 r292799  
    235235        return;
    236236
     237#if ENABLE(WEBASSEMBLY)
     238    if (MIMETypeRegistry::isSupportedWebAssemblyMIMEType(m_responseMIMEType)) {
     239        m_script.append(buffer);
     240        return;
     241    }
     242#endif
     243
    237244    if (!m_decoder)
    238245        m_decoder = TextResourceDecoder::create("text/javascript"_s, "UTF-8");
Note: See TracChangeset for help on using the changeset viewer.