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; }