Bug 1347644 - Baldr: refactor promise compile tasks in prepration for streaming (r=till)
authorLuke Wagner <luke@mozilla.com>
Fri, 06 Oct 2017 10:40:44 -0500
changeset 384758 c504f7082a1f4238a7951131b6a793e6c41e198a
parent 384757 d252581d930e60eb0be420c77aeaa402d4b9d25e
child 384759 f61947de473fa09f3a8aba90ee6eab92d5f0ad80
push id32634
push userkwierso@gmail.com
push dateFri, 06 Oct 2017 19:55:44 +0000
treeherdermozilla-central@2d7b8b5dd174 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstill
bugs1347644
milestone58.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1347644 - Baldr: refactor promise compile tasks in prepration for streaming (r=till) * * * [mq]: blah MozReview-Commit-ID: 6n7zT8hdnZD
js/src/builtin/Promise.cpp
js/src/builtin/Promise.h
js/src/vm/HelperThreads.cpp
js/src/vm/HelperThreads.h
js/src/wasm/WasmJS.cpp
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -3390,17 +3390,17 @@ PromiseObject::onSettled(JSContext* cx, 
     Debugger::onPromiseSettled(cx, promise);
 }
 
 OffThreadPromiseTask::OffThreadPromiseTask(JSContext* cx, Handle<PromiseObject*> promise)
   : runtime_(cx->runtime()),
     promise_(cx, promise),
     registered_(false)
 {
-    MOZ_ASSERT(runtime_ == promise->zone()->runtimeFromActiveCooperatingThread());
+    MOZ_ASSERT(runtime_ == promise_->zone()->runtimeFromActiveCooperatingThread());
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime_));
     MOZ_ASSERT(cx->runtime()->offThreadPromiseState.ref().initialized());
 }
 
 OffThreadPromiseTask::~OffThreadPromiseTask()
 {
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime_));
 
@@ -3449,17 +3449,17 @@ OffThreadPromiseTask::run(JSContext* cx,
         if (!resolve(cx, promise_))
             cx->clearPendingException();
     }
 
     js_delete(this);
 }
 
 void
-OffThreadPromiseTask::dispatchResolve()
+OffThreadPromiseTask::dispatchResolveAndDestroy()
 {
     MOZ_ASSERT(registered_);
 
     OffThreadPromiseRuntimeState& state = runtime_->offThreadPromiseState.ref();
     MOZ_ASSERT(state.initialized());
     MOZ_ASSERT((LockGuard<Mutex>(state.mutex_), state.live_.has(this)));
 
     // If the dispatch succeeds, then we are guaranteed that run() will be
--- a/js/src/builtin/Promise.h
+++ b/js/src/builtin/Promise.h
@@ -182,24 +182,28 @@ class OffThreadPromiseTask : public JS::
     // To be called by OffThreadPromiseTask and implemented by the derived class.
     virtual bool resolve(JSContext* cx, Handle<PromiseObject*> promise) = 0;
 
     // JS::Dispatchable implementation. Ends with 'delete this'.
     void run(JSContext* cx, MaybeShuttingDown maybeShuttingDown) override final;
 
   public:
     ~OffThreadPromiseTask() override;
+
+    // Initializing an OffThreadPromiseTask informs the runtime that it must
+    // wait on shutdown for this task to rejoin the active JSContext by calling
+    // dispatchResolveAndDestroy().
     bool init(JSContext* cx);
 
     // An initialized OffThreadPromiseTask can be dispatched to an active
     // JSContext of its Promise's JSRuntime from any thread. Normally, this will
     // lead to resolve() being called on JSContext thread, given the Promise.
     // However, if shutdown interrupts, resolve() may not be called, though the
     // OffThreadPromiseTask will be destroyed on a JSContext thread.
-    void dispatchResolve();
+    void dispatchResolveAndDestroy();
 };
 
 using OffThreadPromiseTaskSet = HashSet<OffThreadPromiseTask*,
                                         DefaultHasher<OffThreadPromiseTask*>,
                                         SystemAllocPolicy>;
 
 using DispatchableVector = Vector<JS::Dispatchable*, 0, SystemAllocPolicy>;
 
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -1845,17 +1845,17 @@ HelperThread::handlePromiseHelperTaskWor
     MOZ_ASSERT(idle());
 
     PromiseHelperTask* task = HelperThreadState().promiseHelperTasks(locked).popCopy();
     currentTask.emplace(task);
 
     {
         AutoUnlockHelperThreadState unlock(locked);
         task->execute();
-        task->dispatchResolve();
+        task->dispatchResolveAndDestroy();
     }
 
     // No active thread should be waiting on the CONSUMER mutex.
     currentTask.reset();
 }
 
 void
 HelperThread::handleIonWorkload(AutoLockHelperThreadState& locked)
