tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

WebTaskScheduler.h (11529B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim:expandtab:shiftwidth=2:tabstop=2:
      3 */
      4 /* This Source Code Form is subject to the terms of the Mozilla Public
      5 * License, v. 2.0. If a copy of the MPL was not distributed with this
      6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      7 
      8 #ifndef mozilla_dom_WebTaskScheduler_h
      9 #define mozilla_dom_WebTaskScheduler_h
     10 
     11 #include "TaskSignal.h"
     12 #include "mozilla/Variant.h"
     13 #include "mozilla/dom/AbortFollower.h"
     14 #include "mozilla/dom/Promise.h"
     15 #include "mozilla/dom/TimeoutHandler.h"
     16 #include "mozilla/dom/WebTaskSchedulingBinding.h"
     17 #include "nsClassHashtable.h"
     18 #include "nsPIDOMWindow.h"
     19 #include "nsThreadUtils.h"
     20 #include "nsWrapperCache.h"
     21 
     22 namespace mozilla::dom {
     23 
     24 // Keep tracks of the number of same-event-loop-high-priority-queues
     25 // (User_blocking or User_visible) that have at least one task scheduled.
     26 constinit extern uint32_t
     27    gNumNormalOrHighPriorityQueuesHaveTaskScheduledMainThread;
     28 
     29 // https://wicg.github.io/scheduling-apis/#scheduling-state
     30 class WebTaskSchedulingState {
     31 public:
     32  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebTaskSchedulingState)
     33  NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(WebTaskSchedulingState)
     34 
     35  void Reset() {
     36    mAbortSource = nullptr;
     37    mPrioritySource = nullptr;
     38  }
     39 
     40  void SetAbortSource(AbortSignal* aAbortSource) {
     41    mAbortSource = aAbortSource;
     42  }
     43 
     44  AbortSignal* GetAbortSource() { return mAbortSource; }
     45  TaskSignal* GetPrioritySource() { return mPrioritySource; }
     46 
     47  void SetPrioritySource(already_AddRefed<TaskSignal> aPrioritySource) {
     48    mPrioritySource = aPrioritySource;
     49    MOZ_ASSERT(mPrioritySource->IsTaskSignal());
     50  }
     51 
     52 private:
     53  ~WebTaskSchedulingState() = default;
     54 
     55  RefPtr<AbortSignal> mAbortSource;
     56  RefPtr<TaskSignal> mPrioritySource;
     57 };
     58 
     59 class WebTaskQueueHashKey : public PLDHashEntryHdr {
     60 public:
     61  enum { ALLOW_MEMMOVE = false };
     62 
     63  typedef const WebTaskQueueHashKey& KeyType;
     64  typedef const WebTaskQueueHashKey* KeyTypePointer;
     65 
     66  using StaticPriorityTaskQueueKey = uint32_t;
     67  using DynamicPriorityTaskQueueKey = RefPtr<TaskSignal>;
     68 
     69  // When WebTaskQueueTypeKey is RefPtr<TaskSignal>, this
     70  // class holds a strong reference to a cycle collectable
     71  // objects.
     72  using WebTaskQueueTypeKey =
     73      mozilla::Variant<StaticPriorityTaskQueueKey, DynamicPriorityTaskQueueKey>;
     74 
     75  WebTaskQueueHashKey(StaticPriorityTaskQueueKey aKey, bool aIsContinuation)
     76      : mKey(aKey), mIsContinuation(aIsContinuation) {}
     77 
     78  WebTaskQueueHashKey(DynamicPriorityTaskQueueKey aKey, bool aIsContinuation)
     79      : mKey(aKey), mIsContinuation(aIsContinuation) {}
     80 
     81  explicit WebTaskQueueHashKey(KeyTypePointer aKey)
     82      : mKey(aKey->mKey), mIsContinuation(aKey->mIsContinuation) {}
     83 
     84  explicit WebTaskQueueHashKey(KeyType aKey)
     85      : mKey(aKey.mKey), mIsContinuation(aKey.mIsContinuation) {}
     86 
     87  WebTaskQueueHashKey(WebTaskQueueHashKey&& aToMove) = default;
     88 
     89  ~WebTaskQueueHashKey() = default;
     90 
     91  KeyType GetKey() const { return *this; }
     92 
     93  bool KeyEquals(KeyTypePointer aKey) const {
     94    return aKey->mKey == mKey && aKey->mIsContinuation == mIsContinuation;
     95  }
     96 
     97  // https://wicg.github.io/scheduling-apis/#scheduler-task-queue-effective-priority
     98  uint8_t EffectivePriority() const {
     99    switch (Priority()) {
    100      case TaskPriority::Background:
    101        return mIsContinuation ? 1 : 0;
    102      case TaskPriority::User_visible:
    103        return mIsContinuation ? 3 : 2;
    104      case TaskPriority::User_blocking:
    105        return mIsContinuation ? 5 : 4;
    106      default:
    107        MOZ_ASSERT_UNREACHABLE("Unexpected priority");
    108        return 0;
    109    }
    110  }
    111 
    112  TaskPriority Priority() const {
    113    return mKey.match(
    114        [&](const StaticPriorityTaskQueueKey& aStaticKey) {
    115          return static_cast<TaskPriority>(aStaticKey);
    116        },
    117        [&](const DynamicPriorityTaskQueueKey& aDynamicKey) {
    118          return aDynamicKey->Priority();
    119        });
    120  }
    121 
    122  static KeyTypePointer KeyToPointer(KeyType& aKey) { return &aKey; }
    123 
    124  static PLDHashNumber HashKey(KeyTypePointer aKey) {
    125    const WebTaskQueueTypeKey& key = aKey->mKey;
    126    return key.match(
    127        [&](const StaticPriorityTaskQueueKey& aStaticKey) {
    128          return mozilla::HashGeneric(aStaticKey, aKey->mIsContinuation);
    129        },
    130        [&](const DynamicPriorityTaskQueueKey& aDynamicKey) {
    131          return mozilla::HashGeneric(aDynamicKey.get(), aKey->mIsContinuation);
    132        });
    133  }
    134 
    135  WebTaskQueueTypeKey& GetTypeKey() { return mKey; }
    136  const WebTaskQueueTypeKey& GetTypeKey() const { return mKey; }
    137 
    138 private:
    139  WebTaskQueueTypeKey mKey;
    140  const bool mIsContinuation;
    141 };
    142 
    143 class WebTask : public LinkedListElement<RefPtr<WebTask>>,
    144                public AbortFollower,
    145                public SupportsWeakPtr {
    146  friend class WebTaskScheduler;
    147 
    148 public:
    149  MOZ_CAN_RUN_SCRIPT bool Run();
    150 
    151  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
    152 
    153  NS_DECL_CYCLE_COLLECTION_CLASS(WebTask)
    154  WebTask(uint32_t aEnqueueOrder,
    155          const Maybe<SchedulerPostTaskCallback&>& aCallback,
    156          WebTaskSchedulingState* aSchedulingState, Promise* aPromise,
    157          WebTaskScheduler* aWebTaskScheduler,
    158          const WebTaskQueueHashKey& aHashKey);
    159 
    160  void RunAbortAlgorithm() override;
    161 
    162  bool HasScheduled() const { return mHasScheduled; }
    163 
    164  uint32_t EnqueueOrder() const { return mEnqueueOrder; }
    165 
    166  void ClearWebTaskScheduler() { mScheduler = nullptr; }
    167 
    168  const WebTaskQueueHashKey& TaskQueueHashKey() const {
    169    return mWebTaskQueueHashKey;
    170  }
    171 
    172  TaskPriority Priority() const { return mWebTaskQueueHashKey.Priority(); }
    173 
    174 private:
    175  void SetHasScheduled() {
    176    MOZ_ASSERT(!mHasScheduled);
    177    mHasScheduled = true;
    178  }
    179 
    180  uint32_t mEnqueueOrder;
    181 
    182  RefPtr<SchedulerPostTaskCallback> mCallback;
    183  RefPtr<Promise> mPromise;
    184 
    185  bool mHasScheduled;
    186 
    187  RefPtr<WebTaskSchedulingState> mSchedulingState;
    188 
    189  // WebTaskScheduler owns WebTaskQueue, and WebTaskQueue owns WebTask, so it's
    190  // okay to use a raw pointer
    191  WebTaskScheduler* mScheduler;
    192 
    193  // Depending on whether this task was scheduled with static priority
    194  // or dynamic priority, it could hold a reference reference to TaskSignal
    195  // (cycle collectable object).
    196  WebTaskQueueHashKey mWebTaskQueueHashKey;
    197 
    198  ~WebTask() = default;
    199 };
    200 
    201 class WebTaskQueue {
    202 public:
    203  static constexpr int EffectivePriorityCount = 6;
    204 
    205  explicit WebTaskQueue(WebTaskScheduler* aScheduler) : mScheduler(aScheduler) {
    206    MOZ_ASSERT(aScheduler);
    207  }
    208 
    209  WebTaskQueue(WebTaskQueue&& aWebTaskQueue) = default;
    210 
    211  ~WebTaskQueue();
    212 
    213  TaskPriority Priority() const { return mPriority; }
    214  void SetPriority(TaskPriority aNewPriority) { mPriority = aNewPriority; }
    215 
    216  LinkedList<RefPtr<WebTask>>& Tasks() { return mTasks; }
    217  const LinkedList<RefPtr<WebTask>>& Tasks() const { return mTasks; }
    218 
    219  void AddTask(WebTask* aTask) { mTasks.insertBack(aTask); }
    220 
    221  bool IsEmpty() const { return mTasks.isEmpty(); }
    222 
    223  // TODO: To optimize it, we could have the scheduled and unscheduled
    224  // tasks stored separately.
    225  WebTask* GetFirstScheduledTask() {
    226    for (const auto& task : mTasks) {
    227      if (task->HasScheduled()) {
    228        return task;
    229      }
    230    }
    231    return nullptr;
    232  }
    233 
    234  bool HasScheduledTasks() const {
    235    if (mTasks.isEmpty()) {
    236      return false;
    237    }
    238 
    239    for (const auto& task : mTasks) {
    240      if (task->HasScheduled()) {
    241        return true;
    242      }
    243    }
    244    return false;
    245  }
    246 
    247 private:
    248  TaskPriority mPriority = TaskPriority::User_visible;
    249  LinkedList<RefPtr<WebTask>> mTasks;
    250 
    251  // WebTaskScheduler owns WebTaskQueue as a hashtable value, so using a raw
    252  // pointer points to WebTaskScheduler is ok.
    253  WebTaskScheduler* mScheduler;
    254 };
    255 
    256 class WebTaskSchedulerMainThread;
    257 class WebTaskSchedulerWorker;
    258 
    259 class WebTaskScheduler : public nsWrapperCache,
    260                         public SupportsWeakPtr,
    261                         public LinkedListElement<WebTaskScheduler> {
    262  friend class DelayedWebTaskHandler;
    263 
    264 public:
    265  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebTaskScheduler)
    266  NS_DECL_CYCLE_COLLECTION_NATIVE_WRAPPERCACHE_CLASS(WebTaskScheduler)
    267 
    268  static already_AddRefed<WebTaskSchedulerMainThread> CreateForMainThread(
    269      nsGlobalWindowInner* aWindow);
    270 
    271  static already_AddRefed<WebTaskSchedulerWorker> CreateForWorker(
    272      WorkerPrivate* aWorkerPrivate);
    273 
    274  explicit WebTaskScheduler(nsIGlobalObject* aParent);
    275 
    276  already_AddRefed<Promise> PostTask(SchedulerPostTaskCallback& aCallback,
    277                                     const SchedulerPostTaskOptions& aOptions);
    278 
    279  already_AddRefed<Promise> YieldImpl();
    280 
    281  nsIGlobalObject* GetParentObject() const { return mParent; }
    282 
    283  virtual JSObject* WrapObject(JSContext* cx,
    284                               JS::Handle<JSObject*> aGivenProto) override;
    285 
    286  WebTask* GetNextTask(bool aIsMainThread);
    287 
    288  virtual void Disconnect();
    289 
    290  void RunTaskSignalPriorityChange(TaskSignal* aTaskSignal);
    291 
    292  void DeleteEntryFromWebTaskQueueMap(const WebTaskQueueHashKey& aKey) {
    293    DebugOnly<bool> result = mWebTaskQueues.Remove(aKey);
    294    MOZ_ASSERT(result);
    295  }
    296 
    297  void NotifyTaskWillBeRunOrAborted(const WebTask* aWebTask);
    298  virtual void IncreaseNumNormalOrHighPriorityQueuesHaveTaskScheduled() = 0;
    299  virtual void DecreaseNumNormalOrHighPriorityQueuesHaveTaskScheduled() = 0;
    300 
    301 protected:
    302  virtual ~WebTaskScheduler() = default;
    303  nsCOMPtr<nsIGlobalObject> mParent;
    304 
    305 private:
    306  struct SelectedTaskQueueData {
    307    WebTaskQueueHashKey mSelectedQueueHashKey;
    308    WebTaskQueue& mSelectedTaskQueue;
    309  };
    310 
    311  already_AddRefed<WebTask> CreateTask(
    312      AbortSignal* aAbortSignal, TaskSignal* aTaskSignal,
    313      const Optional<TaskPriority>& aPriority, const bool aIsContinuation,
    314      const Maybe<SchedulerPostTaskCallback&>& aCallback,
    315      WebTaskSchedulingState* aSchedulingState, Promise* aPromise);
    316 
    317  bool DispatchTask(WebTask* aTask, EventQueuePriority aPriority);
    318 
    319  SelectedTaskQueueData SelectTaskQueue(TaskSignal* aSignal,
    320                                        const Optional<TaskPriority>& aPriority,
    321                                        const bool aIsContinuation);
    322 
    323  virtual nsresult SetTimeoutForDelayedTask(WebTask* aTask, uint64_t aDelay,
    324                                            EventQueuePriority aPriority) = 0;
    325  virtual bool DispatchEventLoopRunnable(EventQueuePriority aPriority) = 0;
    326 
    327  EventQueuePriority GetEventQueuePriority(const TaskPriority& aPriority,
    328                                           bool aIsContinuation) const;
    329 
    330  nsTHashMap<WebTaskQueueHashKey, WebTaskQueue>& GetWebTaskQueues() {
    331    return mWebTaskQueues;
    332  }
    333 
    334  nsTHashMap<WebTaskQueueHashKey, WebTaskQueue> mWebTaskQueues;
    335 };
    336 
    337 class DelayedWebTaskHandler final : public TimeoutHandler {
    338 public:
    339  DelayedWebTaskHandler(JSContext* aCx, WebTaskScheduler* aScheduler,
    340                        WebTask* aTask, EventQueuePriority aPriority)
    341      : TimeoutHandler(aCx), mScheduler(aScheduler), mWebTask(aTask) {}
    342 
    343  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
    344  NS_DECL_CYCLE_COLLECTION_CLASS(DelayedWebTaskHandler)
    345 
    346  MOZ_CAN_RUN_SCRIPT bool Call(const char* /* unused */) override {
    347    if (mScheduler && mWebTask && mWebTask->isInList()) {
    348      MOZ_ASSERT(!mWebTask->HasScheduled());
    349      if (!mScheduler->DispatchTask(mWebTask, mPriority)) {
    350        return false;
    351      }
    352    }
    353    return true;
    354  }
    355 
    356 private:
    357  ~DelayedWebTaskHandler() override = default;
    358  WeakPtr<WebTaskScheduler> mScheduler;
    359  // WebTask gets added to WebTaskQueue, and WebTaskQueue keeps its alive.
    360  WeakPtr<WebTask> mWebTask;
    361  EventQueuePriority mPriority;
    362 };
    363 }  // namespace mozilla::dom
    364 #endif