mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-10-31 16:28:05 +02:00 
			
		
		
		
	 76d835cbc9
			
		
	
	
		76d835cbc9
		
	
	
	
	
		
			
			Spec: https://wicg.github.io/scheduling-apis/#dom-tasksignal-any Differential Revision: https://phabricator.services.mozilla.com/D248346
		
			
				
	
	
		
			365 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			365 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- 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/. */
 | |
| 
 | |
| #ifndef mozilla_dom_WebTaskScheduler_h
 | |
| #define mozilla_dom_WebTaskScheduler_h
 | |
| 
 | |
| #include "nsThreadUtils.h"
 | |
| #include "nsPIDOMWindow.h"
 | |
| #include "nsWrapperCache.h"
 | |
| #include "nsClassHashtable.h"
 | |
| 
 | |
| #include "TaskSignal.h"
 | |
| #include "mozilla/Variant.h"
 | |
| #include "mozilla/dom/Promise.h"
 | |
| #include "mozilla/dom/AbortFollower.h"
 | |
| #include "mozilla/dom/TimeoutHandler.h"
 | |
| #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(already_AddRefed<TaskSignal> aPrioritySource) {
 | |
|     mPrioritySource = aPrioritySource;
 | |
|     MOZ_ASSERT(mPrioritySource->IsTaskSignal());
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   ~WebTaskSchedulingState() = default;
 | |
| 
 | |
|   RefPtr<AbortSignal> mAbortSource;
 | |
|   RefPtr<TaskSignal> 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<TaskSignal>;
 | |
| 
 | |
|   // When WebTaskQueueTypeKey is RefPtr<TaskSignal>, this
 | |
|   // class holds a strong reference to a cycle collectable
 | |
|   // objects.
 | |
|   using WebTaskQueueTypeKey =
 | |
|       mozilla::Variant<StaticPriorityTaskQueueKey, DynamicPriorityTaskQueueKey>;
 | |
| 
 | |
|   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");
 | |
