Ignore:
Timestamp:
Mar 28, 2017, 4:12:11 PM (8 years ago)
Author:
[email protected]
Message:

WebAssembly: Make WebAssembly.instantiate/compile truly asynchronous
https://bugs.webkit.org/show_bug.cgi?id=169187

Reviewed by Saam Barati.

JSTests:

  • wasm/assert.js:
  • wasm/js-api/Module-compile.js:

(async.testPromiseAPI):

  • wasm/js-api/web-assembly-compile-parallel.js: Added.

(async.throwExn):
(async.test):

  • wasm/js-api/web-assembly-instantiate-parallel.js: Added.

(async.test):

  • wasm/js-api/web-assembly-instantiate.js:

(assert.eq.async.test):
(assert.eq):
(assert.asyncTest.async.test):
(assert.asyncTest):
(assert.truthy.async.test): Deleted.
(assert.truthy): Deleted.

Source/JavaScriptCore:

This patch allows WebAssembly compilations to happen asynchronously.
To do so, it refactors how much of the compilation happens and adds
new infrastructure for async promises.

First, there is a new class, PromiseDeferredTimer that lives on
the VM. PromiseDeferredTimer will manage the life-cycle of async
pending promises and any dependencies that promise
needs. PromiseDeferredTimer automagically releases the pending
promise and dependencies once the JSPromiseDeferred is resolved or
rejected. Additionally, PromiseDeferredTimer provides a mechanism
to poll the run-loop whenever the async task needs to synchronize
with the JS thread. Normally, that will be whenever the async task
finishes. In the case of Web Assembly we also use this feature for
the compile + instantiate case, where we might have more work
after the first async task completes (more on that later).

The next class is Wasm::Worklist, which is used to manage Wasm
compilation tasks. The worklist class works similarly to the
DFG/FTL Worklists. It has a pool of threads that it manages. One
interesting aspect of Wasm Worklist is that it can synchronously
compile a plan that is already potentially running
asynchronously. This can occur if a user calls
WebAssembly.instantiate() then new WebAssembly.instantiate() on
the same module. In that case the Wasm Worklist will bump the
priority of the running pending Plan and block the JS thread.

This patch also makes some of the Wasm Plan code cleaner. Since we
now defer all compilation to instantiation time, we no longer need
to guess at which memory we are going to get. Also, Wasm Plans now
track the work they have done with a state enum.

Finally, this patch makes renamed HeapTimer to JSRunLoopTimer. It
also adds changes test262AsyncTest to a more generic testing
infrastructure. Now, in addition to the old functionality, you can
call asyncTest() with the number of tests you expect. When the jsc
CLI exits, it will guarantee that asyncTestPassed() is called that
many times.

  • CMakeLists.txt:
  • JavaScriptCore.xcodeproj/project.pbxproj:
  • heap/GCActivityCallback.h:
  • heap/IncrementalSweeper.cpp:

(JSC::IncrementalSweeper::scheduleTimer):
(JSC::IncrementalSweeper::IncrementalSweeper):

  • heap/IncrementalSweeper.h:
  • heap/StopIfNecessaryTimer.cpp:

(JSC::StopIfNecessaryTimer::StopIfNecessaryTimer):

  • heap/StopIfNecessaryTimer.h:
  • heap/StrongInlines.h:
  • jsc.cpp:

(GlobalObject::finishCreation):
(printInternal):
(functionAsyncTestStart):
(functionAsyncTestPassed):
(functionTestWasmModuleFunctions):
(CommandLine::parseArguments):
(runJSC):

  • runtime/JSPromiseDeferred.cpp:

(JSC::JSPromiseDeferred::resolve):
(JSC::JSPromiseDeferred::reject):

  • runtime/JSPromiseDeferred.h:

(JSC::JSPromiseDeferred::promiseAsyncPending):

  • runtime/JSRunLoopTimer.cpp: Renamed from Source/JavaScriptCore/heap/HeapTimer.cpp.

(JSC::JSRunLoopTimer::JSRunLoopTimer):
(JSC::JSRunLoopTimer::setRunLoop):
(JSC::JSRunLoopTimer::~JSRunLoopTimer):
(JSC::JSRunLoopTimer::timerDidFire):
(JSC::JSRunLoopTimer::scheduleTimer):
(JSC::JSRunLoopTimer::cancelTimer):
(JSC::JSRunLoopTimer::invalidate):

  • runtime/JSRunLoopTimer.h: Copied from Source/JavaScriptCore/heap/HeapTimer.h.
  • runtime/Options.h:
  • runtime/PromiseDeferredTimer.cpp: Added.