@@ -2091,28 +2091,28 @@ js::CancelOffThreadCompressions(JSRuntim
         HelperThreadState().wait(lock, GlobalHelperThreadState::CONSUMER);
     }
 
     // Clean up finished tasks.
     ClearCompressionTaskList(HelperThreadState().compressionFinishedList(lock), runtime);
 }
 
 void
-PromiseHelperTask::executeAndResolve(JSContext* cx)
+PromiseHelperTask::executeAndResolveAndDestroy(JSContext* cx)
 {
     execute();
     run(cx, JS::Dispatchable::NotShuttingDown);
 }
 
 bool
 js::StartOffThreadPromiseHelperTask(JSContext* cx, UniquePtr<PromiseHelperTask> task)
 {
     // Execute synchronously if there are no helper threads.
     if (!CanUseExtraThreads()) {
-        task.release()->executeAndResolve(cx);
+        task.release()->executeAndResolveAndDestroy(cx);
         return true;
     }
 
     AutoLockHelperThreadState lock;
 
     if (!HelperThreadState().promiseHelperTasks(lock).append(task.get())) {
         ReportOutOfMemory(cx);
         return false;
--- a/js/src/vm/HelperThreads.h
+++ b/js/src/vm/HelperThreads.h
@@ -790,14 +790,14 @@ struct PromiseHelperTask : OffThreadProm
       : OffThreadPromiseTask(cx, promise)
     {}
 
     // To be called on a helper thread and implemented by the derived class.
     virtual void execute() = 0;
 
     // May be called in the absence of helper threads or off-thread promise
     // support to synchronously execute and resolve a PromiseTask.
-    void executeAndResolve(JSContext* cx);
+    void executeAndResolveAndDestroy(JSContext* cx);
 };
 
 } /* namespace js */
 
 #endif /* vm_HelperThreads_h */
--- a/js/src/wasm/WasmJS.cpp
+++ b/js/src/wasm/WasmJS.cpp
@@ -850,24 +850,31 @@ GetBufferSource(JSContext* cx, JSObject*
     if (!(*bytecode)->append(ptr, byteLength)) {
         ReportOutOfMemory(cx);
         return false;
     }
 
     return true;
 }
 
-static bool
-InitCompileArgs(JSContext* cx, CompileArgs* compileArgs)
+static SharedCompileArgs
+InitCompileArgs(JSContext* cx)
 {
     ScriptedCaller scriptedCaller;
     if (!DescribeScriptedCaller(cx, &scriptedCaller))
-        return false;
+        return nullptr;
 
-    return compileArgs->initFromContext(cx, Move(scriptedCaller));
+    MutableCompileArgs compileArgs = cx->new_<CompileArgs>();
+    if (!compileArgs)
+        return nullptr;
+
+    if (!compileArgs->initFromContext(cx, Move(scriptedCaller)))
+        return nullptr;
+
+    return compileArgs;
 }
 
 /* static */ bool
 WasmModuleObject::construct(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs callArgs = CallArgsFromVp(argc, vp);
 
     if (!ThrowIfNotConstructing(cx, callArgs, "Module"))
@@ -880,18 +887,18 @@ WasmModuleObject::construct(JSContext* c
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_BUF_ARG);
         return false;
     }
 
     MutableBytes bytecode;
     if (!GetBufferSource(cx, &callArgs[0].toObject(), JSMSG_WASM_BAD_BUF_ARG, &bytecode))
         return false;
 