|         return 0;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   TaskPriority Priority() const {
 | |
|     return mKey.match(
 | |
|         [&](const StaticPriorityTaskQueueKey& aStaticKey) {
 | |
|           return static_cast<TaskPriority>(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 WebTask : public LinkedListElement<RefPtr<WebTask>>,
 | |
|                 public AbortFollower,
 | |
|                 public SupportsWeakPtr {
 | |
|   friend class WebTaskScheduler;
 | |
| 
 | |
|  public:
 | |
|   MOZ_CAN_RUN_SCRIPT bool Run();
 | |
| 
 | |
|   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
 | |
| 
 | |
|   NS_DECL_CYCLE_COLLECTION_CLASS(WebTask)
 | |
|   WebTask(uint32_t aEnqueueOrder,
 | |
|           const Maybe<SchedulerPostTaskCallback&>& aCallback,
 | |
|           WebTaskSchedulingState* aSchedulingState, Promise* aPromise,
 | |
|           WebTaskScheduler* aWebTaskScheduler,
 | |
|           const WebTaskQueueHashKey& aHashKey);
 | |
| 
 | |
|   void RunAbortAlgorithm() override;
 | |
| 
 | |
|   bool HasScheduled() const { return mHasScheduled; }
 | |
| 
 | |
|   uint32_t EnqueueOrder() const { return mEnqueueOrder; }
 | |
| 
 | |
|   void ClearWebTaskScheduler() { mScheduler = nullptr; }
 | |
| 
 | |
|   const WebTaskQueueHashKey& TaskQueueHashKey() const {
 | |
|     return mWebTaskQueueHashKey;
 | |
|   }
 | |
| 
 | |
|   TaskPriority Priority() const { return mWebTaskQueueHashKey.Priority(); }
 | |
| 
 | |
|  private:
 | |
|   void SetHasScheduled() {
 | |
|     MOZ_ASSERT(!mHasScheduled);
 | |
|     mHasScheduled = true;
 | |
|   }
 | |
| 
 | |
|   uint32_t mEnqueueOrder;
 | |
| 
 | |
|   RefPtr<SchedulerPostTaskCallback> mCallback;
 | |
|   RefPtr<Promise> mPromise;
 | |
| 
 | |
|   bool mHasScheduled;
 | |
| 
 | |
|   RefPtr<WebTaskSchedulingState> 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;
 | |
| 
 | |
|   ~WebTask() = default;
 | |
| };
 | |
| 
 | |
| class WebTaskQueue {
 | |
|  public:
 | |
|   static constexpr int EffectivePriorityCount = 6;
 | |
| 
 | |
|   explicit WebTaskQueue(WebTaskScheduler* aScheduler) : mScheduler(aScheduler) {
 | |
|     MOZ_ASSERT(aScheduler);
 | |
|   }
 | |
| 
 | |
|   WebTaskQueue(WebTaskQueue&& aWebTaskQueue) = default;
 | |
| 
 | |
|   ~WebTaskQueue();
 | |
| 
 | |
|   TaskPriority Priority() const { return mPriority; }
 | |
|   void SetPriority(TaskPriority aNewPriority) { mPriority = aNewPriority; }
 | |
| 
 | |
|   LinkedList<RefPtr<WebTask>>& Tasks() { return mTasks; }
 | |
|   const LinkedList<RefPtr<WebTask>>& Tasks() const { return mTasks; }
 | |
| 
 | |
|   void AddTask(WebTask* aTask) { mTasks.insertBack(aTask); }
 | |
| 
 | |
|   bool IsEmpty() const { return mTasks.isEmpty(); }
 | |
| 
 | |
|   // TODO: To optimize it, we could have the scheduled and unscheduled
 | |
|   // tasks stored separately.
 | |
|   WebTask* GetFirstScheduledTask() {
 | |
|     for (const auto& task : mTasks) {
 | |
|       if (task->HasScheduled()) {
 | |
|         return task;
 | |
|       }
 | |
|     }
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   bool HasScheduledTasks() const {
 | |
|     if (mTasks.isEmpty()) {
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     for (const auto& task : mTasks) {
 | |
|       if (task->HasScheduled()) {
 | |
|         return true;
 | |
|       }
 | |
|     }
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   TaskPriority mPriority = TaskPriority::User_visible;
 | |
|   LinkedList<RefPtr<WebTask>> mTasks;
 | |
| 
 | |
|   // WebTaskScheduler owns WebTaskQueue as a hashtable value, so using a raw
 | |
|   // pointer points to WebTaskScheduler is ok.
 | |
|   WebTaskScheduler* mScheduler;
 | |
| };
 | |
| 
 | |
| class WebTaskSchedulerMainThread;
 | |
| class WebTaskSchedulerWorker;
 | |
| 
 | |
| class WebTaskScheduler : public nsWrapperCache,
 | |
|                          public SupportsWeakPtr,
 | |
|                          public LinkedListElement<WebTaskScheduler> {
 | |
|   friend class DelayedWebTaskHandler;
 | |
| 
 | |
|  public:
 | |
|   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebTaskScheduler)
 | |
|   NS_DECL_CYCLE_COLLECTION_NATIVE_WRAPPERCACHE_CLASS(WebTaskScheduler)
 | |
| 
 | |
|   static already_AddRefed<WebTaskSchedulerMainThread> CreateForMainThread(
 | |
|       nsGlobalWindowInner* aWindow);
 | |
| 
 | |
|   static already_AddRefed<WebTaskSchedulerWorker> CreateForWorker(
 | |
|       WorkerPrivate* aWorkerPrivate);
 | |
| 
 | |
|   explicit WebTaskScheduler(nsIGlobalObject* aParent);
 | |
| 
 | |
|   already_AddRefed<Promise> PostTask(SchedulerPostTaskCallback& aCallback,
 | |
|                                      const SchedulerPostTaskOptions& aOptions);
 | |
| 
 | |
|   already_AddRefed<Promise> YieldImpl();
 | |
| 
 | |
|   nsIGlobalObject* GetParentObject() const { return mParent; }
 | |
| 
 | |
|   virtual JSObject* WrapObject(JSContext* cx,
 | |
|                                JS::Handle<JSObject*> aGivenProto) override;
 | |
| 
 | |
|   WebTask* GetNextTask(bool aIsMainThread);
 | |
| 
 | |
|   virtual void Disconnect();
 | |
| 
 | |
|   void RunTaskSignalPriorityChange(TaskSignal* aTaskSignal);
 | |
| 
 | |
|   void DeleteEntryFromWebTaskQueueMap(const WebTaskQueueHashKey& aKey) {
 | |
|     DebugOnly<bool> result = mWebTaskQueues.Remove(aKey);
 | |
|     MOZ_ASSERT(result);
 | |
|   }
 | |
| 
 | |
|   void NotifyTaskWillBeRunOrAborted(const WebTask* aWebTask);
 | |
|   virtual void IncreaseNumNormalOrHighPriorityQueuesHaveTaskScheduled() = 0;
 | |
|   virtual void DecreaseNumNormalOrHighPriorityQueuesHaveTaskScheduled() = 0;
 | |
| 
 | |
|  protected:
 | |
|   virtual ~WebTaskScheduler() = default;
 | |
|   nsCOMPtr<nsIGlobalObject> mParent;
 | |
| 
 | |
|  private:
 | |
|   struct SelectedTaskQueueData {
 | |
|     WebTaskQueueHashKey mSelectedQueueHashKey;
 | |
|     WebTaskQueue& mSelectedTaskQueue;
 | |
|   };
 | |
| 
 | |
|   already_AddRefed<WebTask> CreateTask(
 | |
|       AbortSignal* aAbortSignal, TaskSignal* aTaskSignal,
 | |
|       const Optional<TaskPriority>& aPriority, const bool aIsContinuation,
 | |
|       const Maybe<SchedulerPostTaskCallback&>& aCallback,
 | |
|       WebTaskSchedulingState* aSchedulingState, Promise* aPromise);
 | |
| 
 | |
|   bool DispatchTask(WebTask* aTask, EventQueuePriority aPriority);
 | |
| 
 | |
|   SelectedTaskQueueData SelectTaskQueue(TaskSignal* aSignal,
 | |
|                                         const Optional<TaskPriority>& aPriority,
 | |
|                                         const bool aIsContinuation);
 | |
| 
 | |
|   virtual nsresult SetTimeoutForDelayedTask(WebTask* aTask, uint64_t aDelay,
 | |
|                                             EventQueuePriority aPriority) = 0;
 | |
|   virtual bool DispatchEventLoopRunnable(EventQueuePriority aPriority) = 0;
 | |
| 
 | |
|   EventQueuePriority GetEventQueuePriority(const TaskPriority& aPriority,
 | |
|                                            bool aIsContinuation) const;
 | |
| 
 | |
|   nsTHashMap<WebTaskQueueHashKey, WebTaskQueue>& GetWebTaskQueues() {
 | |
|     return mWebTaskQueues;
 | |
|   }
 | |
| 
 | |
|   nsTHashMap<WebTaskQueueHashKey, WebTaskQueue> mWebTaskQueues;
 | |
| };
 | |
| 
 | |
| class DelayedWebTaskHandler final : public TimeoutHandler {
 | |
|  public:
 | |
|   DelayedWebTaskHandler(JSContext* aCx, WebTaskScheduler* aScheduler,
 | |
|                         WebTask* aTask, EventQueuePriority aPriority)
 | |
|       : TimeoutHandler(aCx), mScheduler(aScheduler), mWebTask(aTask) {}
 | |
| 
 | |
|   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
 | |
|   NS_DECL_CYCLE_COLLECTION_CLASS(DelayedWebTaskHandler)
 | |
| 
 | |
|   MOZ_CAN_RUN_SCRIPT bool Call(const char* /* unused */) override {
 | |
|     if (mScheduler && mWebTask && mWebTask->isInList()) {
 | |
|       MOZ_ASSERT(!mWebTask->HasScheduled());
 | |
|       if (!mScheduler->DispatchTask(mWebTask, mPriority)) {
 | |
|         return false;
 | |
|       }
 | |
|     }
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   ~DelayedWebTaskHandler() override = default;
 | |
|   WeakPtr<WebTaskScheduler> mScheduler;
 | |
|   // WebTask gets added to WebTaskQueue, and WebTaskQueue keeps its alive.
 | |
|   WeakPtr<WebTask> mWebTask;
 | |
|   EventQueuePriority mPriority;
 | |
| };
 | |
| }  // namespace mozilla::dom
 | |
| #endif
 |