From 69747e811751301b61d44be42ab79ef0624f3d64 Mon Sep 17 00:00:00 2001 From: Stanca Serban Date: Fri, 28 Feb 2025 02:01:11 +0200 Subject: [PATCH] Backed out 8 changesets (bug 1920115) for causing multiple failures. Backed out changeset 2d0905a92f2c (bug 1920115) Backed out changeset 67bf260fe59b (bug 1920115) Backed out changeset 4739af17e757 (bug 1920115) Backed out changeset 0773f669c498 (bug 1920115) Backed out changeset 4b579a1c9b46 (bug 1920115) Backed out changeset 79b3b0148eb4 (bug 1920115) Backed out changeset 78181c32bdc6 (bug 1920115) Backed out changeset 912cf1adac80 (bug 1920115) --- dom/base/IdleRequest.cpp | 14 - dom/base/TimeoutManager.cpp | 18 +- dom/base/nsGlobalWindowInner.cpp | 14 - dom/base/nsGlobalWindowInner.h | 9 - dom/base/nsIGlobalObject.h | 12 - dom/script/ScriptSettings.cpp | 7 - dom/script/ScriptSettings.h | 3 - dom/webidl/WebTaskScheduling.webidl | 3 - dom/webscheduling/TaskSignal.cpp | 23 - dom/webscheduling/TaskSignal.h | 11 +- dom/webscheduling/WebTaskScheduler.cpp | 535 ++++-------------- dom/webscheduling/WebTaskScheduler.h | 259 ++------- .../WebTaskSchedulerMainThread.cpp | 30 +- .../WebTaskSchedulerMainThread.h | 8 +- dom/webscheduling/WebTaskSchedulerWorker.cpp | 39 +- dom/webscheduling/WebTaskSchedulerWorker.h | 21 +- dom/webscheduling/moz.build | 1 - dom/workers/WorkerPrivate.cpp | 5 +- dom/workers/WorkerScope.cpp | 14 - dom/workers/WorkerScope.h | 8 - .../tentative/yield/yield-abort.any.js.ini | 9 + .../yield-inherit-across-promises.any.js.ini | 32 ++ .../yield-priority-idle-callbacks.html.ini | 4 + .../yield/yield-priority-posttask.any.js.ini | 20 + .../yield/yield-priority-timers.any.js.ini | 12 +- .../yield/yield-then-detach.html.ini | 3 + ...st-task-multiple-scheduler-order.window.js | 37 -- .../tentative/yield/yield-abort.any.js | 13 +- xpcom/base/CycleCollectedJSContext.cpp | 62 +- 29 files changed, 302 insertions(+), 924 deletions(-) delete mode 100644 dom/webscheduling/TaskSignal.cpp create mode 100644 testing/web-platform/meta/scheduler/tentative/yield/yield-abort.any.js.ini create mode 100644 testing/web-platform/meta/scheduler/tentative/yield/yield-inherit-across-promises.any.js.ini create mode 100644 testing/web-platform/meta/scheduler/tentative/yield/yield-priority-idle-callbacks.html.ini create mode 100644 testing/web-platform/meta/scheduler/tentative/yield/yield-priority-posttask.any.js.ini create mode 100644 testing/web-platform/meta/scheduler/tentative/yield/yield-then-detach.html.ini delete mode 100644 testing/web-platform/tests/scheduler/post-task-multiple-scheduler-order.window.js diff --git a/dom/base/IdleRequest.cpp b/dom/base/IdleRequest.cpp index 21410ca86de2cf..d5a75fde77a20a 100644 --- a/dom/base/IdleRequest.cpp +++ b/dom/base/IdleRequest.cpp @@ -13,7 +13,6 @@ #include "mozilla/dom/WindowBinding.h" #include "nsComponentManagerUtils.h" #include "nsPIDOMWindow.h" -#include "mozilla/dom/WebTaskScheduler.h" namespace mozilla::dom { @@ -55,20 +54,7 @@ void IdleRequest::IdleRun(nsPIDOMWindowInner* aWindow, new IdleDeadline(aWindow, aDidTimeout, aDeadline); RefPtr callback(std::move(mCallback)); MOZ_ASSERT(!mCallback); - - RefPtr innerWindow = nsGlobalWindowInner::Cast(aWindow); - // https://wicg.github.io/scheduling-apis/#sec-patches-invoke-idle-callbacks - // Let state be a new scheduling state. - RefPtr newState = new WebTaskSchedulingState(); - // Set state’s priority source to the result of creating a fixed priority - // unabortable task signal given "background" and realm. - newState->SetPrioritySource( - new TaskSignal(aWindow->AsGlobal(), TaskPriority::Background)); - // Set event loop’s current scheduling state to state. - innerWindow->SetWebTaskSchedulingState(newState); callback->Call(*deadline, "requestIdleCallback handler"); - // Set event loop’s current scheduling state to null. - innerWindow->SetWebTaskSchedulingState(nullptr); } } // namespace mozilla::dom diff --git a/dom/base/TimeoutManager.cpp b/dom/base/TimeoutManager.cpp index 831b9176d93e72..269219e65bd31a 100644 --- a/dom/base/TimeoutManager.cpp +++ b/dom/base/TimeoutManager.cpp @@ -25,7 +25,6 @@ #include "mozilla/net/WebSocketEventService.h" #include "mozilla/MediaManager.h" #include "mozilla/dom/WorkerScope.h" -#include "mozilla/dom/WebTaskScheduler.h" using namespace mozilla; using namespace mozilla::dom; @@ -663,17 +662,6 @@ void TimeoutManager::RunTimeout(const TimeStamp& aNow, return; } - if (!GetInnerWindow()) { - // Workers don't use TaskController at the moment, so all the - // runnables have the same priorities. So we special case it - // here to allow "higher" prority tasks to run first before - // timers. - if (mGlobalObject.HasScheduledNormalOrHighPriorityWebTasks()) { - MOZ_ALWAYS_SUCCEEDS(MaybeSchedule(aNow)); - return; - } - } - Timeouts& timeouts(aProcessIdle ? mIdleTimeouts : mTimeouts); // Limit the overall time spent in RunTimeout() to reduce jank. @@ -975,12 +963,8 @@ void TimeoutManager::RunTimeout(const TimeStamp& aNow, } // Check to see if we have run out of time to execute timeout handlers. // If we've exceeded our time budget then terminate the loop immediately. - // - // Or if there are high priority tasks dispatched by the Scheduler API, - // they should run first before timers. TimeDuration elapsed = now - start; - if (elapsed >= totalTimeLimit || - mGlobalObject.HasScheduledNormalOrHighPriorityWebTasks()) { + if (elapsed >= totalTimeLimit) { // We ran out of time. Make sure to schedule the executor to // run immediately for the next timer, if it exists. Its possible, // however, that the last timeout handler suspended the window. If diff --git a/dom/base/nsGlobalWindowInner.cpp b/dom/base/nsGlobalWindowInner.cpp index 07b9bc98cc9a5a..9b04d26e06e08c 100644 --- a/dom/base/nsGlobalWindowInner.cpp +++ b/dom/base/nsGlobalWindowInner.cpp @@ -1400,8 +1400,6 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindowInner) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWebTaskScheduler) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWebTaskSchedulingState) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTrustedTypePolicyFactory) #ifdef MOZ_WEBSPEECH @@ -1513,8 +1511,6 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindowInner) NS_IMPL_CYCLE_COLLECTION_UNLINK(mWebTaskScheduler) } - NS_IMPL_CYCLE_COLLECTION_UNLINK(mWebTaskSchedulingState) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mTrustedTypePolicyFactory) #ifdef MOZ_WEBSPEECH @@ -1831,7 +1827,6 @@ void nsGlobalWindowInner::InitDocumentDependentState(JSContext* aCx) { if (mWebTaskScheduler) { mWebTaskScheduler->Disconnect(); mWebTaskScheduler = nullptr; - mWebTaskSchedulingState = nullptr; } // This must be called after nullifying the internal objects because here we @@ -2900,10 +2895,6 @@ bool nsPIDOMWindowInner::IsCurrentInnerWindow() const { return outer && outer->GetCurrentInnerWindow() == this; } -bool nsGlobalWindowInner::HasScheduledNormalOrHighPriorityWebTasks() const { - return gNumNormalOrHighPriorityQueuesHaveTaskScheduledMainThread > 0; -} - bool nsPIDOMWindowInner::IsFullyActive() const { WindowContext* wc = GetWindowContext(); if (!wc || wc->IsDiscarded() || !wc->IsCurrent()) { @@ -4104,11 +4095,6 @@ WebTaskScheduler* nsGlobalWindowInner::Scheduler() { return mWebTaskScheduler; } -inline void nsGlobalWindowInner::SetWebTaskSchedulingState( - WebTaskSchedulingState* aState) { - mWebTaskSchedulingState = aState; -} - bool nsGlobalWindowInner::Find(const nsAString& aString, bool aCaseSensitive, bool aBackwards, bool aWrapAround, bool aWholeWord, bool aSearchInFrames, diff --git a/dom/base/nsGlobalWindowInner.h b/dom/base/nsGlobalWindowInner.h index b1232b3a627c81..1e1970a0e22e09 100644 --- a/dom/base/nsGlobalWindowInner.h +++ b/dom/base/nsGlobalWindowInner.h @@ -130,7 +130,6 @@ class Selection; struct SizeToContentConstraints; class WebTaskScheduler; class WebTaskSchedulerMainThread; -class WebTaskSchedulingState; class SpeechSynthesis; class Timeout; class TrustedTypePolicyFactory; @@ -338,7 +337,6 @@ class nsGlobalWindowInner final : public mozilla::dom::EventTarget, virtual bool HasActiveIndexedDBDatabases() override; virtual bool HasActivePeerConnections() override; virtual bool HasOpenWebSockets() const override; - virtual bool HasScheduledNormalOrHighPriorityWebTasks() const override; void SyncStateFromParentWindow(); // Called on the current inner window of a browsing context when its @@ -963,12 +961,6 @@ class nsGlobalWindowInner final : public mozilla::dom::EventTarget, bool CrossOriginIsolated() const override; mozilla::dom::WebTaskScheduler* Scheduler(); - void SetWebTaskSchedulingState( - mozilla::dom::WebTaskSchedulingState* aState) override; - mozilla::dom::WebTaskSchedulingState* GetWebTaskSchedulingState() - const override { - return mWebTaskSchedulingState; - } protected: // Web IDL helpers @@ -1285,7 +1277,6 @@ class nsGlobalWindowInner final : public mozilla::dom::EventTarget, RefPtr mContentMediaController; RefPtr mWebTaskScheduler; - RefPtr mWebTaskSchedulingState; RefPtr mTrustedTypePolicyFactory; diff --git a/dom/base/nsIGlobalObject.h b/dom/base/nsIGlobalObject.h index 85c1e9ed2b490e..d40db19528ced2 100644 --- a/dom/base/nsIGlobalObject.h +++ b/dom/base/nsIGlobalObject.h @@ -53,7 +53,6 @@ class ServiceWorkerContainer; class ServiceWorkerRegistration; class ServiceWorkerRegistrationDescriptor; class StorageManager; -class WebTaskSchedulingState; enum class CallerType : uint32_t; } // namespace dom namespace ipc { @@ -195,13 +194,6 @@ class nsIGlobalObject : public nsISupports { return nullptr; } - virtual void SetWebTaskSchedulingState( - mozilla::dom::WebTaskSchedulingState* aState) {} - virtual mozilla::dom::WebTaskSchedulingState* GetWebTaskSchedulingState() - const { - return nullptr; - } - // For globals with a concept of a Base URI (windows, workers), the base URI, // nullptr otherwise. virtual nsIURI* GetBaseURI() const; @@ -345,10 +337,6 @@ class nsIGlobalObject : public nsISupports { virtual bool IsXPCSandbox() { return false; } - virtual bool HasScheduledNormalOrHighPriorityWebTasks() const { - return false; - } - /** * Report a localized error message to the error console. Currently this * amounts to a wrapper around nsContentUtils::ReportToConsole for window diff --git a/dom/script/ScriptSettings.cpp b/dom/script/ScriptSettings.cpp index d5964b27e9c99a..3b39538e51840c 100644 --- a/dom/script/ScriptSettings.cpp +++ b/dom/script/ScriptSettings.cpp @@ -233,13 +233,6 @@ nsIGlobalObject* GetCurrentGlobal() { return xpc::NativeGlobal(global); } -WebTaskSchedulingState* GetWebTaskSchedulingState() { - if (const nsIGlobalObject* global = GetEntryGlobal()) { - return global->GetWebTaskSchedulingState(); - } - return nullptr; -} - nsIPrincipal* GetWebIDLCallerPrincipal() { MOZ_ASSERT(NS_IsMainThread()); ScriptSettingsStackEntry* entry = ScriptSettingsStack::EntryPoint(); diff --git a/dom/script/ScriptSettings.h b/dom/script/ScriptSettings.h index 46705f3a9ee785..423da6c92add1d 100644 --- a/dom/script/ScriptSettings.h +++ b/dom/script/ScriptSettings.h @@ -35,7 +35,6 @@ namespace mozilla { namespace dom { class Document; -class WebTaskSchedulingState; /* * Per thread setup/teardown routines. Init and Destroy should be invoked @@ -96,8 +95,6 @@ nsIGlobalObject* GetIncumbentGlobal(); // Returns the global associated with the current compartment. This may be null. nsIGlobalObject* GetCurrentGlobal(); -WebTaskSchedulingState* GetWebTaskSchedulingState(); - // JS-implemented WebIDL presents an interesting situation with respect to the // subject principal. A regular C++-implemented API can simply examine the // compartment of the most-recently-executed script, and use that to infer the diff --git a/dom/webidl/WebTaskScheduling.webidl b/dom/webidl/WebTaskScheduling.webidl index 3d95f0aaf31f01..24659f30598b3a 100644 --- a/dom/webidl/WebTaskScheduling.webidl +++ b/dom/webidl/WebTaskScheduling.webidl @@ -31,9 +31,6 @@ interface Scheduler { SchedulerPostTaskCallback callback, optional SchedulerPostTaskOptions options = {} ); - - [BinaryName="yieldImpl"] - Promise yield(); }; dictionary TaskControllerInit { diff --git a/dom/webscheduling/TaskSignal.cpp b/dom/webscheduling/TaskSignal.cpp deleted file mode 100644 index 0b7e67702d329b..00000000000000 --- a/dom/webscheduling/TaskSignal.cpp +++ /dev/null @@ -1,23 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:expandtab:shiftwidth=2:tabstop=2: - */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "TaskSignal.h" -#include "WebTaskScheduler.h" - -namespace mozilla::dom { - -void TaskSignal::RunPriorityChangeAlgorithms() { - for (const WeakPtr& scheduler : mSchedulers) { - if (scheduler) { - scheduler->RunTaskSignalPriorityChange(this); - } - } -} -void TaskSignal::SetWebTaskScheduler(WebTaskScheduler* aScheduler) { - mSchedulers.AppendElement(aScheduler); -} -} // namespace mozilla::dom diff --git a/dom/webscheduling/TaskSignal.h b/dom/webscheduling/TaskSignal.h index 5fe9dfc10dc3f3..2cc02cc2fc25ac 100644 --- a/dom/webscheduling/TaskSignal.h +++ b/dom/webscheduling/TaskSignal.h @@ -11,6 +11,7 @@ #include "mozilla/DOMEventTargetHelper.h" #include "mozilla/dom/AbortSignal.h" #include "mozilla/dom/WebTaskSchedulingBinding.h" +#include "WebTaskScheduler.h" namespace mozilla::dom { class TaskSignal : public AbortSignal { @@ -39,9 +40,15 @@ class TaskSignal : public AbortSignal { mPriorityChanging = aPriorityChanging; } - void RunPriorityChangeAlgorithms(); + void SetWebTaskScheduler(WebTaskScheduler* aScheduler) { + mSchedulers.AppendElement(aScheduler); + } - void SetWebTaskScheduler(WebTaskScheduler* aScheduler); + void RunPriorityChangeAlgorithms() { + for (const WeakPtr& scheduler : mSchedulers) { + scheduler->RunTaskSignalPriorityChange(this); + } + } private: TaskPriority mPriority; diff --git a/dom/webscheduling/WebTaskScheduler.cpp b/dom/webscheduling/WebTaskScheduler.cpp index f205889b14bd57..f228c836558d8c 100644 --- a/dom/webscheduling/WebTaskScheduler.cpp +++ b/dom/webscheduling/WebTaskScheduler.cpp @@ -8,6 +8,7 @@ #include "WebTaskScheduler.h" #include "WebTaskSchedulerWorker.h" #include "WebTaskSchedulerMainThread.h" +#include "TaskSignal.h" #include "nsGlobalWindowInner.h" #include "mozilla/dom/WorkerPrivate.h" @@ -15,67 +16,22 @@ namespace mozilla::dom { -// Keeps track of all the existings schedulers that -// share the same event loop. -MOZ_RUNINIT static LinkedList gWebTaskSchedulersMainThread; - -static Atomic gWebTaskEnqueueOrder(0); - -// According to -// https://github.com/WICG/scheduling-apis/issues/113#issuecomment-2596102676, -// tasks with User_blocking or User_visible needs to run before timers. -static bool IsNormalOrHighPriority(TaskPriority aPriority) { - return aPriority == TaskPriority::User_blocking || - aPriority == TaskPriority::User_visible; -} - inline void ImplCycleCollectionTraverse( nsCycleCollectionTraversalCallback& aCallback, WebTaskQueue& aQueue, const char* aName, uint32_t aFlags = 0) { ImplCycleCollectionTraverse(aCallback, aQueue.Tasks(), aName, aFlags); } -inline void ImplCycleCollectionTraverse( - nsCycleCollectionTraversalCallback& aCallback, - const WebTaskQueueHashKey& aField, const char* aName, uint32_t aFlags = 0) { - const WebTaskQueueHashKey::WebTaskQueueTypeKey& typeKey = aField.GetTypeKey(); - if (typeKey.is>()) { - ImplCycleCollectionTraverse(aCallback, typeKey.as>(), - aName, aFlags); - } -} - -inline void ImplCycleCollectionUnlink(WebTaskQueueHashKey& aField) { - WebTaskQueueHashKey::WebTaskQueueTypeKey& typeKey = aField.GetTypeKey(); - if (typeKey.is>()) { - ImplCycleCollectionUnlink(typeKey.as>()); - } -} - -NS_IMPL_CYCLE_COLLECTION_CLASS(WebTaskSchedulingState) - -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(WebTaskSchedulingState) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAbortSource, mPrioritySource); -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END - -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(WebTaskSchedulingState) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mAbortSource, mPrioritySource); -NS_IMPL_CYCLE_COLLECTION_UNLINK_END - NS_IMPL_CYCLE_COLLECTION_CLASS(WebTask) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(WebTask) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromise) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWebTaskQueueHashKey) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSchedulingState) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(WebTask) NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback) NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromise) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mWebTaskQueueHashKey) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mSchedulingState) NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_PTR NS_IMPL_CYCLE_COLLECTION_UNLINK_END @@ -95,22 +51,6 @@ NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTING_ADDREF(DelayedWebTaskHandler) NS_IMPL_CYCLE_COLLECTING_RELEASE(DelayedWebTaskHandler) -WebTask::WebTask(uint32_t aEnqueueOrder, - const Maybe& aCallback, - WebTaskSchedulingState* aSchedlingState, Promise* aPromise, - WebTaskScheduler* aWebTaskScheduler, - const WebTaskQueueHashKey& aHashKey) - : mEnqueueOrder(aEnqueueOrder), - mPromise(aPromise), - mHasScheduled(false), - mSchedulingState(aSchedlingState), - mScheduler(aWebTaskScheduler), - mWebTaskQueueHashKey(aHashKey) { - if (aCallback.isSome()) { - mCallback = &aCallback.ref(); - } -} - void WebTask::RunAbortAlgorithm() { // no-op if WebTask::Run has been called already if (mPromise->State() == Promise::PromiseState::Pending) { @@ -121,10 +61,6 @@ void WebTask::RunAbortAlgorithm() { // was async and there's a signal.abort() call in the callback. if (isInList()) { remove(); - MOZ_ASSERT(mScheduler); - if (HasScheduled()) { - mScheduler->NotifyTaskWillBeRunOrAborted(this); - } } AutoJSAPI jsapi; @@ -143,20 +79,13 @@ void WebTask::RunAbortAlgorithm() { bool WebTask::Run() { MOZ_ASSERT(HasScheduled()); - MOZ_ASSERT(mScheduler); + MOZ_ASSERT(mOwnerQueue); remove(); - mScheduler->NotifyTaskWillBeRunOrAborted(this); - ClearWebTaskScheduler(); - - if (!mCallback) { - // Scheduler.yield - mPromise->MaybeResolveWithUndefined(); - MOZ_ASSERT(!isInList()); - return true; - } - - MOZ_ASSERT(mSchedulingState); + mOwnerQueue->RemoveEntryFromTaskQueueMapIfNeeded(); + mOwnerQueue = nullptr; + // At this point mOwnerQueue is destructed and this is fine. + // The caller of WebTask::Run keeps it alive. ErrorResult error; @@ -165,9 +94,6 @@ bool WebTask::Run() { return false; } - // 11.2.2 Set event loop’s current scheduling state to state. - global->SetWebTaskSchedulingState(mSchedulingState); - AutoJSAPI jsapi; if (!jsapi.Init(global)) { return false; @@ -180,9 +106,6 @@ bool WebTask::Run() { MOZ_KnownLive(mCallback)->Call(&returnVal, error, "WebTask", CallbackFunction::eRethrowExceptions); - // 11.2.4 Set event loop’s current scheduling state to null. - global->SetWebTaskSchedulingState(nullptr); - error.WouldReportJSException(); #ifdef DEBUG @@ -208,34 +131,15 @@ bool WebTask::Run() { return true; } -inline void ImplCycleCollectionUnlink( - nsTHashMap& aField) { - aField.Clear(); -} - -inline void ImplCycleCollectionTraverse( - nsCycleCollectionTraversalCallback& aCallback, - nsTHashMap& aField, const char* aName, - uint32_t aFlags = 0) { - for (auto& entry : aField) { - ImplCycleCollectionTraverse( - aCallback, entry.GetKey(), - "nsTHashMap::WebTaskQueueHashKey", - aFlags); - ImplCycleCollectionTraverse( - aCallback, *entry.GetModifiableData(), - "nsTHashMap::WebTaskQueue", aFlags); - } -} - -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebTaskScheduler, mParent, mWebTaskQueues) +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebTaskScheduler, mParent, + mStaticPriorityTaskQueues, + mDynamicPriorityTaskQueues) /* static */ already_AddRefed WebTaskScheduler::CreateForMainThread(nsGlobalWindowInner* aWindow) { RefPtr scheduler = new WebTaskSchedulerMainThread(aWindow->AsGlobal()); - gWebTaskSchedulersMainThread.insertBack(scheduler); return scheduler.forget(); } @@ -248,7 +152,7 @@ already_AddRefed WebTaskScheduler::CreateForWorker( } WebTaskScheduler::WebTaskScheduler(nsIGlobalObject* aParent) - : mParent(aParent) { + : mParent(aParent), mNextEnqueueOrder(1) { MOZ_ASSERT(aParent); } @@ -257,28 +161,6 @@ JSObject* WebTaskScheduler::WrapObject(JSContext* cx, return Scheduler_Binding::Wrap(cx, this, aGivenProto); } -static bool ShouldRejectPromiseWithReasonCausedByAbortSignal( - AbortSignal& aAbortSignal, nsIGlobalObject* aGlobal, Promise& aPromise) { - MOZ_ASSERT(aGlobal); - if (!aAbortSignal.Aborted()) { - return false; - } - - AutoJSAPI jsapi; - if (!jsapi.Init(aGlobal)) { - aPromise.MaybeRejectWithNotSupportedError( - "Failed to initialize the JS context"); - return true; - } - - JSContext* cx = jsapi.cx(); - JS::Rooted reason(cx); - aAbortSignal.GetReason(cx, &reason); - aPromise.MaybeReject(reason); - return true; -} - -// https://wicg.github.io/scheduling-apis/#sec-scheduler-alg-scheduling-tasks-and-continuations already_AddRefed WebTaskScheduler::PostTask( SchedulerPostTaskCallback& aCallback, const SchedulerPostTaskOptions& aOptions) { @@ -301,64 +183,33 @@ already_AddRefed WebTaskScheduler::PostTask( return promise.forget(); } - // 4. Let state be a new scheduling state. - RefPtr newState = new WebTaskSchedulingState(); - AbortSignal* signalValue = nullptr; if (taskSignal.WasPassed()) { - signalValue = &taskSignal.Value(); - // 3. If signal is not null and it is aborted, then reject result with - // signal’s abort reason and return result. - if (ShouldRejectPromiseWithReasonCausedByAbortSignal(*signalValue, global, - *promise)) { - return promise.forget(); - } - - // 5. Set state’s abort source to signal. - newState->SetAbortSource(signalValue); - } + AbortSignal& signalValue = taskSignal.Value(); - if (taskPriority.WasPassed()) { - // 6. If options["priority"] exists, then set state’s priority source to the - // result of creating a fixed priority unabortable task signal given - // options["priority"] - newState->SetPrioritySource( - new TaskSignal(GetParentObject(), taskPriority.Value())); - } else if (signalValue && signalValue->IsTaskSignal()) { - // 7. Otherwise if signal is not null and implements the TaskSignal - // interface, then set state’s priority source to signal. - newState->SetPrioritySource(static_cast(signalValue)); - } + if (signalValue.Aborted()) { + AutoJSAPI jsapi; + if (!jsapi.Init(global)) { + promise->MaybeReject(NS_ERROR_UNEXPECTED); + return promise.forget(); + } - if (!newState->GetPrioritySource()) { - // 8. If state’s priority source is null, then set state’s priority - // source to the result of creating a fixed priority unabortable task - // signal given "user-visible". - newState->SetPrioritySource( - new TaskSignal(GetParentObject(), TaskPriority::User_visible)); + JSContext* cx = jsapi.cx(); + JS::Rooted reason(cx); + signalValue.GetReason(cx, &reason); + promise->MaybeReject(reason); + return promise.forget(); + } } - MOZ_ASSERT(newState->GetPrioritySource()); + // Let queue be the result of selecting the scheduler task queue for scheduler + // given signal and priority. + WebTaskQueue& taskQueue = SelectTaskQueue(taskSignal, taskPriority); - // 9. Let handle be the result of creating a task handle given result and - // signal. - // 10. If signal is not null, then add handle’s abort steps to signal. - // 11. Let enqueueSteps be the following steps... - RefPtr task = CreateTask(signalValue, newState->GetPrioritySource(), - taskPriority, false /* aIsContinuation */, - SomeRef(aCallback), newState, promise); + uint64_t delay = aOptions.mDelay; - const TaskSignal* finalPrioritySource = newState->GetPrioritySource(); - // 12. Let delay be options["delay"]. - const uint64_t delay = aOptions.mDelay; - - // 13. If delay is greater than 0, then run steps after a timeout given - // scheduler’s relevant global object, "scheduler-postTask", delay, and the - // following steps... + RefPtr task = CreateTask(taskQueue, taskSignal, aCallback, promise); if (delay > 0) { - nsresult rv = SetTimeoutForDelayedTask( - task, delay, - GetEventQueuePriority(finalPrioritySource->Priority(), - false /* aIsContinuation */)); + nsresult rv = SetTimeoutForDelayedTask(task, delay); if (NS_FAILED(rv)) { promise->MaybeRejectWithUnknownError( "Failed to setup timeout for delayed task"); @@ -366,9 +217,7 @@ already_AddRefed WebTaskScheduler::PostTask( return promise.forget(); } - // 14. Otherwise, run enqueueSteps. - if (!DispatchTask(task, GetEventQueuePriority(finalPrioritySource->Priority(), - false /* aIsContinuation */))) { + if (!QueueTask(task)) { MOZ_ASSERT(task->isInList()); task->remove(); @@ -379,289 +228,155 @@ already_AddRefed WebTaskScheduler::PostTask( return promise.forget(); } -// https://wicg.github.io/scheduling-apis/#schedule-a-yield-continuation -already_AddRefed WebTaskScheduler::YieldImpl() { - ErrorResult rv; - // 1. Let result be a new promise. - RefPtr promise = Promise::Create(mParent, rv); - if (rv.Failed()) { - return nullptr; - } - - nsIGlobalObject* global = GetParentObject(); - if (!global || global->IsDying()) { - promise->MaybeRejectWithNotSupportedError("Current window is detached"); - return promise.forget(); - } - - RefPtr abortSource; - RefPtr prioritySource; - // 2. Let inheritedState be the scheduler’s relevant agent's event loop's - // current scheduling state. - if (auto* schedulingState = global->GetWebTaskSchedulingState()) { - // 3. Let abortSource be inheritedState’s abort source if inheritedState is - // not null, or otherwise null. - abortSource = schedulingState->GetAbortSource(); - // 5. Let prioritySource be inheritedState’s priority source if - // inheritedState is not null, or otherwise null. - prioritySource = schedulingState->GetPrioritySource(); - } - - if (abortSource) { - // 4. If abortSource is not null and abortSource is aborted, then reject - // result with abortSource’s abort reason and return result. - if (ShouldRejectPromiseWithReasonCausedByAbortSignal(*abortSource, global, - *promise)) { - return promise.forget(); - } - } +already_AddRefed WebTaskScheduler::CreateTask( + WebTaskQueue& aQueue, const Optional>& aSignal, + SchedulerPostTaskCallback& aCallback, Promise* aPromise) { + uint32_t nextEnqueueOrder = mNextEnqueueOrder; + ++mNextEnqueueOrder; - if (!prioritySource) { - // 6. If prioritySource is null, then set prioritySource to the result of - // creating a fixed priority unabortable task signal given "user-visible". - prioritySource = - new TaskSignal(GetParentObject(), TaskPriority::User_visible); - } + RefPtr task = new WebTask(nextEnqueueOrder, aCallback, aPromise); - // 7. Let handle be the result of creating a task handle given result and - // abortSource. - // 8. If abortSource is not null, then add handle’s abort steps to - // abortSource. - // 9. Set handle’s queue to the result of selecting the scheduler task queue - // for scheduler given prioritySource and true. - // 10. Schedule a task to invoke an algorithm for scheduler given handle and - // the following steps: - RefPtr task = - CreateTask(abortSource, prioritySource, {}, true /* aIsContinuation */, - Nothing(), nullptr, promise); - - EventQueuePriority eventQueuePriority = GetEventQueuePriority( - prioritySource->Priority(), true /* aIsContinuation */); - if (!DispatchTask(task, eventQueuePriority)) { - MOZ_ASSERT(task->isInList()); - // CreateTask adds the task to WebTaskScheduler's queue, so we - // need to remove from it when we failed to dispatch the runnable. - task->remove(); + aQueue.AddTask(task); - promise->MaybeRejectWithNotSupportedError("Unable to queue the task"); - return promise.forget(); - } - - return promise.forget(); -} - -already_AddRefed WebTaskScheduler::CreateTask( - AbortSignal* aAbortSignal, TaskSignal* aTaskSignal, - const Optional& aPriority, bool aIsContinuation, - const Maybe& aCallback, - WebTaskSchedulingState* aSchedulingState, Promise* aPromise) { - WebTaskScheduler::SelectedTaskQueueData selectedTaskQueueData = - SelectTaskQueue(aTaskSignal, aPriority, aIsContinuation); - - gWebTaskEnqueueOrder += 1; - RefPtr task = - new WebTask(gWebTaskEnqueueOrder, aCallback, aSchedulingState, aPromise, - this, selectedTaskQueueData.mSelectedQueueHashKey); - - selectedTaskQueueData.mSelectedTaskQueue.AddTask(task); - - if (aAbortSignal) { - task->Follow(aAbortSignal); + if (aSignal.WasPassed()) { + AbortSignal& signalValue = aSignal.Value(); + task->Follow(&signalValue); } return task.forget(); } -bool WebTaskScheduler::DispatchTask(WebTask* aTask, - EventQueuePriority aPriority) { - if (!DispatchEventLoopRunnable(aPriority)) { +bool WebTaskScheduler::QueueTask(WebTask* aTask) { + if (!DispatchEventLoopRunnable()) { return false; } MOZ_ASSERT(!aTask->HasScheduled()); - - auto taskQueue = mWebTaskQueues.Lookup(aTask->TaskQueueHashKey()); - MOZ_ASSERT(taskQueue); - - if (IsNormalOrHighPriority(aTask->Priority()) && - !taskQueue->HasScheduledTasks()) { - // This is the first task that is scheduled for this queue. - IncreaseNumNormalOrHighPriorityQueuesHaveTaskScheduled(); - } - - aTask->SetHasScheduled(); + aTask->SetHasScheduled(true); return true; } -// https://wicg.github.io/scheduling-apis/#select-the-next-scheduler-task-queue-from-all-schedulers -WebTask* WebTaskScheduler::GetNextTask(bool aIsMainThread) { - // 1. Let queues be an empty set. - AutoTArray, WebTaskQueue::EffectivePriorityCount> - allQueues; - allQueues.SetLength(WebTaskQueue::EffectivePriorityCount); - - auto processScheduler = [&](WebTaskScheduler& aScheduler) { - for (auto iter = aScheduler.GetWebTaskQueues().Iter(); !iter.Done(); - iter.Next()) { - auto& queue = iter.Data(); - if (queue.HasScheduledTasks()) { - const WebTaskQueueHashKey& key = iter.Key(); - nsTArray& queuesForThisPriority = - allQueues[key.EffectivePriority()]; - queuesForThisPriority.AppendElement(&queue); - } +WebTask* WebTaskScheduler::GetNextTask() const { + // We first combine queues from both mStaticPriorityTaskQueues and + // mDynamicPriorityTaskQueues into a single hash map which the + // keys are the priorities and the values are all the queues that + // belong to this priority. + // + // Then From the queues which + // 1. Have scheduled tasks + // 2. Its priority is not less than any other queues' priority + // We pick the task which has the smallest enqueue order. + nsTHashMap> allQueues; + + for (auto iter = mStaticPriorityTaskQueues.ConstIter(); !iter.Done(); + iter.Next()) { + const auto& queue = iter.Data(); + if (!queue->Tasks().isEmpty() && queue->GetFirstScheduledTask()) { + nsTArray& queuesForThisPriority = + allQueues.LookupOrInsert(static_cast(iter.Key())); + queuesForThisPriority.AppendElement(queue.get()); } - }; - // 3. For each scheduler in schedulers, extend queues with the result of - // getting the runnable task queues for scheduler. - if (aIsMainThread) { - // 2. Let schedulers be the set of all Scheduler objects whose relevant - // agent’s event loop is event loop and that have a runnable task. - for (const auto& scheduler : gWebTaskSchedulersMainThread) { - processScheduler(*scheduler); + } + + for (auto iter = mDynamicPriorityTaskQueues.ConstIter(); !iter.Done(); + iter.Next()) { + const auto& queue = iter.Data(); + if (!queue->Tasks().isEmpty() && queue->GetFirstScheduledTask()) { + nsTArray& queuesForThisPriority = allQueues.LookupOrInsert( + static_cast(iter.Key()->Priority())); + queuesForThisPriority.AppendElement(queue.get()); } - } else { - // Workers don't share the same event loop. - processScheduler(*this); } if (allQueues.IsEmpty()) { return nullptr; } - // Reverse checking the queues, so it starts with the highest priority - for (auto& queues : Reversed(allQueues)) { - if (queues.IsEmpty()) { - continue; - } - WebTaskQueue* oldestQueue = nullptr; - for (auto& webTaskQueue : queues) { - MOZ_ASSERT(webTaskQueue->HasScheduledTasks()); - if (!oldestQueue) { - oldestQueue = webTaskQueue; - } else { - WebTask* firstScheduledRunnableForCurrentQueue = - webTaskQueue->GetFirstScheduledTask(); - WebTask* firstScheduledRunnableForOldQueue = - oldestQueue->GetFirstScheduledTask(); - if (firstScheduledRunnableForOldQueue->EnqueueOrder() > - firstScheduledRunnableForCurrentQueue->EnqueueOrder()) { + for (TaskPriority priority : MakeWebIDLEnumeratedRange()) { + if (auto queues = allQueues.Lookup(UnderlyingValue(priority))) { + WebTaskQueue* oldestQueue = nullptr; + MOZ_ASSERT(!queues.Data().IsEmpty()); + for (auto& webTaskQueue : queues.Data()) { + MOZ_ASSERT(webTaskQueue->GetFirstScheduledTask()); + if (!oldestQueue) { oldestQueue = webTaskQueue; + } else { + WebTask* firstScheduledRunnableForCurrentQueue = + webTaskQueue->GetFirstScheduledTask(); + WebTask* firstScheduledRunnableForOldQueue = + oldestQueue->GetFirstScheduledTask(); + if (firstScheduledRunnableForOldQueue->EnqueueOrder() > + firstScheduledRunnableForCurrentQueue->EnqueueOrder()) { + oldestQueue = webTaskQueue; + } } } + MOZ_ASSERT(oldestQueue); + return oldestQueue->GetFirstScheduledTask(); } - MOZ_ASSERT(oldestQueue); - return oldestQueue->GetFirstScheduledTask(); } return nullptr; } void WebTaskScheduler::Disconnect() { - if (isInList()) { - remove(); - } - mWebTaskQueues.Clear(); + mStaticPriorityTaskQueues.Clear(); + mDynamicPriorityTaskQueues.Clear(); } void WebTaskScheduler::RunTaskSignalPriorityChange(TaskSignal* aTaskSignal) { - // aIsContinuation is always false because continued tasks, - // a.k.a yield(), can't change its priority. - WebTaskQueueHashKey key(aTaskSignal, false /* aIsContinuation */); - if (auto entry = mWebTaskQueues.Lookup(key)) { - if (IsNormalOrHighPriority(entry.Data().Priority()) != - IsNormalOrHighPriority(key.Priority())) { - // The counter needs to be adjusted if it has scheduled tasks - // because this queue changes its priority. - if (entry.Data().HasScheduledTasks()) { - if (IsNormalOrHighPriority(key.Priority())) { - // Promoted from lower priority to high priority. - IncreaseNumNormalOrHighPriorityQueuesHaveTaskScheduled(); - } else { - // Demoted from high priority to low priority. - DecreaseNumNormalOrHighPriorityQueuesHaveTaskScheduled(); - } - } - } - entry.Data().SetPriority(aTaskSignal->Priority()); + if (WebTaskQueue* const taskQueue = + mDynamicPriorityTaskQueues.Get(aTaskSignal)) { + taskQueue->SetPriority(aTaskSignal->Priority()); } } -WebTaskScheduler::SelectedTaskQueueData WebTaskScheduler::SelectTaskQueue( - TaskSignal* aTaskSignal, const Optional& aPriority, - const bool aIsContinuation) { - bool useSignal = !aPriority.WasPassed() && aTaskSignal; +WebTaskQueue& WebTaskScheduler::SelectTaskQueue( + const Optional>& aSignal, + const Optional& aPriority) { + bool useSignal = !aPriority.WasPassed() && aSignal.WasPassed() && + aSignal.Value().IsTaskSignal(); if (useSignal) { - WebTaskQueueHashKey signalHashKey(aTaskSignal, aIsContinuation); - WebTaskQueue& taskQueue = - mWebTaskQueues.LookupOrInsert(signalHashKey, this); + TaskSignal* taskSignal = static_cast(&(aSignal.Value())); + WebTaskQueue* const taskQueue = + mDynamicPriorityTaskQueues.GetOrInsertNew(taskSignal, taskSignal, this); + taskQueue->SetPriority(taskSignal->Priority()); + taskSignal->SetWebTaskScheduler(this); + MOZ_ASSERT(mDynamicPriorityTaskQueues.Contains(taskSignal)); - taskQueue.SetPriority(aTaskSignal->Priority()); - aTaskSignal->SetWebTaskScheduler(this); - - return SelectedTaskQueueData{WebTaskQueueHashKey(signalHashKey), taskQueue}; + return *taskQueue; } TaskPriority taskPriority = aPriority.WasPassed() ? aPriority.Value() : TaskPriority::User_visible; uint32_t staticTaskQueueMapKey = static_cast(taskPriority); - WebTaskQueueHashKey staticHashKey(staticTaskQueueMapKey, aIsContinuation); - WebTaskQueue& taskQueue = mWebTaskQueues.LookupOrInsert(staticHashKey, this); - taskQueue.SetPriority(taskPriority); - - return SelectedTaskQueueData{WebTaskQueueHashKey(staticHashKey), taskQueue}; + WebTaskQueue* const taskQueue = mStaticPriorityTaskQueues.GetOrInsertNew( + staticTaskQueueMapKey, staticTaskQueueMapKey, this); + taskQueue->SetPriority(taskPriority); + MOZ_ASSERT( + mStaticPriorityTaskQueues.Contains(static_cast(taskPriority))); + return *taskQueue; } -EventQueuePriority WebTaskScheduler::GetEventQueuePriority( - const TaskPriority& aPriority, bool aIsContinuation) const { - switch (aPriority) { - case TaskPriority::User_blocking: - return EventQueuePriority::MediumHigh; - case TaskPriority::User_visible: - return aIsContinuation ? EventQueuePriority::MediumHigh - : EventQueuePriority::Normal; - case TaskPriority::Background: - return EventQueuePriority::Low; - default: - MOZ_ASSERT_UNREACHABLE("Invalid TaskPriority"); - return EventQueuePriority::Normal; - } +void WebTaskScheduler::DeleteEntryFromStaticQueueMap(uint32_t aKey) { + DebugOnly result = mStaticPriorityTaskQueues.Remove(aKey); + MOZ_ASSERT(result); } -void WebTaskScheduler::NotifyTaskWillBeRunOrAborted(const WebTask* aWebTask) { - const WebTaskQueueHashKey& hashKey = aWebTask->TaskQueueHashKey(); - MOZ_ASSERT(mWebTaskQueues.Contains(hashKey)); - if (auto entry = mWebTaskQueues.Lookup(hashKey)) { - const WebTaskQueue& taskQueue = *entry; - if (IsNormalOrHighPriority(taskQueue.Priority())) { - // If the taskQueue - // 1. is empty - // 2. or it's not empty but the existing tasks are - // not scheduled (delay tasks). - if (!taskQueue.HasScheduledTasks()) { - DecreaseNumNormalOrHighPriorityQueuesHaveTaskScheduled(); - } - } - if (taskQueue.IsEmpty()) { - DeleteEntryFromWebTaskQueueMap(hashKey); - } - } +void WebTaskScheduler::DeleteEntryFromDynamicQueueMap(TaskSignal* aKey) { + DebugOnly result = mDynamicPriorityTaskQueues.Remove(aKey); + MOZ_ASSERT(result); } -WebTaskQueue::~WebTaskQueue() { +void WebTaskQueue::RemoveEntryFromTaskQueueMapIfNeeded() { MOZ_ASSERT(mScheduler); - - bool hasScheduledTask = false; - for (const auto& task : mTasks) { - if (!hasScheduledTask && task->HasScheduled()) { - hasScheduledTask = true; + if (mTasks.isEmpty()) { + if (mOwnerKey.is()) { + mScheduler->DeleteEntryFromStaticQueueMap(mOwnerKey.as()); + } else { + MOZ_ASSERT(mOwnerKey.is()); + mScheduler->DeleteEntryFromDynamicQueueMap(mOwnerKey.as()); } - task->ClearWebTaskScheduler(); - } - mTasks.clear(); - - if (hasScheduledTask && IsNormalOrHighPriority(Priority())) { - mScheduler->DecreaseNumNormalOrHighPriorityQueuesHaveTaskScheduled(); } } } // namespace mozilla::dom diff --git a/dom/webscheduling/WebTaskScheduler.h b/dom/webscheduling/WebTaskScheduler.h index b682092a76c4c0..18767be8a9999b 100644 --- a/dom/webscheduling/WebTaskScheduler.h +++ b/dom/webscheduling/WebTaskScheduler.h @@ -13,7 +13,6 @@ #include "nsWrapperCache.h" #include "nsClassHashtable.h" -#include "TaskSignal.h" #include "mozilla/Variant.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/AbortFollower.h" @@ -21,125 +20,7 @@ #include "mozilla/dom/WebTaskSchedulingBinding.h" namespace mozilla::dom { - -// Keep tracks of the number of same-event-loop-high-priority-queues -// (User_blocking or User_visible) that have at least one task scheduled. -MOZ_CONSTINIT extern uint32_t - gNumNormalOrHighPriorityQueuesHaveTaskScheduledMainThread; - -// https://wicg.github.io/scheduling-apis/#scheduling-state -class WebTaskSchedulingState { - public: - NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebTaskSchedulingState) - NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(WebTaskSchedulingState) - - void Reset() { - mAbortSource = nullptr; - mPrioritySource = nullptr; - } - - void SetAbortSource(AbortSignal* aAbortSource) { - mAbortSource = aAbortSource; - } - - AbortSignal* GetAbortSource() { return mAbortSource; } - TaskSignal* GetPrioritySource() { return mPrioritySource; } - - void SetPrioritySource(TaskSignal* aPrioritySource) { - MOZ_ASSERT(aPrioritySource->IsTaskSignal()); - mPrioritySource = aPrioritySource; - } - - private: - ~WebTaskSchedulingState() = default; - - RefPtr mAbortSource; - RefPtr mPrioritySource; -}; - -class WebTaskQueueHashKey : public PLDHashEntryHdr { - public: - enum { ALLOW_MEMMOVE = false }; - - typedef const WebTaskQueueHashKey& KeyType; - typedef const WebTaskQueueHashKey* KeyTypePointer; - - using StaticPriorityTaskQueueKey = uint32_t; - using DynamicPriorityTaskQueueKey = RefPtr; - - // When WebTaskQueueTypeKey is RefPtr, this - // class holds a strong reference to a cycle collectable - // objects. - using WebTaskQueueTypeKey = - mozilla::Variant; - - WebTaskQueueHashKey(StaticPriorityTaskQueueKey aKey, bool aIsContinuation) - : mKey(aKey), mIsContinuation(aIsContinuation) {} - - WebTaskQueueHashKey(DynamicPriorityTaskQueueKey aKey, bool aIsContinuation) - : mKey(aKey), mIsContinuation(aIsContinuation) {} - - explicit WebTaskQueueHashKey(KeyTypePointer aKey) - : mKey(aKey->mKey), mIsContinuation(aKey->mIsContinuation) {} - - explicit WebTaskQueueHashKey(KeyType aKey) - : mKey(aKey.mKey), mIsContinuation(aKey.mIsContinuation) {} - - WebTaskQueueHashKey(WebTaskQueueHashKey&& aToMove) = default; - - ~WebTaskQueueHashKey() = default; - - KeyType GetKey() const { return *this; } - - bool KeyEquals(KeyTypePointer aKey) const { - return aKey->mKey == mKey && aKey->mIsContinuation == mIsContinuation; - } - - // https://wicg.github.io/scheduling-apis/#scheduler-task-queue-effective-priority - uint8_t EffectivePriority() const { - switch (Priority()) { - case TaskPriority::Background: - return mIsContinuation ? 1 : 0; - case TaskPriority::User_visible: - return mIsContinuation ? 3 : 2; - case TaskPriority::User_blocking: - return mIsContinuation ? 5 : 4; - default: - MOZ_ASSERT_UNREACHABLE("Unexpected priority"); - } - } - - TaskPriority Priority() const { - return mKey.match( - [&](const StaticPriorityTaskQueueKey& aStaticKey) { - return static_cast(aStaticKey); - }, - [&](const DynamicPriorityTaskQueueKey& aDynamicKey) { - return aDynamicKey->Priority(); - }); - } - - static KeyTypePointer KeyToPointer(KeyType& aKey) { return &aKey; } - - static PLDHashNumber HashKey(KeyTypePointer aKey) { - const WebTaskQueueTypeKey& key = aKey->mKey; - return key.match( - [&](const StaticPriorityTaskQueueKey& aStaticKey) { - return mozilla::HashGeneric(aStaticKey, aKey->mIsContinuation); - }, - [&](const DynamicPriorityTaskQueueKey& aDynamicKey) { - return mozilla::HashGeneric(aDynamicKey.get(), aKey->mIsContinuation); - }); - } - - WebTaskQueueTypeKey& GetTypeKey() { return mKey; } - const WebTaskQueueTypeKey& GetTypeKey() const { return mKey; } - - private: - WebTaskQueueTypeKey mKey; - const bool mIsContinuation; -}; - +class WebTaskQueue; class WebTask : public LinkedListElement>, public AbortFollower, public SupportsWeakPtr { @@ -151,11 +32,13 @@ class WebTask : public LinkedListElement>, NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_CLASS(WebTask) - WebTask(uint32_t aEnqueueOrder, - const Maybe& aCallback, - WebTaskSchedulingState* aSchedulingState, Promise* aPromise, - WebTaskScheduler* aWebTaskScheduler, - const WebTaskQueueHashKey& aHashKey); + WebTask(uint32_t aEnqueueOrder, SchedulerPostTaskCallback& aCallback, + Promise* aPromise) + : mEnqueueOrder(aEnqueueOrder), + mCallback(&aCallback), + mPromise(aPromise), + mHasScheduled(false), + mOwnerQueue(nullptr) {} void RunAbortAlgorithm() override; @@ -163,19 +46,12 @@ class WebTask : public LinkedListElement>, uint32_t EnqueueOrder() const { return mEnqueueOrder; } - void ClearWebTaskScheduler() { mScheduler = nullptr; } - - const WebTaskQueueHashKey& TaskQueueHashKey() const { - return mWebTaskQueueHashKey; + void SetWebTaskQueue(WebTaskQueue* aWebTaskQueue) { + mOwnerQueue = aWebTaskQueue; } - TaskPriority Priority() const { return mWebTaskQueueHashKey.Priority(); } - private: - void SetHasScheduled() { - MOZ_ASSERT(!mHasScheduled); - mHasScheduled = true; - } + void SetHasScheduled(bool aHasScheduled) { mHasScheduled = aHasScheduled; } uint32_t mEnqueueOrder; @@ -184,42 +60,30 @@ class WebTask : public LinkedListElement>, bool mHasScheduled; - RefPtr mSchedulingState; - - // WebTaskScheduler owns WebTaskQueue, and WebTaskQueue owns WebTask, so it's - // okay to use a raw pointer - WebTaskScheduler* mScheduler; - - // Depending on whether this task was scheduled with static priority - // or dynamic priority, it could hold a reference reference to TaskSignal - // (cycle collectable object). - WebTaskQueueHashKey mWebTaskQueueHashKey; + // WebTaskQueue owns WebTask, so it's okay to use a raw pointer + WebTaskQueue* mOwnerQueue; ~WebTask() = default; }; class WebTaskQueue { public: - static constexpr int EffectivePriorityCount = 6; - - explicit WebTaskQueue(WebTaskScheduler* aScheduler) : mScheduler(aScheduler) { - MOZ_ASSERT(aScheduler); - } - - WebTaskQueue(WebTaskQueue&& aWebTaskQueue) = default; - - ~WebTaskQueue(); + WebTaskQueue(uint32_t aKey, WebTaskScheduler* aScheduler) + : mOwnerKey(AsVariant(aKey)), mScheduler(aScheduler) {} + WebTaskQueue(TaskSignal* aKey, WebTaskScheduler* aScheduler) + : mOwnerKey(AsVariant(aKey)), mScheduler(aScheduler) {} TaskPriority Priority() const { return mPriority; } void SetPriority(TaskPriority aNewPriority) { mPriority = aNewPriority; } LinkedList>& Tasks() { return mTasks; } - const LinkedList>& Tasks() const { return mTasks; } - - void AddTask(WebTask* aTask) { mTasks.insertBack(aTask); } - bool IsEmpty() const { return mTasks.isEmpty(); } + void AddTask(WebTask* aTask) { + mTasks.insertBack(aTask); + aTask->SetWebTaskQueue(this); + } + void RemoveEntryFromTaskQueueMapIfNeeded(); // TODO: To optimize it, we could have the scheduled and unscheduled // tasks stored separately. WebTask* GetFirstScheduledTask() { @@ -231,23 +95,23 @@ class WebTaskQueue { return nullptr; } - bool HasScheduledTasks() const { - if (mTasks.isEmpty()) { - return false; - } - + ~WebTaskQueue() { + mOwnerKey = AsVariant(Nothing()); for (const auto& task : mTasks) { - if (task->HasScheduled()) { - return true; - } + task->SetWebTaskQueue(nullptr); } - return false; + mTasks.clear(); } private: TaskPriority mPriority = TaskPriority::User_visible; LinkedList> mTasks; + // When mOwnerKey is TaskSignal*, it means as long as + // WebTaskQueue is alive, the corresponding TaskSignal + // is alive, so using a raw pointer is ok. + Variant mOwnerKey; + // WebTaskScheduler owns WebTaskQueue as a hashtable value, so using a raw // pointer points to WebTaskScheduler is ok. WebTaskScheduler* mScheduler; @@ -256,9 +120,7 @@ class WebTaskQueue { class WebTaskSchedulerMainThread; class WebTaskSchedulerWorker; -class WebTaskScheduler : public nsWrapperCache, - public SupportsWeakPtr, - public LinkedListElement { +class WebTaskScheduler : public nsWrapperCache, public SupportsWeakPtr { friend class DelayedWebTaskHandler; public: @@ -276,68 +138,50 @@ class WebTaskScheduler : public nsWrapperCache, already_AddRefed PostTask(SchedulerPostTaskCallback& aCallback, const SchedulerPostTaskOptions& aOptions); - already_AddRefed YieldImpl(); - nsIGlobalObject* GetParentObject() const { return mParent; } virtual JSObject* WrapObject(JSContext* cx, JS::Handle aGivenProto) override; - WebTask* GetNextTask(bool aIsMainThread); + WebTask* GetNextTask() const; virtual void Disconnect(); void RunTaskSignalPriorityChange(TaskSignal* aTaskSignal); - void DeleteEntryFromWebTaskQueueMap(const WebTaskQueueHashKey& aKey) { - DebugOnly result = mWebTaskQueues.Remove(aKey); - MOZ_ASSERT(result); - } - - void NotifyTaskWillBeRunOrAborted(const WebTask* aWebTask); - virtual void IncreaseNumNormalOrHighPriorityQueuesHaveTaskScheduled() = 0; - virtual void DecreaseNumNormalOrHighPriorityQueuesHaveTaskScheduled() = 0; + void DeleteEntryFromStaticQueueMap(uint32_t aKey); + void DeleteEntryFromDynamicQueueMap(TaskSignal* aKey); protected: virtual ~WebTaskScheduler() = default; nsCOMPtr mParent; - private: - struct SelectedTaskQueueData { - WebTaskQueueHashKey mSelectedQueueHashKey; - WebTaskQueue& mSelectedTaskQueue; - }; + uint32_t mNextEnqueueOrder; + private: already_AddRefed CreateTask( - AbortSignal* aAbortSignal, TaskSignal* aTaskSignal, - const Optional& aPriority, const bool aIsContinuation, - const Maybe& aCallback, - WebTaskSchedulingState* aSchedulingState, Promise* aPromise); - - bool DispatchTask(WebTask* aTask, EventQueuePriority aPriority); + WebTaskQueue& aQueue, const Optional>& aSignal, + SchedulerPostTaskCallback& aCallback, Promise* aPromise); - SelectedTaskQueueData SelectTaskQueue(TaskSignal* aSignal, - const Optional& aPriority, - const bool aIsContinuation); + bool QueueTask(WebTask* aTask); - virtual nsresult SetTimeoutForDelayedTask(WebTask* aTask, uint64_t aDelay, - EventQueuePriority aPriority) = 0; - virtual bool DispatchEventLoopRunnable(EventQueuePriority aPriority) = 0; + WebTaskQueue& SelectTaskQueue( + const Optional>& aSignal, + const Optional& aPriority); - EventQueuePriority GetEventQueuePriority(const TaskPriority& aPriority, - bool aIsContinuation) const; - - nsTHashMap& GetWebTaskQueues() { - return mWebTaskQueues; - } + virtual nsresult SetTimeoutForDelayedTask(WebTask* aTask, + uint64_t aDelay) = 0; + virtual bool DispatchEventLoopRunnable() = 0; - nsTHashMap mWebTaskQueues; + nsClassHashtable mStaticPriorityTaskQueues; + nsClassHashtable, WebTaskQueue> + mDynamicPriorityTaskQueues; }; class DelayedWebTaskHandler final : public TimeoutHandler { public: DelayedWebTaskHandler(JSContext* aCx, WebTaskScheduler* aScheduler, - WebTask* aTask, EventQueuePriority aPriority) + WebTask* aTask) : TimeoutHandler(aCx), mScheduler(aScheduler), mWebTask(aTask) {} NS_DECL_CYCLE_COLLECTING_ISUPPORTS @@ -346,7 +190,7 @@ class DelayedWebTaskHandler final : public TimeoutHandler { MOZ_CAN_RUN_SCRIPT bool Call(const char* /* unused */) override { if (mScheduler && mWebTask) { MOZ_ASSERT(!mWebTask->HasScheduled()); - if (!mScheduler->DispatchTask(mWebTask, mPriority)) { + if (!mScheduler->QueueTask(mWebTask)) { return false; } } @@ -358,7 +202,6 @@ class DelayedWebTaskHandler final : public TimeoutHandler { WeakPtr mScheduler; // WebTask gets added to WebTaskQueue, and WebTaskQueue keeps its alive. WeakPtr mWebTask; - EventQueuePriority mPriority; }; } // namespace mozilla::dom #endif diff --git a/dom/webscheduling/WebTaskSchedulerMainThread.cpp b/dom/webscheduling/WebTaskSchedulerMainThread.cpp index a095cf08ccfde9..112de23cbe69b5 100644 --- a/dom/webscheduling/WebTaskSchedulerMainThread.cpp +++ b/dom/webscheduling/WebTaskSchedulerMainThread.cpp @@ -5,6 +5,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "mozilla/TaskController.h" #include "mozilla/dom/TimeoutManager.h" #include "nsContentUtils.h" @@ -12,11 +13,9 @@ namespace mozilla::dom { -uint32_t gNumNormalOrHighPriorityQueuesHaveTaskScheduledMainThread = 0; - NS_IMETHODIMP WebTaskMainThreadRunnable::Run() { if (mScheduler) { - RefPtr task = mScheduler->GetNextTask(true /* aIsMainThread */); + RefPtr task = mScheduler->GetNextTask(); if (task) { task->Run(); } @@ -24,8 +23,8 @@ NS_IMETHODIMP WebTaskMainThreadRunnable::Run() { return NS_OK; } -nsresult WebTaskSchedulerMainThread::SetTimeoutForDelayedTask( - WebTask* aTask, uint64_t aDelay, EventQueuePriority aPriority) { +nsresult WebTaskSchedulerMainThread::SetTimeoutForDelayedTask(WebTask* aTask, + uint64_t aDelay) { JSContext* cx = nsContentUtils::GetCurrentJSContext(); if (!cx) { return NS_ERROR_UNEXPECTED; @@ -34,7 +33,7 @@ nsresult WebTaskSchedulerMainThread::SetTimeoutForDelayedTask( MOZ_ASSERT(global); RefPtr handler = - new DelayedWebTaskHandler(cx, this, aTask, aPriority); + new DelayedWebTaskHandler(cx, this, aTask); int32_t delay = aDelay > INT32_MAX ? INT32_MAX : (int32_t)aDelay; @@ -45,25 +44,12 @@ nsresult WebTaskSchedulerMainThread::SetTimeoutForDelayedTask( Timeout::Reason::eDelayedWebTaskTimeout, &handle); } -bool WebTaskSchedulerMainThread::DispatchEventLoopRunnable( - EventQueuePriority aPriority) { +bool WebTaskSchedulerMainThread::DispatchEventLoopRunnable() { RefPtr runnable = new WebTaskMainThreadRunnable(this); - MOZ_ALWAYS_SUCCEEDS( - NS_DispatchToMainThreadQueue(runnable.forget(), aPriority)); + MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThreadQueue(runnable.forget(), + EventQueuePriority::Normal)); return true; } - -void WebTaskSchedulerMainThread:: - IncreaseNumNormalOrHighPriorityQueuesHaveTaskScheduled() { - ++gNumNormalOrHighPriorityQueuesHaveTaskScheduledMainThread; -} - -void WebTaskSchedulerMainThread:: - DecreaseNumNormalOrHighPriorityQueuesHaveTaskScheduled() { - MOZ_DIAGNOSTIC_ASSERT( - gNumNormalOrHighPriorityQueuesHaveTaskScheduledMainThread > 0); - --gNumNormalOrHighPriorityQueuesHaveTaskScheduledMainThread; -} } // namespace mozilla::dom diff --git a/dom/webscheduling/WebTaskSchedulerMainThread.h b/dom/webscheduling/WebTaskSchedulerMainThread.h index cc640608f9977c..a5d57f6228de8c 100644 --- a/dom/webscheduling/WebTaskSchedulerMainThread.h +++ b/dom/webscheduling/WebTaskSchedulerMainThread.h @@ -32,13 +32,9 @@ class WebTaskSchedulerMainThread final : public WebTaskScheduler { explicit WebTaskSchedulerMainThread(nsIGlobalObject* aParent) : WebTaskScheduler(aParent) {} - void IncreaseNumNormalOrHighPriorityQueuesHaveTaskScheduled() override; - void DecreaseNumNormalOrHighPriorityQueuesHaveTaskScheduled() override; - private: - nsresult SetTimeoutForDelayedTask(WebTask* aTask, uint64_t aDelay, - EventQueuePriority aPriority) override; - bool DispatchEventLoopRunnable(EventQueuePriority aPriority) override; + nsresult SetTimeoutForDelayedTask(WebTask* aTask, uint64_t aDelay) override; + bool DispatchEventLoopRunnable() override; ~WebTaskSchedulerMainThread() = default; }; diff --git a/dom/webscheduling/WebTaskSchedulerWorker.cpp b/dom/webscheduling/WebTaskSchedulerWorker.cpp index 0ccb2dc1fda123..59a43d81f94eb5 100644 --- a/dom/webscheduling/WebTaskSchedulerWorker.cpp +++ b/dom/webscheduling/WebTaskSchedulerWorker.cpp @@ -9,8 +9,9 @@ #include "mozilla/dom/TimeoutManager.h" namespace mozilla::dom { + WebTaskWorkerRunnable::WebTaskWorkerRunnable( - WebTaskSchedulerWorker* aSchedulerWorker) + WorkerPrivate* aWorkerPrivate, WebTaskSchedulerWorker* aSchedulerWorker) : WorkerSameThreadRunnable("WebTaskWorkerRunnable"), mSchedulerWorker(aSchedulerWorker) { MOZ_ASSERT(mSchedulerWorker); @@ -45,8 +46,7 @@ bool WebTaskWorkerRunnable::WorkerRun(JSContext* aCx, aWorkerPrivate->AssertIsOnWorkerThread(); if (mSchedulerWorker) { - RefPtr task = - mSchedulerWorker->GetNextTask(false /* aIsMainThread */); + RefPtr task = mSchedulerWorker->GetNextTask(); if (task) { task->Run(); } @@ -54,11 +54,12 @@ bool WebTaskWorkerRunnable::WorkerRun(JSContext* aCx, return true; } -nsresult WebTaskSchedulerWorker::SetTimeoutForDelayedTask( - WebTask* aTask, uint64_t aDelay, EventQueuePriority aPriority) { +nsresult WebTaskSchedulerWorker::SetTimeoutForDelayedTask(WebTask* aTask, + uint64_t aDelay) { if (mWorkerIsShuttingDown) { return NS_ERROR_ABORT; } + if (!mWorkerRef) { return NS_ERROR_UNEXPECTED; } @@ -72,7 +73,7 @@ nsresult WebTaskSchedulerWorker::SetTimeoutForDelayedTask( return NS_ERROR_UNEXPECTED; } RefPtr handler = - new DelayedWebTaskHandler(cx, this, aTask, aPriority); + new DelayedWebTaskHandler(cx, this, aTask); ErrorResult rv; int32_t delay = aDelay > INT32_MAX ? INT32_MAX : (int32_t)aDelay; @@ -82,16 +83,7 @@ nsresult WebTaskSchedulerWorker::SetTimeoutForDelayedTask( return rv.StealNSResult(); } -bool WebTaskSchedulerWorker::DispatchEventLoopRunnable( - EventQueuePriority aPriority) { - // aPriority is unused at the moment, we can't control - // the priorities for runnables on workers at the moment. - // - // This won't affect the correctness of this API because - // WebTaskScheduler maintains its own priority queues. - // - // It's just that we can't interfere the ordering between - // `WebTask` and other runnables. +bool WebTaskSchedulerWorker::DispatchEventLoopRunnable() { if (mWorkerIsShuttingDown) { return false; } @@ -102,7 +94,8 @@ bool WebTaskSchedulerWorker::DispatchEventLoopRunnable( MOZ_ASSERT(mWorkerRef->Private()); mWorkerRef->Private()->AssertIsOnWorkerThread(); - RefPtr runnable = new WebTaskWorkerRunnable(this); + RefPtr runnable = + new WebTaskWorkerRunnable(mWorkerRef->Private(), this); return runnable->Dispatch(mWorkerRef->Private()); } @@ -112,16 +105,4 @@ void WebTaskSchedulerWorker::Disconnect() { } WebTaskScheduler::Disconnect(); } - -void WebTaskSchedulerWorker:: - IncreaseNumNormalOrHighPriorityQueuesHaveTaskScheduled() { - ++mNumHighPriorityQueuesHaveTaskScheduled; -} - -void WebTaskSchedulerWorker:: - DecreaseNumNormalOrHighPriorityQueuesHaveTaskScheduled() { - MOZ_ASSERT(mNumHighPriorityQueuesHaveTaskScheduled > 0); - --mNumHighPriorityQueuesHaveTaskScheduled; -} - } // namespace mozilla::dom diff --git a/dom/webscheduling/WebTaskSchedulerWorker.h b/dom/webscheduling/WebTaskSchedulerWorker.h index 2943c40cfae655..145c698b6ed494 100644 --- a/dom/webscheduling/WebTaskSchedulerWorker.h +++ b/dom/webscheduling/WebTaskSchedulerWorker.h @@ -20,7 +20,8 @@ namespace mozilla::dom { class WebTaskWorkerRunnable final : public WorkerSameThreadRunnable { public: - explicit WebTaskWorkerRunnable(WebTaskSchedulerWorker* aSchedulerWorker); + WebTaskWorkerRunnable(WorkerPrivate* aWorkerPrivate, + WebTaskSchedulerWorker* aSchedulerWorker); MOZ_CAN_RUN_SCRIPT_BOUNDARY bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override; @@ -38,28 +39,14 @@ class WebTaskSchedulerWorker final : public WebTaskScheduler { void Disconnect() override; - void IncreaseNumNormalOrHighPriorityQueuesHaveTaskScheduled() override; - void DecreaseNumNormalOrHighPriorityQueuesHaveTaskScheduled() override; - - bool HasScheduledNormalOrHighPriorityWebTasks() const { - return mNumHighPriorityQueuesHaveTaskScheduled; - } - private: ~WebTaskSchedulerWorker() = default; - nsresult SetTimeoutForDelayedTask(WebTask* aTask, uint64_t aDelay, - EventQueuePriority aPriority) override; - bool DispatchEventLoopRunnable(EventQueuePriority aPriority) override; + nsresult SetTimeoutForDelayedTask(WebTask* aTask, uint64_t aDelay) override; + bool DispatchEventLoopRunnable() override; RefPtr mWorkerRef; bool mWorkerIsShuttingDown{false}; - - // Unlike window global where multiple globals can share the - // same event loop, worker globals don't share event loops, - // so it's okay to have this counter lives inside the - // scheduler for workers. - uint32_t mNumHighPriorityQueuesHaveTaskScheduled = 0; }; } // namespace mozilla::dom #endif diff --git a/dom/webscheduling/moz.build b/dom/webscheduling/moz.build index 67281a565144c7..b30b87972c025c 100644 --- a/dom/webscheduling/moz.build +++ b/dom/webscheduling/moz.build @@ -16,7 +16,6 @@ EXPORTS.mozilla.dom += [ ] UNIFIED_SOURCES += [ - "TaskSignal.cpp", "WebTaskController.cpp", "WebTaskScheduler.cpp", "WebTaskSchedulerMainThread.cpp", diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp index 41e6ab9920a3de..5d918a82708a26 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -3606,9 +3606,8 @@ void WorkerPrivate::DoRunLoop(JSContext* aCx) { if (data->mScope) { data->mScope->NoteTerminating(); data->mScope->DisconnectGlobalTeardownObservers(); - if (WebTaskScheduler* scheduler = - data->mScope->GetExistingScheduler()) { - scheduler->Disconnect(); + if (data->mScope->GetExistingScheduler()) { + data->mScope->GetExistingScheduler()->Disconnect(); } } } diff --git a/dom/workers/WorkerScope.cpp b/dom/workers/WorkerScope.cpp index a10a21f8bb2f78..49988503024176 100644 --- a/dom/workers/WorkerScope.cpp +++ b/dom/workers/WorkerScope.cpp @@ -507,7 +507,6 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WorkerGlobalScope, NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCrypto) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPerformance) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWebTaskScheduler) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWebTaskSchedulingState) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTrustedTypePolicyFactory) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocation) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNavigator) @@ -525,7 +524,6 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WorkerGlobalScope, tmp->mWebTaskScheduler->Disconnect(); NS_IMPL_CYCLE_COLLECTION_UNLINK(mWebTaskScheduler) } - NS_IMPL_CYCLE_COLLECTION_UNLINK(mWebTaskSchedulingState) NS_IMPL_CYCLE_COLLECTION_UNLINK(mTrustedTypePolicyFactory) NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocation) NS_IMPL_CYCLE_COLLECTION_UNLINK(mNavigator) @@ -771,13 +769,6 @@ int32_t WorkerGlobalScope::SetTimeoutOrInterval( Timeout::Reason::eTimeoutOrInterval, aRv); } -bool WorkerGlobalScope::HasScheduledNormalOrHighPriorityWebTasks() const { - if (!mWebTaskScheduler) { - return false; - } - return mWebTaskScheduler->HasScheduledNormalOrHighPriorityWebTasks(); -} - void WorkerGlobalScope::GetOrigin(nsAString& aOrigin) const { AssertIsOnWorkerThread(); nsContentUtils::GetWebExposedOriginSerialization( @@ -901,11 +892,6 @@ WebTaskScheduler* WorkerGlobalScope::GetExistingScheduler() const { return mWebTaskScheduler; } -inline void WorkerGlobalScope::SetWebTaskSchedulingState( - WebTaskSchedulingState* aState) { - mWebTaskSchedulingState = aState; -} - already_AddRefed WorkerGlobalScope::CreateImageBitmap( const ImageBitmapSource& aImage, const ImageBitmapOptions& aOptions, ErrorResult& aRv) { diff --git a/dom/workers/WorkerScope.h b/dom/workers/WorkerScope.h index 4c1225a005cb48..43d1de000e1655 100644 --- a/dom/workers/WorkerScope.h +++ b/dom/workers/WorkerScope.h @@ -86,7 +86,6 @@ class WorkerPrivate; class VsyncWorkerChild; class WebTaskScheduler; class WebTaskSchedulerWorker; -class WebTaskSchedulingState; struct RequestInit; namespace cache { @@ -382,12 +381,6 @@ class WorkerGlobalScope : public WorkerGlobalScopeBase { WebTaskScheduler* Scheduler(); WebTaskScheduler* GetExistingScheduler() const; - void SetWebTaskSchedulingState(WebTaskSchedulingState* aState) override; - bool HasScheduledNormalOrHighPriorityWebTasks() const override; - - WebTaskSchedulingState* GetWebTaskSchedulingState() const override { - return mWebTaskSchedulingState; - } bool WindowInteractionAllowed() const; @@ -424,7 +417,6 @@ class WorkerGlobalScope : public WorkerGlobalScopeBase { RefPtr mCacheStorage; RefPtr mDebuggerNotificationManager; RefPtr mWebTaskScheduler; - RefPtr mWebTaskSchedulingState; RefPtr mTrustedTypePolicyFactory; uint32_t mWindowInteractionsAllowed = 0; bool mIsEligibleForMessaging{true}; diff --git a/testing/web-platform/meta/scheduler/tentative/yield/yield-abort.any.js.ini b/testing/web-platform/meta/scheduler/tentative/yield/yield-abort.any.js.ini new file mode 100644 index 00000000000000..d474cf6fb909dc --- /dev/null +++ b/testing/web-platform/meta/scheduler/tentative/yield/yield-abort.any.js.ini @@ -0,0 +1,9 @@ +[yield-abort.any.html] + expected: ERROR + [yield() aborted in a separate task] + expected: FAIL + + +[yield-abort.any.worker.html] + [yield() aborted in a separate task] + expected: FAIL diff --git a/testing/web-platform/meta/scheduler/tentative/yield/yield-inherit-across-promises.any.js.ini b/testing/web-platform/meta/scheduler/tentative/yield/yield-inherit-across-promises.any.js.ini new file mode 100644 index 00000000000000..556e7ff42b6900 --- /dev/null +++ b/testing/web-platform/meta/scheduler/tentative/yield/yield-inherit-across-promises.any.js.ini @@ -0,0 +1,32 @@ +[yield-inherit-across-promises.any.worker.html] + [yield() inherits priority (string) across promises (user-blocking)] + expected: FAIL + + [yield() inherits priority (signal) across promises (user-blocking)] + expected: FAIL + + [yield() inherits priority (string) across promises (background)] + expected: FAIL + + [yield() inherits priority (signal) across promises (background)] + expected: FAIL + + [yield() inherits abort across promises] + expected: FAIL + + +[yield-inherit-across-promises.any.html] + [yield() inherits priority (string) across promises (user-blocking)] + expected: FAIL + + [yield() inherits priority (signal) across promises (user-blocking)] + expected: FAIL + + [yield() inherits priority (string) across promises (background)] + expected: FAIL + + [yield() inherits priority (signal) across promises (background)] + expected: FAIL + + [yield() inherits abort across promises] + expected: FAIL diff --git a/testing/web-platform/meta/scheduler/tentative/yield/yield-priority-idle-callbacks.html.ini b/testing/web-platform/meta/scheduler/tentative/yield/yield-priority-idle-callbacks.html.ini new file mode 100644 index 00000000000000..ded4ff766ca826 --- /dev/null +++ b/testing/web-platform/meta/scheduler/tentative/yield/yield-priority-idle-callbacks.html.ini @@ -0,0 +1,4 @@ +[yield-priority-idle-callbacks.html] + expected: ERROR + [requestIdleCallback() yields at background priority when inheriting signal] + expected: TIMEOUT diff --git a/testing/web-platform/meta/scheduler/tentative/yield/yield-priority-posttask.any.js.ini b/testing/web-platform/meta/scheduler/tentative/yield/yield-priority-posttask.any.js.ini new file mode 100644 index 00000000000000..8c93d01c1749d3 --- /dev/null +++ b/testing/web-platform/meta/scheduler/tentative/yield/yield-priority-posttask.any.js.ini @@ -0,0 +1,20 @@ +[yield-priority-posttask.any.worker.html] + [yield() with TaskSignal has dynamic priority] + expected: FAIL + + [yield() with postTask tasks (priority)] + expected: FAIL + + [yield() with postTask tasks (signal)] + expected: FAIL + + +[yield-priority-posttask.any.html] + [yield() with TaskSignal has dynamic priority] + expected: FAIL + + [yield() with postTask tasks (priority)] + expected: FAIL + + [yield() with postTask tasks (signal)] + expected: FAIL diff --git a/testing/web-platform/meta/scheduler/tentative/yield/yield-priority-timers.any.js.ini b/testing/web-platform/meta/scheduler/tentative/yield/yield-priority-timers.any.js.ini index a126af784b7c45..677ba8fc588acc 100644 --- a/testing/web-platform/meta/scheduler/tentative/yield/yield-priority-timers.any.js.ini +++ b/testing/web-platform/meta/scheduler/tentative/yield/yield-priority-timers.any.js.ini @@ -1,6 +1,10 @@ [yield-priority-timers.any.worker.html] - # Currently worker doesn't use the TimeoutManager. - # This test needs to be investigated when `dom.workers.timeoutmanager` is enabled. - # Bug 1947484 + expected: TIMEOUT [yield() with timer tasks (inherit signal)] - expected: FAIL + expected: TIMEOUT + + +[yield-priority-timers.any.html] + expected: ERROR + [yield() with timer tasks (inherit signal)] + expected: TIMEOUT diff --git a/testing/web-platform/meta/scheduler/tentative/yield/yield-then-detach.html.ini b/testing/web-platform/meta/scheduler/tentative/yield/yield-then-detach.html.ini new file mode 100644 index 00000000000000..4f5f5c8c5328c3 --- /dev/null +++ b/testing/web-platform/meta/scheduler/tentative/yield/yield-then-detach.html.ini @@ -0,0 +1,3 @@ +[yield-then-detach.html] + [Test scheduler.yield() from an iframe that is removed] + expected: FAIL diff --git a/testing/web-platform/tests/scheduler/post-task-multiple-scheduler-order.window.js b/testing/web-platform/tests/scheduler/post-task-multiple-scheduler-order.window.js deleted file mode 100644 index 14c7013d5267c0..00000000000000 --- a/testing/web-platform/tests/scheduler/post-task-multiple-scheduler-order.window.js +++ /dev/null @@ -1,37 +0,0 @@ -// META: title=Scheduler: Tasks run order when multiple scheduler interfere the same event loop -promise_test(async t => { - - const iframe = document.createElement("iframe"); - - iframe.onload = async function() { - const runOrder = []; - const tasks = []; - - tasks.push(window.scheduler.postTask(function(){runOrder.push("outer")})); - tasks.push(iframe.contentWindow.scheduler.postTask(function(){runOrder.push("inner")})); - - await Promise.all(tasks); - - assert_equals(runOrder.toString(),'outer,inner'); - } - - document.body.appendChild(iframe); -}, 'Test scheduler.postTask() from two different schedulers with the same priority will run based on their enqueue order.'); - -promise_test(async t => { - const iframe = document.createElement("iframe"); - - iframe.onload = async function() { - const runOrder = []; - const tasks = []; - - tasks.push(window.scheduler.postTask(function(){runOrder.push("outer")})); - tasks.push(iframe.contentWindow.scheduler.postTask(function(){runOrder.push("inner")}, {priority: "user-blocking"})); - - await Promise.all(tasks); - - assert_equals(runOrder.toString(),'inner,outer'); - } - - document.body.appendChild(iframe); -}, 'Test scheduler.postTask() from two different schedulers with different priorities will run based on their priorities'); diff --git a/testing/web-platform/tests/scheduler/tentative/yield/yield-abort.any.js b/testing/web-platform/tests/scheduler/tentative/yield/yield-abort.any.js index d5119b1c1638ec..94a8b2cb2b8f84 100644 --- a/testing/web-platform/tests/scheduler/tentative/yield/yield-abort.any.js +++ b/testing/web-platform/tests/scheduler/tentative/yield/yield-abort.any.js @@ -20,15 +20,4 @@ promise_test(t => { const p = scheduler.yield(); await promise_rejects_dom(t, 'AbortError', p); }, {signal}); -}, 'yield() aborted by TaskController in a separate task'); - -promise_test(t => { - const controller = new AbortController(); - const signal = controller.signal; - return scheduler.postTask(async () => { - scheduler.postTask(async () => {controller.abort();}, {priority: 'user-blocking'}); - t.step(() => assert_false(signal.aborted)); - const p = scheduler.yield(); - await promise_rejects_dom(t, 'AbortError', p); - }, {signal}); -}, 'yield() aborted by AbortController in a separate task'); +}, 'yield() aborted in a separate task'); diff --git a/xpcom/base/CycleCollectedJSContext.cpp b/xpcom/base/CycleCollectedJSContext.cpp index d3e57c3e77878b..4d322789474ea2 100644 --- a/xpcom/base/CycleCollectedJSContext.cpp +++ b/xpcom/base/CycleCollectedJSContext.cpp @@ -35,7 +35,6 @@ #include "mozilla/dom/RootedDictionary.h" #include "mozilla/dom/ScriptSettings.h" #include "mozilla/dom/UserActivation.h" -#include "mozilla/dom/WebTaskScheduler.h" #include "nsContentUtils.h" #include "nsCycleCollectionNoteRootCallback.h" #include "nsCycleCollectionParticipant.h" @@ -177,11 +176,9 @@ class PromiseJobRunnable final : public MicroTaskRunnable { PromiseJobRunnable(JS::HandleObject aPromise, JS::HandleObject aCallback, JS::HandleObject aCallbackGlobal, JS::HandleObject aAllocationSite, - nsIGlobalObject* aIncumbentGlobal, - WebTaskSchedulingState* aSchedulingState) + nsIGlobalObject* aIncumbentGlobal) : mCallback(new PromiseJobCallback(aCallback, aCallbackGlobal, aAllocationSite, aIncumbentGlobal)), - mSchedulingState(aSchedulingState), mPropagateUserInputEventHandling(false) { MOZ_ASSERT(js::IsFunctionObject(aCallback)); @@ -200,11 +197,10 @@ class PromiseJobRunnable final : public MicroTaskRunnable { MOZ_CAN_RUN_SCRIPT virtual void Run(AutoSlowOperation& aAso) override { JSObject* callback = mCallback->CallbackPreserveColor(); - nsCOMPtr global = - callback ? xpc::NativeGlobal(callback) : nullptr; + nsIGlobalObject* global = callback ? xpc::NativeGlobal(callback) : nullptr; if (global && !global->IsDying()) { // Propagate the user input event handling bit if needed. - nsPIDOMWindowInner* win = global->GetAsInnerWindow(); + nsCOMPtr win = do_QueryInterface(global); RefPtr doc; if (win) { doc = win->GetExtantDoc(); @@ -212,17 +208,7 @@ class PromiseJobRunnable final : public MicroTaskRunnable { AutoHandlingUserInputStatePusher userInpStatePusher( mPropagateUserInputEventHandling); - // https://wicg.github.io/scheduling-apis/#sec-patches-html-hostcalljobcallback - // 2. Set event loop’s current scheduling state to - // callback.[[HostDefined]].[[SchedulingState]]. - global->SetWebTaskSchedulingState(mSchedulingState); - mCallback->Call("promise callback"); - - // (The step after step 7): Set event loop’s current scheduling state to - // null - global->SetWebTaskSchedulingState(nullptr); - aAso.CheckForInterrupt(); } // Now that mCallback is no longer needed, clear any pointers it contains to @@ -241,24 +227,15 @@ class PromiseJobRunnable final : public MicroTaskRunnable { private: const RefPtr mCallback; - const RefPtr mSchedulingState; bool mPropagateUserInputEventHandling; }; -enum { INCUMBENT_SETTING_SLOT, SCHEDULING_STATE_SLOT, HOSTDEFINED_DATA_SLOTS }; - -// Finalizer for instances of HostDefinedData. -void FinalizeHostDefinedData(JS::GCContext* gcx, JSObject* objSelf) { - JS::Value slotEvent = JS::GetReservedSlot(objSelf, SCHEDULING_STATE_SLOT); - if (slotEvent.isUndefined()) { - return; - } - - WebTaskSchedulingState* schedulingState = - static_cast(slotEvent.toPrivate()); - JS_SetReservedSlot(objSelf, SCHEDULING_STATE_SLOT, JS::UndefinedValue()); - schedulingState->Release(); -} +// Finalizer for instances of FinalizeHostDefinedData. +// +// HostDefinedData only contains incumbent global, no need to +// clean that up. +// TODO(sefeng): Bug 1929356 will add [[SchedulingState]] to HostDefinedData. +void FinalizeHostDefinedData(JS::GCContext* gcx, JSObject* objSelf) {} static const JSClassOps sHostDefinedData = { nullptr /* addProperty */, nullptr /* delProperty */, @@ -267,11 +244,13 @@ static const JSClassOps sHostDefinedData = { FinalizeHostDefinedData /* finalize */ }; +enum { INCUMBENT_SETTING_SLOT, HOSTDEFINED_DATA_SLOTS }; + // Implements `HostDefined` in https://html.spec.whatwg.org/#hostmakejobcallback static const JSClass sHostDefinedDataClass = { "HostDefinedData", JSCLASS_HAS_RESERVED_SLOTS(HOSTDEFINED_DATA_SLOTS) | - JSCLASS_FOREGROUND_FINALIZE, + JSCLASS_BACKGROUND_FINALIZE, &sHostDefinedData}; bool CycleCollectedJSContext::getHostDefinedData( @@ -300,14 +279,6 @@ bool CycleCollectedJSContext::getHostDefinedData( JS_SetReservedSlot(objResult, INCUMBENT_SETTING_SLOT, JS::ObjectValue(*incumbentGlobal)); - - if (mozilla::dom::WebTaskSchedulingState* schedulingState = - mozilla::dom::GetWebTaskSchedulingState()) { - schedulingState->AddRef(); - JS_SetReservedSlot(objResult, SCHEDULING_STATE_SLOT, - JS::PrivateValue(schedulingState)); - } - aData.set(objResult); return true; @@ -320,7 +291,6 @@ bool CycleCollectedJSContext::enqueuePromiseJob( MOZ_ASSERT(Get() == this); nsIGlobalObject* global = nullptr; - WebTaskSchedulingState* schedulingState = nullptr; if (hostDefinedData) { MOZ_RELEASE_ASSERT(JS::GetClass(hostDefinedData.get()) == @@ -330,17 +300,11 @@ bool CycleCollectedJSContext::enqueuePromiseJob( // hostDefinedData is only created when incumbent global exists. MOZ_ASSERT(incumbentGlobal.isObject()); global = xpc::NativeGlobal(&incumbentGlobal.toObject()); - - JS::Value state = - JS::GetReservedSlot(hostDefinedData.get(), SCHEDULING_STATE_SLOT); - if (!state.isUndefined()) { - schedulingState = static_cast(state.toPrivate()); - } } JS::RootedObject jobGlobal(aCx, JS::CurrentGlobalOrNull(aCx)); RefPtr runnable = new PromiseJobRunnable( - aPromise, aJob, jobGlobal, aAllocationSite, global, schedulingState); + aPromise, aJob, jobGlobal, aAllocationSite, global); DispatchToMicroTask(runnable.forget()); return true; }