(JSC::PromiseDeferredTimer::PromiseDeferredTimer):
(JSC::PromiseDeferredTimer::doWork):
(JSC::PromiseDeferredTimer::runRunLoop):
(JSC::PromiseDeferredTimer::addPendingPromise):
(JSC::PromiseDeferredTimer::cancelPendingPromise):
(JSC::PromiseDeferredTimer::scheduleWorkSoon):
(JSC::PromiseDeferredTimer::scheduleBlockedTask):

  • runtime/PromiseDeferredTimer.h: Renamed from Source/JavaScriptCore/heap/HeapTimer.h.

(JSC::PromiseDeferredTimer::stopRunningTasks):

  • runtime/VM.cpp:

(JSC::VM::VM):
(JSC::VM::~VM):

  • runtime/VM.h:
  • wasm/JSWebAssembly.cpp:

(JSC::reject):
(JSC::webAssemblyCompileFunc):
(JSC::resolve):
(JSC::instantiate):
(JSC::compileAndInstantiate):
(JSC::webAssemblyInstantiateFunc):
(JSC::webAssemblyValidateFunc):

  • wasm/WasmB3IRGenerator.cpp:

(JSC::Wasm::B3IRGenerator::B3IRGenerator):
(JSC::Wasm::B3IRGenerator::emitCheckAndPreparePointer):
(JSC::Wasm::B3IRGenerator::memoryKind):
(JSC::Wasm::parseAndCompile):

  • wasm/WasmB3IRGenerator.h:
  • wasm/WasmFormat.h:

(JSC::Wasm::ModuleInformation::internalFunctionCount):

  • wasm/WasmFunctionParser.h:
  • wasm/WasmMemory.h:
  • wasm/WasmMemoryInformation.cpp:

(JSC::Wasm::MemoryInformation::MemoryInformation):

  • wasm/WasmMemoryInformation.h:

(JSC::Wasm::MemoryInformation::maximum):
(JSC::Wasm::MemoryInformation::hasReservedMemory): Deleted.
(JSC::Wasm::MemoryInformation::takeReservedMemory): Deleted.
(JSC::Wasm::MemoryInformation::mode): Deleted.

  • wasm/WasmModuleParser.cpp:
  • wasm/WasmModuleParser.h:

(JSC::Wasm::ModuleParser::ModuleParser):

  • wasm/WasmPlan.cpp:

(JSC::Wasm::Plan::Plan):
(JSC::Wasm::Plan::stateString):
(JSC::Wasm::Plan::moveToState):
(JSC::Wasm::Plan::fail):
(JSC::Wasm::Plan::parseAndValidateModule):
(JSC::Wasm::Plan::prepare):
(JSC::Wasm::Plan::ThreadCountHolder::ThreadCountHolder):
(JSC::Wasm::Plan::ThreadCountHolder::~ThreadCountHolder):
(JSC::Wasm::Plan::compileFunctions):
(JSC::Wasm::Plan::complete):
(JSC::Wasm::Plan::waitForCompletion):
(JSC::Wasm::Plan::cancel):
(JSC::Wasm::Plan::run): Deleted.
(JSC::Wasm::Plan::initializeCallees): Deleted.

  • wasm/WasmPlan.h:

(JSC::Wasm::Plan::dontFinalize):
(JSC::Wasm::Plan::exports):
(JSC::Wasm::Plan::internalFunctionCount):
(JSC::Wasm::Plan::takeModuleInformation):
(JSC::Wasm::Plan::takeCallLinkInfos):
(JSC::Wasm::Plan::takeWasmExitStubs):
(JSC::Wasm::Plan::setModeAndPromise):
(JSC::Wasm::Plan::mode):
(JSC::Wasm::Plan::pendingPromise):
(JSC::Wasm::Plan::vm):
(JSC::Wasm::Plan::errorMessage):
(JSC::Wasm::Plan::failed):
(JSC::Wasm::Plan::hasWork):
(JSC::Wasm::Plan::hasBeenPrepared):

  • wasm/WasmPlanInlines.h: Copied from Source/JavaScriptCore/wasm/WasmB3IRGenerator.h.

(JSC::Wasm::Plan::initializeCallees):

  • wasm/WasmValidate.cpp:
  • wasm/WasmWorklist.cpp: Added.

