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 676129 c504f7082a1f4238a7951131b6a793e6c41e198a
parent 676128 d252581d930e60eb0be420c77aeaa402d4b9d25e
child 676130 f61947de473fa09f3a8aba90ee6eab92d5f0ad80
push id83398
push userbmo:rail@mozilla.com
push dateFri, 06 Oct 2017 17:12:44 +0000
reviewerstill
bugs1347644
milestone58.0a1
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;