-    MutableCompileArgs compileArgs = cx->new_<CompileArgs>();
-    if (!compileArgs || !InitCompileArgs(cx, compileArgs.get()))
+    SharedCompileArgs compileArgs = InitCompileArgs(cx);
+    if (!compileArgs)
         return false;
 
     UniqueChars error;
     SharedModule module = CompileInitialTier(*bytecode, *compileArgs, &error);
     if (!module) {
         if (error) {
             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_COMPILE_ERROR,
                                       error.get());
@@ -1886,64 +1893,106 @@ Reject(JSContext* cx, const CompileArgs&
     if (!errorObj)
         return false;
 
     RootedValue rejectionValue(cx, ObjectValue(*errorObj));
     return PromiseObject::reject(cx, promise, rejectionValue);
 }
 
 static bool
-ResolveCompilation(JSContext* cx, Module& module, const CompileArgs& compileArgs,
-                   Handle<PromiseObject*> promise)
-{
-    RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmModule).toObject());
-    RootedObject moduleObj(cx, WasmModuleObject::create(cx, module, proto));
-    if (!moduleObj)
-        return false;
-
-    RootedValue resolutionValue(cx, ObjectValue(*moduleObj));
-    return PromiseObject::resolve(cx, promise, resolutionValue);
-}
-
-struct CompilePromiseTask : PromiseHelperTask
-{
-    MutableBytes      bytecode;
-    SharedCompileArgs compileArgs;
-    UniqueChars       error;
-    SharedModule      module;
-
-    CompilePromiseTask(JSContext* cx, Handle<PromiseObject*> promise)
-      : PromiseHelperTask(cx, promise)
-    {}
-
-    void execute() override {
-        module = CompileInitialTier(*bytecode, *compileArgs, &error);
-    }
-
-    bool resolve(JSContext* cx, Handle<PromiseObject*> promise) override {
-        return module
-               ? ResolveCompilation(cx, *module, *compileArgs, promise)
-               : Reject(cx, *compileArgs, Move(error), promise);
-    }
-};
-
-static bool
 RejectWithPendingException(JSContext* cx, Handle<PromiseObject*> promise)
 {
     if (!cx->isExceptionPending())
         return false;
 
     RootedValue rejectionValue(cx);
     if (!GetAndClearException(cx, &rejectionValue))
         return false;
 
     return PromiseObject::reject(cx, promise, rejectionValue);
 }
 
 static bool
+Resolve(JSContext* cx, Module& module, const CompileArgs& compileArgs,
+        Handle<PromiseObject*> promise, bool instantiate, HandleObject importObj)
+{
+    RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmModule).toObject());
+    RootedObject moduleObj(cx, WasmModuleObject::create(cx, module, proto));
+    if (!moduleObj)
+        return RejectWithPendingException(cx, promise);
+
+    RootedValue resolutionValue(cx);
+    if (instantiate) {
+        RootedWasmInstanceObject instanceObj(cx);
+        if (!Instantiate(cx, module, importObj, &instanceObj))
+            return RejectWithPendingException(cx, promise);
+
+        RootedObject resultObj(cx, JS_NewPlainObject(cx));
+        if (!resultObj)
+            return RejectWithPendingException(cx, promise);
+
+        RootedValue val(cx, ObjectValue(*moduleObj));
+        if (!JS_DefineProperty(cx, resultObj, "module", val, JSPROP_ENUMERATE))
+            return RejectWithPendingException(cx, promise);
+
+        val = ObjectValue(*instanceObj);
+        if (!JS_DefineProperty(cx, resultObj, "instance", val, JSPROP_ENUMERATE))
+            return RejectWithPendingException(cx, promise);
+
+        resolutionValue = ObjectValue(*resultObj);
+    } else {
+        MOZ_ASSERT(!importObj);
+        resolutionValue = ObjectValue(*moduleObj);
+    }
+
+    if (!PromiseObject::resolve(cx, promise, resolutionValue))
+        return RejectWithPendingException(cx, promise);
+
+    return true;
+}
+
+struct CompileBufferTask : PromiseHelperTask
+{
+    MutableBytes           bytecode;
+    SharedCompileArgs      compileArgs;
+    UniqueChars            error;
+    SharedModule           module;
+    bool                   instantiate;
+    PersistentRootedObject importObj;
+
+    CompileBufferTask(JSContext* cx, Handle<PromiseObject*> promise, HandleObject importObj)
+      : PromiseHelperTask(cx, promise),
+        instantiate(true),
+        importObj(cx, importObj)
+    {}
+
+    CompileBufferTask(JSContext* cx, Handle<PromiseObject*> promise)
+      : PromiseHelperTask(cx, promise),
+        instantiate(false)
+    {}
+
+    bool init(JSContext* cx) {
+        compileArgs = InitCompileArgs(cx);
+        if (!compileArgs)
+            return false;
+        return PromiseHelperTask::init(cx);
+    }
+
+    void execute() override {
+        module = CompileInitialTier(*bytecode, *compileArgs, &error);
+    }
+
+    bool resolve(JSContext* cx, Handle<PromiseObject*> promise) override {
+        return module
+               ? Resolve(cx, *module, *compileArgs, promise, instantiate, importObj)
+               : Reject(cx, *compileArgs, Move(error), promise);
+    }
+};
+
+static bool
 RejectWithPendingException(JSContext* cx, Handle<PromiseObject*> promise, CallArgs& callArgs)
 {
     if (!RejectWithPendingException(cx, promise))
         return false;
 
     callArgs.rval().setObject(*promise);
     return true;
 }