(JSC::Wasm::Worklist::priorityString):
(JSC::Wasm::Worklist::QueueElement::setToNextPriority):
(JSC::Wasm::Worklist::iterate):
(JSC::Wasm::Worklist::enqueue):
(JSC::Wasm::Worklist::completePlanSynchronously):
(JSC::Wasm::Worklist::stopAllPlansForVM):
(JSC::Wasm::Worklist::Worklist):
(JSC::Wasm::Worklist::~Worklist):
(JSC::Wasm::existingWorklistOrNull):
(JSC::Wasm::ensureWorklist):

  • wasm/WasmWorklist.h: Added.

(JSC::Wasm::Worklist::nextTicket):
(JSC::Wasm::Worklist::Comparator::operator()):

  • wasm/js/JSWebAssemblyCallee.h:
  • wasm/js/JSWebAssemblyCodeBlock.cpp:

(JSC::JSWebAssemblyCodeBlock::JSWebAssemblyCodeBlock):
(JSC::JSWebAssemblyCodeBlock::initialize):
(JSC::JSWebAssemblyCodeBlock::isSafeToRun):

  • wasm/js/JSWebAssemblyCodeBlock.h:

(JSC::JSWebAssemblyCodeBlock::create):
(JSC::JSWebAssemblyCodeBlock::initialized):
(JSC::JSWebAssemblyCodeBlock::plan):
(JSC::JSWebAssemblyCodeBlock::runnable):
(JSC::JSWebAssemblyCodeBlock::errorMessage):
(JSC::JSWebAssemblyCodeBlock::callees):

  • wasm/js/JSWebAssemblyHelpers.h:

(JSC::createSourceBufferFromValue):

  • wasm/js/JSWebAssemblyInstance.cpp:

(JSC::JSWebAssemblyInstance::finishCreation):
(JSC::JSWebAssemblyInstance::visitChildren):
(JSC::JSWebAssemblyInstance::addUnitializedCodeBlock):
(JSC::JSWebAssemblyInstance::finalizeCreation):
(JSC::JSWebAssemblyInstance::create):
(JSC::JSWebAssemblyInstance::setMemory): Deleted.

  • wasm/js/JSWebAssemblyInstance.h:

(JSC::JSWebAssemblyInstance::codeBlock):
(JSC::JSWebAssemblyInstance::initialized):
(JSC::JSWebAssemblyInstance::module):
(JSC::JSWebAssemblyInstance::importFunction):
(JSC::JSWebAssemblyInstance::setMemory):
(JSC::JSWebAssemblyInstance::table):
(JSC::JSWebAssemblyInstance::importFunctions):
(JSC::JSWebAssemblyInstance::setImportFunction): Deleted.
(JSC::JSWebAssemblyInstance::setTable): Deleted.

  • wasm/js/JSWebAssemblyModule.cpp:

(JSC::JSWebAssemblyModule::createStub):
(JSC::JSWebAssemblyModule::JSWebAssemblyModule):
(JSC::JSWebAssemblyModule::finishCreation):
(JSC::JSWebAssemblyModule::setCodeBlock):
(JSC::JSWebAssemblyModule::buildCodeBlock): Deleted.
(JSC::JSWebAssemblyModule::create): Deleted.
(JSC::JSWebAssemblyModule::codeBlock): Deleted.

  • wasm/js/JSWebAssemblyModule.h:

(JSC::JSWebAssemblyModule::moduleInformation):
(JSC::JSWebAssemblyModule::codeBlock):
(JSC::JSWebAssemblyModule::source):
(JSC::JSWebAssemblyModule::takeReservedMemory): Deleted.
(JSC::JSWebAssemblyModule::codeBlockFor): Deleted.

  • wasm/js/WebAssemblyInstanceConstructor.cpp:

(JSC::constructJSWebAssemblyInstance):
(JSC::WebAssemblyInstanceConstructor::createInstance): Deleted.

  • wasm/js/WebAssemblyModuleConstructor.cpp:

(JSC::WebAssemblyModuleConstructor::createModule):

  • wasm/js/WebAssemblyModulePrototype.cpp:

(JSC::webAssemblyModuleProtoImports):
(JSC::webAssemblyModuleProtoExports):

  • wasm/js/WebAssemblyModuleRecord.cpp:

