TimeoutManager.h (8783B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef mozilla_dom_TimeoutManager_h__ 8 #define mozilla_dom_TimeoutManager_h__ 9 10 #include "mozilla/dom/Timeout.h" 11 #include "mozilla/dom/TimeoutBudgetManager.h" 12 #include "nsISerialEventTarget.h" 13 #include "nsTArray.h" 14 15 class nsIEventTarget; 16 class nsITimer; 17 class nsIGlobalObject; 18 19 namespace mozilla { 20 21 namespace dom { 22 23 class TimeoutExecutor; 24 class TimeoutHandler; 25 26 // This class manages the timeouts in a Window's setTimeout/setInterval pool. 27 class TimeoutManager final { 28 private: 29 struct Timeouts; 30 31 public: 32 TimeoutManager(nsIGlobalObject& aHandle, uint32_t aMaxIdleDeferMS, 33 nsISerialEventTarget* aEventTarget, 34 bool aIsChromeWorker = false); 35 ~TimeoutManager(); 36 TimeoutManager(const TimeoutManager& rhs) = delete; 37 void operator=(const TimeoutManager& rhs) = delete; 38 39 bool IsRunningTimeout() const; 40 41 uint32_t GetNestingLevelForWorker() { 42 MOZ_ASSERT(!NS_IsMainThread()); 43 return mNestingLevel; 44 } 45 uint32_t GetNestingLevelForWindow() { 46 MOZ_ASSERT(NS_IsMainThread()); 47 return sNestingLevel; 48 } 49 50 void SetNestingLevelForWorker(uint32_t aLevel) { 51 MOZ_ASSERT(!NS_IsMainThread()); 52 mNestingLevel = aLevel; 53 } 54 55 void SetNestingLevelForWindow(uint32_t aLevel) { 56 MOZ_ASSERT(NS_IsMainThread()); 57 sNestingLevel = aLevel; 58 } 59 60 bool HasTimeouts() const { 61 return !mTimeouts.IsEmpty() || !mIdleTimeouts.IsEmpty(); 62 } 63 64 nsresult SetTimeout(TimeoutHandler* aHandler, int32_t interval, 65 bool aIsInterval, mozilla::dom::Timeout::Reason aReason, 66 int32_t* aReturn); 67 void ClearTimeout(int32_t aTimerId, mozilla::dom::Timeout::Reason aReason); 68 bool ClearTimeoutInternal(int32_t aTimerId, 69 mozilla::dom::Timeout::Reason aReason, 70 bool aIsIdle); 71 72 // The timeout implementation functions. 73 MOZ_CAN_RUN_SCRIPT 74 void RunTimeout(const TimeStamp& aNow, const TimeStamp& aTargetDeadline, 75 bool aProcessIdle); 76 77 void ClearAllTimeouts(); 78 int32_t GetTimeoutId(mozilla::dom::Timeout::Reason aReason); 79 80 TimeDuration CalculateDelay(Timeout* aTimeout) const; 81 82 // aTimeout is the timeout that we're about to start running. This function 83 // returns the current timeout. 84 mozilla::dom::Timeout* BeginRunningTimeout(mozilla::dom::Timeout* aTimeout); 85 // aTimeout is the last running timeout. 86 void EndRunningTimeout(mozilla::dom::Timeout* aTimeout); 87 88 void UnmarkGrayTimers(); 89 90 // These four methods are intended to be called from the corresponding methods 91 // on nsGlobalWindow. 92 void Suspend(); 93 void Resume(); 94 void Freeze(); 95 void Thaw(); 96 97 // This should be called by nsGlobalWindow when the window might have moved 98 // to the background or foreground. 99 void UpdateBackgroundState(); 100 101 // The document finished loading 102 void OnDocumentLoaded(); 103 void StartThrottlingTimeouts(); 104 105 // Run some code for each Timeout in our list. Note that this function 106 // doesn't guarantee that Timeouts are iterated in any particular order. 107 template <class Callable> 108 void ForEachUnorderedTimeout(Callable c) { 109 mIdleTimeouts.ForEach(c); 110 mTimeouts.ForEach(c); 111 } 112 113 void BeginSyncOperation(); 114 void EndSyncOperation(); 115 116 nsIEventTarget* EventTarget(); 117 118 bool BudgetThrottlingEnabled(bool aIsBackground) const; 119 120 static const uint32_t InvalidFiringId; 121 122 void SetLoading(bool value); 123 124 private: 125 void MaybeStartThrottleTimeout(); 126 127 // get nsGlobalWindowInner 128 // if the method returns nullptr, then we have a worker, 129 // which should be handled differently according to TimeoutManager logic 130 nsGlobalWindowInner* GetInnerWindow() const; 131 132 // Return true if |aTimeout| needs to be reinserted into the timeout list. 133 bool RescheduleTimeout(mozilla::dom::Timeout* aTimeout, 134 const TimeStamp& aLastCallbackTime, 135 const TimeStamp& aCurrentNow); 136 137 void MoveIdleToActive(); 138 139 bool IsBackground() const; 140 141 bool IsActive() const; 142 143 uint32_t CreateFiringId(); 144 145 void DestroyFiringId(uint32_t aFiringId); 146 147 bool IsValidFiringId(uint32_t aFiringId) const; 148 149 bool IsInvalidFiringId(uint32_t aFiringId) const; 150 151 TimeDuration MinSchedulingDelay() const; 152 153 nsresult MaybeSchedule(const TimeStamp& aWhen, 154 const TimeStamp& aNow = TimeStamp::Now()); 155 156 void RecordExecution(Timeout* aRunningTimeout, Timeout* aTimeout); 157 158 void UpdateBudget(const TimeStamp& aNow, 159 const TimeDuration& aDuration = TimeDuration()); 160 161 private: 162 struct Timeouts { 163 explicit Timeouts(const TimeoutManager& aManager) 164 : mManager(aManager), mTimeouts(new Timeout::TimeoutSet()) {} 165 166 // Insert aTimeout into the list, before all timeouts that would 167 // fire after it, but no earlier than the last Timeout with a 168 // valid FiringId. 169 enum class SortBy { TimeRemaining, TimeWhen }; 170 void Insert(mozilla::dom::Timeout* aTimeout, SortBy aSortBy); 171 172 const Timeout* GetFirst() const { return mTimeoutList.getFirst(); } 173 Timeout* GetFirst() { return mTimeoutList.getFirst(); } 174 const Timeout* GetLast() const { return mTimeoutList.getLast(); } 175 Timeout* GetLast() { return mTimeoutList.getLast(); } 176 bool IsEmpty() const { return mTimeoutList.isEmpty(); } 177 void InsertFront(Timeout* aTimeout) { 178 aTimeout->SetTimeoutContainer(mTimeouts); 179 mTimeoutList.insertFront(aTimeout); 180 } 181 void InsertBack(Timeout* aTimeout) { 182 aTimeout->SetTimeoutContainer(mTimeouts); 183 mTimeoutList.insertBack(aTimeout); 184 } 185 void Clear() { 186 mTimeouts->Clear(); 187 mTimeoutList.clear(); 188 } 189 190 template <class Callable> 191 void ForEach(Callable c) { 192 for (Timeout* timeout = GetFirst(); timeout; 193 timeout = timeout->getNext()) { 194 c(timeout); 195 } 196 } 197 198 // Returns true when a callback aborts iteration. 199 template <class Callable> 200 bool ForEachAbortable(Callable c) { 201 for (Timeout* timeout = GetFirst(); timeout; 202 timeout = timeout->getNext()) { 203 if (c(timeout)) { 204 return true; 205 } 206 } 207 return false; 208 } 209 210 Timeout* GetTimeout(int32_t aTimeoutId, Timeout::Reason aReason) { 211 Timeout::TimeoutIdAndReason key = {aTimeoutId, aReason}; 212 return mTimeouts->Get(key); 213 } 214 215 private: 216 // The TimeoutManager that owns this Timeouts structure. This is 217 // mainly used to call state inspecting methods like IsValidFiringId(). 218 const TimeoutManager& mManager; 219 220 using TimeoutList = mozilla::LinkedList<RefPtr<Timeout>>; 221 222 // mTimeoutList is generally sorted by mWhen, but new values are always 223 // inserted after any Timeouts with a valid FiringId. 224 TimeoutList mTimeoutList; 225 226 // mTimeouts is a set of all the timeouts in the mTimeoutList. 227 // It let's one to have O(1) check whether a timeout id/reason is in the 228 // list. 229 RefPtr<Timeout::TimeoutSet> mTimeouts; 230 }; 231 232 // Each nsIGlobalObject object has a TimeoutManager member. This 233 // reference points to that holder object. 234 nsIGlobalObject& mGlobalObject; 235 // The executor is specific to the nsGlobalWindow/TimeoutManager, but it 236 // can live past the destruction of the window if its scheduled. Therefore 237 // it must be a separate ref-counted object. 238 RefPtr<TimeoutExecutor> mExecutor; 239 // For timeouts run off the idle queue 240 RefPtr<TimeoutExecutor> mIdleExecutor; 241 // The list of timeouts coming from non-tracking scripts. 242 Timeouts mTimeouts; 243 int32_t mTimeoutIdCounter; 244 uint32_t mNextFiringId; 245 #ifdef DEBUG 246 int64_t mFiringIndex; 247 int64_t mLastFiringIndex; 248 #endif 249 AutoTArray<uint32_t, 2> mFiringIdStack; 250 mozilla::dom::Timeout* mRunningTimeout; 251 252 // Timeouts that would have fired but are being deferred until MainThread 253 // is idle (because we're loading) 254 Timeouts mIdleTimeouts; 255 256 // The current idle request callback timeout handle 257 int32_t mIdleCallbackTimeoutCounter; 258 259 nsCOMPtr<nsITimer> mThrottleTimeoutsTimer; 260 mozilla::TimeStamp mLastBudgetUpdate; 261 mozilla::TimeDuration mExecutionBudget; 262 263 bool mThrottleTimeouts; 264 bool mThrottleTrackingTimeouts; 265 bool mBudgetThrottleTimeouts; 266 267 bool mIsLoading; 268 nsCOMPtr<nsISerialEventTarget> mEventTarget; 269 270 const bool mIsWindow; 271 272 const bool mIsChromeWorker; 273 274 uint32_t mNestingLevel{0}; 275 276 static uint32_t sNestingLevel; 277 278 TimeoutBudgetManager mBudgetManager; 279 static TimeoutBudgetManager sBudgetManager; 280 }; 281 282 } // namespace dom 283 } // namespace mozilla 284 285 #endif