commit 52e447e5854d0298d1a9545e1e4941ff2df33beb
parent 4c47a4eb09689eb87cb1d2fd327cdbe781f73266
Author: Cristina Horotan <chorotan@mozilla.com>
Date: Sat, 13 Dec 2025 01:08:37 +0200
Revert "Bug 1988289 - Don't terminate worker when there are pending JS async tasks. r=iain,asuth,dom-worker-reviewers,smaug,edenchuang" for causing SM failures
This reverts commit 54d5bd08f37314363b2b6de2fec6ac389b8bc2ce.
Diffstat:
9 files changed, 69 insertions(+), 284 deletions(-)
diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp
@@ -1806,9 +1806,8 @@ void nsJSContext::EnsureStatics() {
JS::SetCreateGCSliceBudgetCallback(jsapi.cx(), CreateGCSliceBudget);
- JS::InitAsyncTaskCallbacks(jsapi.cx(), DispatchToEventLoop,
- DelayedDispatchToEventLoop, nullptr, nullptr,
- nullptr);
+ JS::InitDispatchsToEventLoop(jsapi.cx(), DispatchToEventLoop,
+ DelayedDispatchToEventLoop, nullptr);
JS::InitConsumeStreamCallback(jsapi.cx(), ConsumeStream,
FetchUtil::ReportJSStreamError);
diff --git a/dom/workers/RuntimeService.cpp b/dom/workers/RuntimeService.cpp
@@ -345,11 +345,9 @@ void LoadJSGCMemoryOptions(const char* aPrefName, void* /* aClosure */) {
JSGCParamKey key;
};
-#define PREF(suffix_, key_) \
- { \
- nsLiteralCString(suffix_), \
- PREF_JS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX suffix_, key_ \
- }
+#define PREF(suffix_, key_) \
+ {nsLiteralCString(suffix_), \
+ PREF_JS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX suffix_, key_}
constexpr WorkerGCPref kWorkerPrefs[] = {
PREF("max", JSGC_MAX_BYTES),
PREF("gc_high_frequency_time_limit_ms", JSGC_HIGH_FREQUENCY_TIME_LIMIT),
@@ -727,22 +725,6 @@ static bool DelayedDispatchToEventLoop(
return true;
}
-static void AsyncTaskStarted(void* aClosure, JS::Dispatchable* aDispatchable) {
- // See comment at JS::InitDispatchsToEventLoop() below for how we know the
- // WorkerPrivate is alive.
- WorkerPrivate* workerPrivate = reinterpret_cast<WorkerPrivate*>(aClosure);
- workerPrivate->AssertIsOnWorkerThread();
- workerPrivate->JSAsyncTaskStarted(aDispatchable);
-}
-
-static void AsyncTaskFinished(void* aClosure, JS::Dispatchable* aDispatchable) {
- // See comment at JS::InitDispatchsToEventLoop() below for how we know the
- // WorkerPrivate is alive.
- WorkerPrivate* workerPrivate = reinterpret_cast<WorkerPrivate*>(aClosure);
- workerPrivate->AssertIsOnWorkerThread();
- workerPrivate->JSAsyncTaskFinished(aDispatchable);
-}
-
static bool ConsumeStream(JSContext* aCx, JS::Handle<JSObject*> aObj,
JS::MimeType aMimeType,
JS::StreamConsumer* aConsumer) {
@@ -784,9 +766,9 @@ bool InitJSContextForWorker(WorkerPrivate* aWorkerPrivate,
// A WorkerPrivate lives strictly longer than its JSRuntime so we can safely
// store a raw pointer as the callback's closure argument on the JSRuntime.
- JS::InitAsyncTaskCallbacks(aWorkerCx, DispatchToEventLoop,
- DelayedDispatchToEventLoop, AsyncTaskStarted,
- AsyncTaskFinished, (void*)aWorkerPrivate);
+ JS::InitDispatchsToEventLoop(aWorkerCx, DispatchToEventLoop,
+ DelayedDispatchToEventLoop,
+ (void*)aWorkerPrivate);
JS::InitConsumeStreamCallback(aWorkerCx, ConsumeStream,
FetchUtil::ReportJSStreamError);
diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp
@@ -2999,7 +2999,6 @@ WorkerPrivate::WorkerPrivate(
WorkerPrivate::~WorkerPrivate() {
MOZ_DIAGNOSTIC_ASSERT(mTopLevelWorkerFinishedRunnableCount == 0);
MOZ_DIAGNOSTIC_ASSERT(mWorkerFinishedRunnableCount == 0);
- MOZ_DIAGNOSTIC_ASSERT(mPendingJSAsyncTasks.empty());
mWorkerDebuggerEventTarget->ForgetWorkerPrivate(this);
@@ -5041,19 +5040,6 @@ nsresult WorkerPrivate::UnregisterShutdownTask(nsITargetShutdownTask* aTask) {
return mShutdownTasks.RemoveTask(aTask);
}
-void WorkerPrivate::JSAsyncTaskStarted(JS::Dispatchable* aDispatchable) {
- RefPtr<StrongWorkerRef> ref = StrongWorkerRef::Create(this, "JSAsyncTask");
- MOZ_ASSERT_DEBUG_OR_FUZZING(ref);
- if (NS_WARN_IF(!ref)) {
- return;
- }
- MOZ_ALWAYS_TRUE(mPendingJSAsyncTasks.putNew(aDispatchable, std::move(ref)));
-}
-
-void WorkerPrivate::JSAsyncTaskFinished(JS::Dispatchable* aDispatchable) {
- mPendingJSAsyncTasks.remove(aDispatchable);
-}
-
void WorkerPrivate::RunShutdownTasks() {
TargetShutdownTaskSet::TasksArray shutdownTasks;
@@ -5810,19 +5796,6 @@ bool WorkerPrivate::NotifyInternal(WorkerStatus aStatus) {
if (aStatus >= Closing) {
CancelAllTimeouts();
-
- JSContext* cx = GetJSContext();
- if (cx) {
- // This will invoke the JS async task finished callback for cancellable
- // JS tasks, which will invoke JSAsyncTaskFinished and remove from
- // mPendingJSAsyncTasks.
- //
- // There may still be outstanding JS tasks for things that couldn't be
- // cancelled. These must either finish normally, or be blocked on
- // through a call to JS::ShutdownAsyncTasks. Cycle collector shutdown
- // will call this during worker shutdown.
- JS::CancelAsyncTasks(cx);
- }
}
if (aStatus == Closing && GlobalScope()) {
diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h
@@ -61,8 +61,7 @@ class nsIThreadInternal;
namespace JS {
struct RuntimeStats;
-class Dispatchable;
-} // namespace JS
+}
namespace mozilla {
class ThrottledEventQueue;
@@ -1209,9 +1208,6 @@ class WorkerPrivate final
void IncreaseWorkerFinishedRunnableCount() { ++mWorkerFinishedRunnableCount; }
void DecreaseWorkerFinishedRunnableCount() { --mWorkerFinishedRunnableCount; }
- void JSAsyncTaskStarted(JS::Dispatchable* aDispatchable);
- void JSAsyncTaskFinished(JS::Dispatchable* aDispatchable);
-
void RunShutdownTasks();
bool CancelBeforeWorkerScopeConstructed() const {
@@ -1719,9 +1715,6 @@ class WorkerPrivate final
Atomic<uint32_t> mTopLevelWorkerFinishedRunnableCount;
Atomic<uint32_t> mWorkerFinishedRunnableCount;
- // A set of active JS async tasks that should prevent idle shutdown.
- HashMap<JS::Dispatchable*, RefPtr<StrongWorkerRef>> mPendingJSAsyncTasks;
-
TargetShutdownTaskSet mShutdownTasks MOZ_GUARDED_BY(mMutex);
bool mShutdownTasksRun MOZ_GUARDED_BY(mMutex) = false;
diff --git a/dom/worklet/WorkletThread.cpp b/dom/worklet/WorkletThread.cpp
@@ -387,9 +387,9 @@ void WorkletThread::EnsureCycleCollectedJSContext(
// A thread lives strictly longer than its JSRuntime so we can safely
// store a raw pointer as the callback's closure argument on the JSRuntime.
- JS::InitAsyncTaskCallbacks(context->Context(), DispatchToEventLoop,
- DelayedDispatchToEventLoop, nullptr, nullptr,
- NS_GetCurrentThread());
+ JS::InitDispatchsToEventLoop(context->Context(), DispatchToEventLoop,
+ DelayedDispatchToEventLoop,
+ NS_GetCurrentThread());
JS_SetNativeStackQuota(context->Context(),
WORKLET_CONTEXT_NATIVE_STACK_LIMIT);
diff --git a/js/public/Promise.h b/js/public/Promise.h
@@ -664,7 +664,7 @@ class JS_PUBLIC_API Dispatchable {
};
/**
- * Callback to dispatch a JS::Dispatchable to a JSContext's thread's event
+ * Callbacks to dispatch a JS::Dispatchable to a JSContext's thread's event
* loop.
*
* The DispatchToEventLoopCallback set on a particular JSContext must accept
@@ -680,9 +680,6 @@ class JS_PUBLIC_API Dispatchable {
* If a timeout manager is not available for given context, it should return
* false, optionally with a warning message printed.
*
- * The callback accepts a closure that is the same as the one given by the call
- * to JS::InitAsyncTaskCallbacks.
- *
* If the callback returns `true`, it must eventually run the given
* Dispatchable; otherwise, SpiderMonkey may leak memory or hang.
*
@@ -690,113 +687,30 @@ class JS_PUBLIC_API Dispatchable {
* shutting down and is no longer accepting runnables. Shutting down is a
* one-way transition: once the callback has rejected a runnable, it must reject
* all subsequently submitted runnables as well.
+ *
+ * To establish a DispatchToEventLoopCallback, the embedding may either call
+ * InitDispatchsToEventLoop to provide its own, or call js::UseInternalJobQueues
+ * to select a default implementation built into SpiderMonkey. This latter
+ * depends on the embedding to call js::RunJobs on the JavaScript thread to
+ * process queued Dispatchables at appropriate times.
*/
typedef bool (*DispatchToEventLoopCallback)(
void* closure, js::UniquePtr<Dispatchable>&& dispatchable);
-/**
- * Callback to dispatch a JS::Dispatchable to a JSContext's thread's event
- * loop with a delay.
- *
- * The DelayedDispatchToEventLoopCallback set on a particular JSContext must
- * accept JS::Dispatchable instances and arrange for their `run` methods to be
- * called after the given delay on the JSContext's thread. This is used for
- * cross-thread dispatch, so the callback itself must be safe to call from any
- * thread. It cannot trigger a GC.
- *
- * The embeddings must have its own timeout manager to handle the delay.
- * If a timeout manager is not available for given context, it should return
- * false, optionally with a warning message printed.
- *
- * The callback accepts a closure that is the same as the one given by the call
- * to JS::InitAsyncTaskCallbacks.
- *
- * If the callback returns `true`, it must eventually run the given
- * Dispatchable; otherwise, SpiderMonkey may leak memory or hang.
- *
- * The callback may return `false` to indicate that the JSContext's thread is
- * shutting down and is no longer accepting runnables. Shutting down is a
- * one-way transition: once the callback has rejected a runnable, it must reject
- * all subsequently submitted runnables as well.
- */
-
typedef bool (*DelayedDispatchToEventLoopCallback)(
void* closure, js::UniquePtr<Dispatchable>&& dispatchable, uint32_t delay);
-/**
- * Callback to notify the embedder that a background task has begun. This can
- * be used to keep the JSContext's thread alive if it's subject to garbage
- * collection (e.g. as web workers are).
- *
- * This callback will be called on the same thread as the JSContext, but it
- * must not perform any garbage collection or re-enter the engine.
- *
- * The passed dispatchable can be used to track the async task. Most async
- * tasks will finish in a bounded amount of time, but some
- * (i.e. Atomics.waitAsync) are not guaranteed to finish. These tasks may be
- * canceled using JS::CancelAsyncTasks.
- *
- * The callback accepts a closure that is the same as the one given by the call
- * to JS::InitAsyncTaskCallbacks.
- */
-typedef void (*AsyncTaskStartedCallback)(void* closure,
- Dispatchable* dispatchable);
-
-/**
- * Callback to notify the embedder that a background task has finished. This can
- * be used to keep the JSContext's thread alive if it's subject to garbage
- * collection (e.g. as web workers are).
- *
- * This callback will be called on the same thread as the JSContext, but it
- * must not perform any garbage collection or re-enter the engine.
- *
- * The passed dispatchable will be the same as the one given in
- * AsyncTaskStartedCallback. See that callback for more information.
- *
- * The callback accepts a closure that is the same as the one given by the call
- * to JS::InitAsyncTaskCallbacks.
- */
-typedef void (*AsyncTaskFinishedCallback)(void* closure,
- Dispatchable* dispatchable);
-
-/**
- * Initialize the async task callbacks. Embedders must use this or else call
- * js::UseInternalJobQueues to select a default implementation built into
- * SpiderMonkey. This latter depends on the embedding to call js::RunJobs on
- * the JavaScript thread to process queued Dispatchables at appropriate times.
- *
- * The dispatchCallback and delayedDispatchCallback are mandatory.
- * asyncTaskStartedCallback and asyncTaskFinishedCallback are optional and may
- * be null.
- *
- */
-extern JS_PUBLIC_API void InitAsyncTaskCallbacks(
- JSContext* cx, DispatchToEventLoopCallback dispatchCallback,
- DelayedDispatchToEventLoopCallback delayedDispatchCallback,
- AsyncTaskStartedCallback asyncTaskStartedCallback,
- AsyncTaskFinishedCallback asyncTaskFinishedCallback, void* closure);
-
-/**
- * Cancel all cancellable async tasks. Some tasks may not be cancellable, and
- * must be waited on through a call to JS::ShutdownAsyncTasks.
- *
- * This will fire JSAsyncTaskFinished callbacks for cancelled tasks.
- */
-extern JS_PUBLIC_API void CancelAsyncTasks(JSContext* cx);
+extern JS_PUBLIC_API void InitDispatchsToEventLoop(
+ JSContext* cx, DispatchToEventLoopCallback callback,
+ DelayedDispatchToEventLoopCallback delayedCallback, void* closure);
/**
- * Cancel all cancellable async tasks and block until non-cancellable tasks are
- * finished.
- *
- * This will fire JSAsyncTaskFinished callbacks for all tasks, and there should
- * be no outstanding tasks after this returns. All roots held by tasks will be
- * released.
- *
- * Destroying a JSRuntime will implicitly perform this. However, this is not
- * soon enough for cycle collection, which needs to have roots dropped earlier
- * so that the cycle collector can transitively remove roots for a future GC.
- * For these and other cases, the set of pending async tasks can be canceled
+ * When a JSRuntime is destroyed it implicitly cancels all async tasks in
+ * progress, releasing any roots held by the task. However, this is not soon
+ * enough for cycle collection, which needs to have roots dropped earlier so
+ * that the cycle collector can transitively remove roots for a future GC. For
+ * these and other cases, the set of pending async tasks can be canceled
* with this call earlier than JSRuntime destruction.
*/
diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp
@@ -3179,18 +3179,11 @@ JS_PUBLIC_API JSObject* JS::GetWaitForAllPromise(
return js::GetWaitForAllPromise(cx, promises);
}
-JS_PUBLIC_API void JS::InitAsyncTaskCallbacks(
- JSContext* cx, JS::DispatchToEventLoopCallback dispatchCallback,
- JS::DelayedDispatchToEventLoopCallback delayedDispatchCallback,
- JS::AsyncTaskStartedCallback asyncTaskStartedCallback,
- JS::AsyncTaskFinishedCallback asyncTaskFinishedCallback, void* closure) {
- cx->runtime()->offThreadPromiseState.ref().init(
- dispatchCallback, delayedDispatchCallback, asyncTaskStartedCallback,
- asyncTaskFinishedCallback, closure);
-}
-
-JS_PUBLIC_API void JS::CancelAsyncTasks(JSContext* cx) {
- cx->runtime()->offThreadPromiseState.ref().cancelTasks(cx);
+JS_PUBLIC_API void JS::InitDispatchsToEventLoop(
+ JSContext* cx, JS::DispatchToEventLoopCallback callback,
+ JS::DelayedDispatchToEventLoopCallback delayCallback, void* closure) {
+ cx->runtime()->offThreadPromiseState.ref().init(callback, delayCallback,
+ closure);
}
JS_PUBLIC_API void JS::ShutdownAsyncTasks(JSContext* cx) {
diff --git a/js/src/vm/OffThreadPromiseRuntimeState.cpp b/js/src/vm/OffThreadPromiseRuntimeState.cpp
@@ -63,7 +63,8 @@ bool OffThreadPromiseTask::init(JSContext* cx,
OffThreadPromiseRuntimeState& state = runtime_->offThreadPromiseState.ref();
MOZ_ASSERT(state.initialized());
- state.registerTask(cx, this);
+ state.numRegistered_++;
+ registered_ = true;
return true;
}
@@ -87,20 +88,14 @@ bool OffThreadPromiseTask::InitCancellable(
return false;
}
- if (!state.cancellable().putNew(task.get())) {
+ OffThreadPromiseTask* rawTask = task.release();
+ if (!state.cancellable().putNew(rawTask)) {
+ state.numRegistered_--;
+ rawTask->registered_ = false;
ReportOutOfMemory(cx);
- // The task will be freed and unregistered because it's only owned by this
- // function.
return false;
}
-
- // We're infallible from this point on.
- OffThreadPromiseTask* rawTask = task.release();
-
- // Only mark the task as cancellable once we've added it to the cancellable
- // set. The destructor will remove from the set if this flag is set.
rawTask->cancellable_ = true;
-
return true;
}
@@ -111,7 +106,12 @@ void OffThreadPromiseTask::unregister(OffThreadPromiseRuntimeState& state) {
void OffThreadPromiseTask::unregister(OffThreadPromiseRuntimeState& state,
const AutoLockHelperThreadState& lock) {
MOZ_ASSERT(registered_);
- state.unregisterTask(this);
+ if (cancellable_) {
+ cancellable_ = false;
+ state.cancellable().remove(this);
+ }
+ state.numRegistered_--;
+ registered_ = false;
}
void OffThreadPromiseTask::run(JSContext* cx,
@@ -229,7 +229,8 @@ void OffThreadPromiseTask::DispatchResolveAndDestroy(
{
// Hazard analysis can't tell that the callback does not GC.
JS::AutoSuppressGCAnalysis nogc;
- if (state.dispatchToEventLoop(std::move(task))) {
+ if (state.dispatchToEventLoopCallback_(state.dispatchToEventLoopClosure_,
+ std::move(task))) {
return;
}
}
@@ -247,8 +248,6 @@ void OffThreadPromiseTask::DispatchResolveAndDestroy(
OffThreadPromiseRuntimeState::OffThreadPromiseRuntimeState()
: dispatchToEventLoopCallback_(nullptr),
delayedDispatchToEventLoopCallback_(nullptr),
- asyncTaskStartedCallback_(nullptr),
- asyncTaskFinishedCallback_(nullptr),
dispatchToEventLoopClosure_(nullptr),
#ifdef DEBUG
forceQuitting_(false),
@@ -265,16 +264,12 @@ OffThreadPromiseRuntimeState::~OffThreadPromiseRuntimeState() {
}
void OffThreadPromiseRuntimeState::init(
- JS::DispatchToEventLoopCallback dispatchCallback,
- JS::DelayedDispatchToEventLoopCallback delayedDispatchCallback,
- JS::AsyncTaskStartedCallback asyncTaskStartedCallback,
- JS::AsyncTaskFinishedCallback asyncTaskFinishedCallback, void* closure) {
+ JS::DispatchToEventLoopCallback callback,
+ JS::DelayedDispatchToEventLoopCallback delayedCallback, void* closure) {
MOZ_ASSERT(!initialized());
- dispatchToEventLoopCallback_ = dispatchCallback;
- delayedDispatchToEventLoopCallback_ = delayedDispatchCallback;
- asyncTaskStartedCallback_ = asyncTaskStartedCallback;
- asyncTaskFinishedCallback_ = asyncTaskFinishedCallback;
+ dispatchToEventLoopCallback_ = callback;
+ delayedDispatchToEventLoopCallback_ = delayedCallback;
dispatchToEventLoopClosure_ = closure;
MOZ_ASSERT(initialized());
@@ -292,46 +287,6 @@ bool OffThreadPromiseRuntimeState::delayedDispatchToEventLoop(
std::move(dispatchable), delay);
}
-void OffThreadPromiseRuntimeState::registerTask(JSContext* cx,
- OffThreadPromiseTask* task) {
- // Track the total number of pending async tasks
- numRegistered_++;
-
- // Mark the task as registered
- task->registered_ = true;
-
- if (!asyncTaskStartedCallback_) {
- return;
- }
-
- // The embedder must not perform a GC, suppress GC analysis here.
- JS::AutoSuppressGCAnalysis nogc(cx);
- asyncTaskStartedCallback_(dispatchToEventLoopClosure_, task);
-}
-
-void OffThreadPromiseRuntimeState::unregisterTask(OffThreadPromiseTask* task) {
- // Track the total number of pending async tasks
- MOZ_ASSERT(numRegistered_ != 0);
- numRegistered_--;
-
- // Mark the task as unregistered
- task->registered_ = false;
-
- // If the task was cancellable, remove from our cancellable set.
- if (task->cancellable_) {
- task->cancellable_ = false;
- cancellable().remove(task);
- }
-
- if (!asyncTaskFinishedCallback_) {
- return;
- }
-
- // The embedder must not perform a GC, suppress GC analysis here.
- JS::AutoSuppressGCAnalysis nogc;
- asyncTaskFinishedCallback_(dispatchToEventLoopClosure_, task);
-}
-
/* static */
bool OffThreadPromiseRuntimeState::internalDispatchToEventLoop(
void* closure, js::UniquePtr<JS::Dispatchable>&& d) {
@@ -425,8 +380,7 @@ bool OffThreadPromiseRuntimeState::usingInternalDispatchQueue() const {
}
void OffThreadPromiseRuntimeState::initInternalDispatchQueue() {
- init(internalDispatchToEventLoop, internalDelayedDispatchToEventLoop, nullptr,
- nullptr, this);
+ init(internalDispatchToEventLoop, internalDelayedDispatchToEventLoop, this);
MOZ_ASSERT(usingInternalDispatchQueue());
}
@@ -492,14 +446,14 @@ void OffThreadPromiseRuntimeState::stealFailedTask(JS::Dispatchable* task) {
}
}
-void OffThreadPromiseRuntimeState::cancelTasks(
- js::AutoLockHelperThreadState& lock, JSContext* cx) {
- MOZ_ASSERT(initialized());
+void OffThreadPromiseRuntimeState::shutdown(JSContext* cx) {
if (!initialized()) {
return;
}
- // Cancel all undispatched tasks that are cancellable.
+ AutoLockHelperThreadState lock;
+
+ // Cancel all undispatched tasks.
for (auto iter = cancellable().modIter(); !iter.done(); iter.next()) {
OffThreadPromiseTask* task = iter.get();
MOZ_ASSERT(task->cancellable_);
@@ -507,26 +461,6 @@ void OffThreadPromiseRuntimeState::cancelTasks(
OffThreadPromiseTask::DestroyUndispatchedTask(task, *this, lock);
}
-}
-
-void OffThreadPromiseRuntimeState::cancelTasks(JSContext* cx) {
- if (!initialized()) {
- return;
- }
-
- AutoLockHelperThreadState lock;
- cancelTasks(lock, cx);
-}
-
-void OffThreadPromiseRuntimeState::shutdown(JSContext* cx) {
- if (!initialized()) {
- return;
- }
-
- AutoLockHelperThreadState lock;
-
- // Cancel all undispatched tasks.
- cancelTasks(lock, cx);
MOZ_ASSERT(cancellable().empty());
// When the shell is using the internal event loop, we must simulate our
@@ -602,11 +536,18 @@ js::PromiseObject* OffThreadPromiseTask::ExtractAndForget(
task->runtime()->offThreadPromiseState.ref();
MOZ_ASSERT(state.initialized());
MOZ_ASSERT(task->registered_);
+
+ // TODO: This has overlap with removeFromCancellableListAndDispatch.
+ // The two methods should be refactored so that they are consistant and
+ // we don't have unnecessary repetition or distribution of responsibilities.
+ state.numRegistered_--;
+ if (task->cancellable_) {
+ state.cancellable().remove(task);
+ }
+ task->registered_ = false;
+
js::PromiseObject* promise = task->promise_;
- // Call the unregister method manually with our provided lock, otherwise the
- // destructor will try to acquire it and fail.
- task->unregister(state, lock);
- // Now we can call the destructor.
js_delete(task);
+
return promise;
}
diff --git a/js/src/vm/OffThreadPromiseRuntimeState.h b/js/src/vm/OffThreadPromiseRuntimeState.h
@@ -257,8 +257,6 @@ class OffThreadPromiseRuntimeState {
// not require a lock.
JS::DispatchToEventLoopCallback dispatchToEventLoopCallback_;
JS::DelayedDispatchToEventLoopCallback delayedDispatchToEventLoopCallback_;
- JS::AsyncTaskStartedCallback asyncTaskStartedCallback_;
- JS::AsyncTaskFinishedCallback asyncTaskFinishedCallback_;
void* dispatchToEventLoopClosure_;
#ifdef DEBUG
@@ -307,6 +305,7 @@ class OffThreadPromiseRuntimeState {
// mean "the DispatchToEventLoopCallback failed after this task was dispatched
// for execution".
HelperThreadLockData<ConditionVariable> allFailed_;
+ HelperThreadLockData<size_t> numFailed_;
// The queue of JS::Dispatchables used by the DispatchToEventLoopCallback that
// calling js::UseInternalJobQueues installs.
@@ -342,17 +341,11 @@ class OffThreadPromiseRuntimeState {
void operator=(const OffThreadPromiseRuntimeState&) = delete;
OffThreadPromiseRuntimeState(const OffThreadPromiseRuntimeState&) = delete;
- // Used by OffThreadPromiseTask
- void registerTask(JSContext* cx, OffThreadPromiseTask* task);
- void unregisterTask(OffThreadPromiseTask* task);
-
public:
OffThreadPromiseRuntimeState();
~OffThreadPromiseRuntimeState();
- void init(JS::DispatchToEventLoopCallback dispatchCallback,
- JS::DelayedDispatchToEventLoopCallback delayedDispatchCallback,
- JS::AsyncTaskStartedCallback asyncTaskStartedCallback,
- JS::AsyncTaskFinishedCallback asyncTaskFinishedCallback,
+ void init(JS::DispatchToEventLoopCallback callback,
+ JS::DelayedDispatchToEventLoopCallback delayCallback,
void* closure);
void initInternalDispatchQueue();
bool initialized() const;
@@ -369,9 +362,6 @@ class OffThreadPromiseRuntimeState {
bool delayedDispatchToEventLoop(
js::UniquePtr<JS::Dispatchable>&& dispatchable, uint32_t delay);
- void cancelTasks(JSContext* cx);
- void cancelTasks(AutoLockHelperThreadState& lock, JSContext* cx);
-
// shutdown() must be called by the JSRuntime while the JSRuntime is valid.
void shutdown(JSContext* cx);