(JSC::WebAssemblyModuleRecord::finishCreation):
(JSC::WebAssemblyModuleRecord::link):
(JSC::WebAssemblyModuleRecord::evaluate):

  • wasm/js/WebAssemblyModuleRecord.h:
File:
1 moved

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/runtime/PromiseDeferredTimer.h

    r214502 r214504  
    11/*
    2  * Copyright (C) 2012, 2015-2016 Apple Inc. All rights reserved.
     2 * Copyright (C) 2017 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    2121 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    2222 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
     23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    2424 */
    2525
    2626#pragma once
    2727
     28#include "JSRunLoopTimer.h"
     29#include "Strong.h"
     30#include "WriteBarrier.h"
     31
     32#include <wtf/HashMap.h>
    2833#include <wtf/Lock.h>
    29 #include <wtf/RefPtr.h>
    30 #include <wtf/RetainPtr.h>
    31 #include <wtf/ThreadSafeRefCounted.h>
    32 #include <wtf/Threading.h>
    33 
    34 #if USE(CF)
    35 #include <CoreFoundation/CoreFoundation.h>
    36 #endif
    37 
    38 #if USE(GLIB)
    39 #include <wtf/glib/GRefPtr.h>
    40 #endif
     34#include <wtf/SharedTask.h>
     35#include <wtf/Vector.h>
    4136
    4237namespace JSC {
    4338
    44 class JSLock;
     39class JSPromiseDeferred;
    4540class VM;
     41class JSCell;
    4642
    47 class HeapTimer : public ThreadSafeRefCounted<HeapTimer> {
     43class PromiseDeferredTimer : public JSRunLoopTimer {
    4844public:
    49     HeapTimer(VM*);
     45    using Base = JSRunLoopTimer;
     46
     47    PromiseDeferredTimer(VM&);
     48
     49    void doWork() override;
     50
     51    void addPendingPromise(JSPromiseDeferred*, Vector<Strong<JSCell>>&& dependencies);
     52    // JSPromiseDeferred should handle canceling when the promise is resolved or rejected.
     53    bool cancelPendingPromise(JSPromiseDeferred*);
     54
     55    typedef std::function<void()> Task;
     56    void scheduleWorkSoon(JSPromiseDeferred*, Task&&);
     57
     58    // Blocked tasks should only be registered while holding the JS API lock. If we didn't require holding the
     59    // JS API lock then there might be a race where the promise you are waiting on is run before your task is
     60    // registered.
     61    void scheduleBlockedTask(JSPromiseDeferred*, Task&&);
     62
     63    void stopRunningTasks() { m_runTasks = false; }
    5064#if USE(CF)
    51     static void timerDidFire(CFRunLoopTimerRef, void*);
     65    JS_EXPORT_PRIVATE void runRunLoop();
    5266#endif
    53    
    54     JS_EXPORT_PRIVATE virtual ~HeapTimer();
    55     virtual void doWork() = 0;
    5667
    57     void scheduleTimer(double intervalInSeconds);
    58     void cancelTimer();
    59     bool isScheduled() const { return m_isScheduled; }
    60 
     68private:
     69    HashMap<JSPromiseDeferred*, Vector<Strong<JSCell>>> m_pendingPromises;
     70    Lock m_taskLock;
     71    bool m_runTasks { true };
    6172#if USE(CF)
    62     JS_EXPORT_PRIVATE void setRunLoop(CFRunLoopRef);
    63 #endif // USE(CF)
    64    
    65 protected:
    66     VM* m_vm;
    67 
    68     RefPtr<JSLock> m_apiLock;
    69     bool m_isScheduled { false };
    70 #if USE(CF)
    71     static const CFTimeInterval s_decade;
    72 
    73     RetainPtr<CFRunLoopTimerRef> m_timer;
    74     RetainPtr<CFRunLoopRef> m_runLoop;
    75    
    76     CFRunLoopTimerContext m_context;
    77 
    78     Lock m_shutdownMutex;
    79 #elif USE(GLIB)
    80     static const long s_decade;
    81     void timerDidFire();
    82     GRefPtr<GSource> m_timer;
     73    bool m_shouldStopRunLoopWhenAllPromisesFinish { false };
    8374#endif
    84    
    85 private:
    86     void invalidate();
     75    Vector<std::tuple<JSPromiseDeferred*, Task>> m_tasks;
     76    HashMap<JSPromiseDeferred*, Vector<Task>> m_blockedTasks;
    8777};
    8878
Note: See TracChangeset for help on using the changeset viewer.