@@ -1977,93 +2026,33 @@ WebAssembly_compile(JSContext* cx, unsig
 {
     if (!EnsurePromiseSupport(cx))
         return false;
 
     Rooted<PromiseObject*> promise(cx, PromiseObject::createSkippingExecutor(cx));
     if (!promise)
         return false;
 
-    auto task = cx->make_unique<CompilePromiseTask>(cx, promise);
+    auto task = cx->make_unique<CompileBufferTask>(cx, promise);
     if (!task || !task->init(cx))
         return false;
 
     CallArgs callArgs = CallArgsFromVp(argc, vp);
 
     if (!GetBufferSource(cx, callArgs, "WebAssembly.compile", &task->bytecode))
         return RejectWithPendingException(cx, promise, callArgs);
 
-    MutableCompileArgs compileArgs = cx->new_<CompileArgs>();
-    if (!compileArgs || !InitCompileArgs(cx, compileArgs))
-        return false;
-    task->compileArgs = compileArgs;
-
     if (!StartOffThreadPromiseHelperTask(cx, Move(task)))
         return false;
 
     callArgs.rval().setObject(*promise);
     return true;
 }
 
 static bool
-ResolveInstantiation(JSContext* cx, Module& module, const CompileArgs& compileArgs,
-                     HandleObject importObj, Handle<PromiseObject*> promise)
-{
-    RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmModule).toObject());
-    RootedObject moduleObj(cx, WasmModuleObject::create(cx, module, proto));
-    if (!moduleObj)
-        return false;
-
-    RootedWasmInstanceObject instanceObj(cx);
-    if (!Instantiate(cx, module, importObj, &instanceObj))
-        return RejectWithPendingException(cx, promise);
-
-    RootedObject resultObj(cx, JS_NewPlainObject(cx));
-    if (!resultObj)
-        return false;
-
-    RootedValue val(cx, ObjectValue(*moduleObj));
-    if (!JS_DefineProperty(cx, resultObj, "module", val, JSPROP_ENUMERATE))
-        return false;
-
-    val = ObjectValue(*instanceObj);
-    if (!JS_DefineProperty(cx, resultObj, "instance", val, JSPROP_ENUMERATE))
-        return false;
-
-    val = ObjectValue(*resultObj);
-    return PromiseObject::resolve(cx, promise, val);
-}
-
-struct InstantiatePromiseTask : PromiseHelperTask
-{
-    MutableBytes           bytecode;
-    SharedCompileArgs      compileArgs;
-    UniqueChars            error;
-    SharedModule           module;
-    PersistentRootedObject importObj;
-
-    InstantiatePromiseTask(JSContext* cx, Handle<PromiseObject*> promise,
-                           const CompileArgs& compileArgs, HandleObject importObj)
-      : PromiseHelperTask(cx, promise),
-        compileArgs(&compileArgs),
-        importObj(cx, importObj)
-    {}
-
-    void execute() override {
-        module = CompileInitialTier(*bytecode, *compileArgs, &error);
-    }
-
-    bool resolve(JSContext* cx, Handle<PromiseObject*> promise) override {
-        return module
-               ? ResolveInstantiation(cx, *module, *compileArgs, importObj, promise)
-               : Reject(cx, *compileArgs, Move(error), promise);
-    }
-};
-
-static bool
 GetInstantiateArgs(JSContext* cx, CallArgs callArgs, MutableHandleObject firstArg,
                    MutableHandleObject importObj)
 {
     if (!callArgs.requireAtLeast(cx, "WebAssembly.instantiate", 1))
         return false;
 
     if (!callArgs[0].isObject()) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_BUF_MOD_ARG);
@@ -2097,21 +2086,17 @@ WebAssembly_instantiate(JSContext* cx, u
         RootedWasmInstanceObject instanceObj(cx);
         if (!Instantiate(cx, *module, importObj, &instanceObj))
             return RejectWithPendingException(cx, promise, callArgs);
 
         RootedValue resolutionValue(cx, ObjectValue(*instanceObj));
         if (!PromiseObject::resolve(cx, promise, resolutionValue))
             return false;
     } else {
-        MutableCompileArgs compileArgs = cx->new_<CompileArgs>();
-        if (!compileArgs || !InitCompileArgs(cx, compileArgs.get()))
-            return false;
-
-        auto task = cx->make_unique<InstantiatePromiseTask>(cx, promise, *compileArgs, importObj);
+        auto task = cx->make_unique<CompileBufferTask>(cx, promise, importObj);
         if (!task || !task->init(cx))
             return false;
 
         if (!GetBufferSource(cx, firstArg, JSMSG_WASM_BAD_BUF_MOD_ARG, &task->bytecode))
             return RejectWithPendingException(cx, promise, callArgs);
 
         if (!StartOffThreadPromiseHelperTask(cx, Move(task)))
             return false;