| File: | var/lib/jenkins/workspace/firefox-scan-build/dom/base/CCGCScheduler.h | 
| Warning: | line 128, column 7 Excessive padding in 'class mozilla::CCGCScheduler' (32 padding bytes, where 0 is optimal). Optimal fields order: mCCBlockStart, mLastForgetSkippableEndTime, mForgetSkippableFrequencyStartTime, mLastCCEndTime, mLastForgetSkippableCycleEndTime, mCCDelay, mGCRunner, mCCRunner, mShrinkingGCTimer, mFullGCTimer, mActiveIntersliceGCBudget, mTriggeredGCDeadline, mInterruptRequested, mForgetSkippableCounter, mCCRunnerState, mCCRunnerEarlyFireCount, mPreviousSuspectedCount, mCleanupsSinceLastGC, mMajorGCReason, mEagerMajorGCReason, mEagerMinorGCReason, mCCollectedWaitingForGC, mCCollectedZonesWaitingForGC, mLikelyShortLivingObjectsNeedingGC, mInIncrementalGC, mAskParentBeforeMajorGC, mHaveAskedParent, mReadyForMajorGC, mWantAtLeastRegularGC, mDidShutdown, mHasRunGC, mNeedsFullCC, mNeedsFullGC, mNeedsGCAfterCC, mCCReason, mIsCompactingOnUserInactive, mIsCollectingCycles, mUserIsActive, mCurrentCollectionHasSeenNonIdle, mPreferFasterCollection, consider reordering the fields or adding explicit padding members  | 
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* This Source Code Form is subject to the terms of the Mozilla Public | 
| 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this | 
| 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | 
| 4 | |
| 5 | #include "js/SliceBudget.h" | 
| 6 | #include "mozilla/ArrayUtils.h" | 
| 7 | #include "mozilla/CycleCollectedJSContext.h" | 
| 8 | #include "mozilla/IdleTaskRunner.h" | 
| 9 | #include "mozilla/MainThreadIdlePeriod.h" | 
| 10 | #include "mozilla/Telemetry.h" | 
| 11 | #include "mozilla/TimeStamp.h" | 
| 12 | #include "mozilla/ipc/IdleSchedulerChild.h" | 
| 13 | #include "nsCycleCollector.h" | 
| 14 | #include "nsJSEnvironment.h" | 
| 15 | #include "nsCycleCollectionParticipant.h" | 
| 16 | |
| 17 | namespace mozilla { | 
| 18 | |
| 19 | extern const TimeDuration kOneMinute; | 
| 20 | |
| 21 | // The amount of time we wait between a request to CC (after GC ran) | 
| 22 | // and doing the actual CC. | 
| 23 | extern const TimeDuration kCCDelay; | 
| 24 | |
| 25 | extern const TimeDuration kCCSkippableDelay; | 
| 26 | |
| 27 | // In case the cycle collector isn't run at all, we don't want forget skippables | 
| 28 | // to run too often. So limit the forget skippable cycle to start at earliest 2 | 
| 29 | // seconds after the end of the previous cycle. | 
| 30 | extern const TimeDuration kTimeBetweenForgetSkippableCycles; | 
| 31 | |
| 32 | // ForgetSkippable is usually fast, so we can use small budgets. | 
| 33 | // This isn't a real budget but a hint to IdleTaskRunner whether there | 
| 34 | // is enough time to call ForgetSkippable. | 
| 35 | extern const TimeDuration kForgetSkippableSliceDuration; | 
| 36 | |
| 37 | // Maximum amount of time that should elapse between incremental CC slices | 
| 38 | extern const TimeDuration kICCIntersliceDelay; | 
| 39 | |
| 40 | // Time budget for an incremental CC slice when using timer to run it. | 
| 41 | extern const TimeDuration kICCSliceBudget; | 
| 42 | // Minimum budget for an incremental CC slice when using idle time to run it. | 
| 43 | extern const TimeDuration kIdleICCSliceBudget; | 
| 44 | |
| 45 | // Maximum total duration for an ICC | 
| 46 | extern const TimeDuration kMaxICCDuration; | 
| 47 | |
| 48 | // Force a CC after this long if there's more than NS_CC_FORCED_PURPLE_LIMIT | 
| 49 | // objects in the purple buffer. | 
| 50 | extern const TimeDuration kCCForced; | 
| 51 | constexpr uint32_t kCCForcedPurpleLimit = 10; | 
| 52 | |
| 53 | // Don't allow an incremental GC to lock out the CC for too long. | 
| 54 | extern const TimeDuration kMaxCCLockedoutTime; | 
| 55 | |
| 56 | // Trigger a CC if the purple buffer exceeds this size when we check it. | 
| 57 | constexpr uint32_t kCCPurpleLimit = 200; | 
| 58 | |
| 59 | // Actions performed by the GCRunner state machine. | 
| 60 | enum class GCRunnerAction { | 
| 61 | MinorGC, // Run a minor GC (nursery collection) | 
| 62 | WaitToMajorGC, // We want to start a new major GC | 
| 63 | StartMajorGC, // The parent says we may begin our major GC | 
| 64 | GCSlice, // Run a single slice of a major GC | 
| 65 | None | 
| 66 | }; | 
| 67 | |
| 68 | struct GCRunnerStep { | 
| 69 | GCRunnerAction mAction; | 
| 70 | JS::GCReason mReason; | 
| 71 | }; | 
| 72 | |
| 73 | // Actions that are output from the CCRunner state machine. | 
| 74 | enum class CCRunnerAction { | 
| 75 | // Do nothing. | 
| 76 | None, | 
| 77 | |
| 78 | // We crossed an eager minor GC threshold in the middle of an incremental CC, | 
| 79 | // and we have some idle time. | 
| 80 | MinorGC, | 
| 81 | |
| 82 | // Various cleanup actions. | 
| 83 | ForgetSkippable, | 
| 84 | CleanupContentUnbinder, | 
| 85 | CleanupDeferred, | 
| 86 | |
| 87 | // Do the actual cycle collection (build the graph etc). | 
| 88 | CycleCollect, | 
| 89 | |
| 90 | // All done. | 
| 91 | StopRunning | 
| 92 | }; | 
| 93 | |
| 94 | enum CCRunnerYield { Continue, Yield }; | 
| 95 | |
| 96 | enum CCRunnerForgetSkippableRemoveChildless { | 
| 97 | KeepChildless = false, | 
| 98 | RemoveChildless = true | 
| 99 | }; | 
| 100 | |
| 101 | struct CCRunnerStep { | 
| 102 | // The action the scheduler is instructing the caller to perform. | 
| 103 | CCRunnerAction mAction; | 
| 104 | |
| 105 | // Whether to stop processing actions for this invocation of the timer | 
| 106 | // callback. | 
| 107 | CCRunnerYield mYield; | 
| 108 | |
| 109 | union ActionData { | 
| 110 | // If the action is ForgetSkippable, then whether to remove childless nodes | 
| 111 | // or not. | 
| 112 | CCRunnerForgetSkippableRemoveChildless mRemoveChildless; | 
| 113 | |
| 114 | // If the action is CycleCollect, the reason for the collection. | 
| 115 | CCReason mCCReason; | 
| 116 | |
| 117 | // If the action is MinorGC, the reason for the GC. | 
| 118 | JS::GCReason mReason; | 
| 119 | |
| 120 | MOZ_IMPLICIT ActionData(CCRunnerForgetSkippableRemoveChildless v) | 
| 121 | : mRemoveChildless(v) {} | 
| 122 | MOZ_IMPLICIT ActionData(CCReason v) : mCCReason(v) {} | 
| 123 | MOZ_IMPLICIT ActionData(JS::GCReason v) : mReason(v) {} | 
| 124 | ActionData() = default; | 
| 125 | } mParam; | 
| 126 | }; | 
| 127 | |
| 128 | class CCGCScheduler { | 
Excessive padding in 'class mozilla::CCGCScheduler' (32 padding bytes, where 0 is optimal). Optimal fields order: mCCBlockStart, mLastForgetSkippableEndTime, mForgetSkippableFrequencyStartTime, mLastCCEndTime, mLastForgetSkippableCycleEndTime, mCCDelay, mGCRunner, mCCRunner, mShrinkingGCTimer, mFullGCTimer, mActiveIntersliceGCBudget, mTriggeredGCDeadline, mInterruptRequested, mForgetSkippableCounter, mCCRunnerState, mCCRunnerEarlyFireCount, mPreviousSuspectedCount, mCleanupsSinceLastGC, mMajorGCReason, mEagerMajorGCReason, mEagerMinorGCReason, mCCollectedWaitingForGC, mCCollectedZonesWaitingForGC, mLikelyShortLivingObjectsNeedingGC, mInIncrementalGC, mAskParentBeforeMajorGC, mHaveAskedParent, mReadyForMajorGC, mWantAtLeastRegularGC, mDidShutdown, mHasRunGC, mNeedsFullCC, mNeedsFullGC, mNeedsGCAfterCC, mCCReason, mIsCompactingOnUserInactive, mIsCollectingCycles, mUserIsActive, mCurrentCollectionHasSeenNonIdle, mPreferFasterCollection, consider reordering the fields or adding explicit padding members  | |
| 129 | public: | 
| 130 | CCGCScheduler() | 
| 131 | : mAskParentBeforeMajorGC(XRE_IsContentProcess()), | 
| 132 | mReadyForMajorGC(!mAskParentBeforeMajorGC), | 
| 133 | mInterruptRequested(false) {} | 
| 134 | |
| 135 | bool CCRunnerFired(TimeStamp aDeadline); | 
| 136 | |
| 137 | // Parameter setting | 
| 138 | |
| 139 | void SetActiveIntersliceGCBudget(TimeDuration aDuration) { | 
| 140 | mActiveIntersliceGCBudget = aDuration; | 
| 141 | } | 
| 142 | |
| 143 | // State retrieval | 
| 144 | |
| 145 | TimeDuration GetCCBlockedTime(TimeStamp aNow) const { | 
| 146 |     MOZ_ASSERT(mInIncrementalGC)do { static_assert( mozilla::detail::AssertionConditionType< decltype(mInIncrementalGC)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(mInIncrementalGC))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("mInIncrementalGC" , "/var/lib/jenkins/workspace/firefox-scan-build/dom/base/CCGCScheduler.h" , 146); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mInIncrementalGC" ")"); do { *((volatile int*)__null) = 146; __attribute__((nomerge )) ::abort(); } while (false); } } while (false);  | 
| 147 |     MOZ_ASSERT(!mCCBlockStart.IsNull())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!mCCBlockStart.IsNull())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!mCCBlockStart.IsNull()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("!mCCBlockStart.IsNull()" , "/var/lib/jenkins/workspace/firefox-scan-build/dom/base/CCGCScheduler.h" , 147); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mCCBlockStart.IsNull()" ")"); do { *((volatile int*)__null) = 147; __attribute__((nomerge )) ::abort(); } while (false); } } while (false);  | 
| 148 | return aNow - mCCBlockStart; | 
| 149 | } | 
| 150 | |
| 151 | bool InIncrementalGC() const { return mInIncrementalGC; } | 
| 152 | |
| 153 | TimeStamp GetLastCCEndTime() const { return mLastCCEndTime; } | 
| 154 | |
| 155 | bool IsEarlyForgetSkippable(uint32_t aN = kMajorForgetSkippableCalls) const { | 
| 156 | return mCleanupsSinceLastGC < aN; | 
| 157 | } | 
| 158 | |
| 159 | bool NeedsFullGC() const { return mNeedsFullGC; } | 
| 160 | |
| 161 | // Requests | 
| 162 | void PokeGC(JS::GCReason aReason, JSObject* aObj, TimeDuration aDelay = 0); | 
| 163 | void PokeShrinkingGC(); | 
| 164 | void PokeFullGC(); | 
| 165 | void MaybePokeCC(TimeStamp aNow, uint32_t aSuspectedCCObjects); | 
| 166 | void PokeMinorGC(JS::GCReason aReason); | 
| 167 | |
| 168 | void UserIsInactive(); | 
| 169 | void UserIsActive(); | 
| 170 | bool IsUserActive() const { return mUserIsActive; } | 
| 171 | |
| 172 | void KillShrinkingGCTimer(); | 
| 173 | void KillFullGCTimer(); | 
| 174 | void KillGCRunner(); | 
| 175 | void KillCCRunner(); | 
| 176 | void KillAllTimersAndRunners(); | 
| 177 | |
| 178 | JS::SliceBudget CreateGCSliceBudget(mozilla::TimeDuration aDuration, | 
| 179 | bool isIdle, bool isExtended) { | 
| 180 | mInterruptRequested = false; | 
| 181 | // Don't try to interrupt if we are in a mode where idle time is rare. | 
| 182 | auto budget = JS::SliceBudget( | 
| 183 | aDuration, mPreferFasterCollection ? nullptr : &mInterruptRequested); | 
| 184 | budget.idle = isIdle; | 
| 185 | budget.extended = isExtended; | 
| 186 | return budget; | 
| 187 | } | 
| 188 | |
| 189 | /* | 
| 190 | * aDelay is the delay before the first time the idle task runner runs. | 
| 191 | * Then it runs every | 
| 192 | * StaticPrefs::javascript_options_gc_delay_interslice() | 
| 193 | */ | 
| 194 | void EnsureGCRunner(TimeDuration aDelay); | 
| 195 | |
| 196 | // If GCRunner isn't active, this calls EnsureGCRunner(0). Otherwise the timer | 
| 197 | // is reset. | 
| 198 | void EnsureOrResetGCRunner(); | 
| 199 | |
| 200 | void EnsureCCRunner(TimeDuration aDelay, TimeDuration aBudget); | 
| 201 | |
| 202 | // State modification | 
| 203 | |
| 204 | void SetNeedsFullGC(bool aNeedGC = true) { mNeedsFullGC = aNeedGC; } | 
| 205 | |
| 206 | void SetWantMajorGC(JS::GCReason aReason) { | 
| 207 |     MOZ_ASSERT(aReason != JS::GCReason::NO_REASON)do { static_assert( mozilla::detail::AssertionConditionType< decltype(aReason != JS::GCReason::NO_REASON)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(aReason != JS::GCReason::NO_REASON ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "aReason != JS::GCReason::NO_REASON", "/var/lib/jenkins/workspace/firefox-scan-build/dom/base/CCGCScheduler.h" , 207); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aReason != JS::GCReason::NO_REASON" ")"); do { *((volatile int*)__null) = 207; __attribute__((nomerge )) ::abort(); } while (false); } } while (false);  | 
| 208 | |
| 209 | // If the GC being requested is not a shrinking GC set this flag. | 
| 210 | // If/when the shrinking GC timer fires but the user is active we check | 
| 211 | // this flag before canceling the GC, so as not to cancel the | 
| 212 | // non-shrinking GC being requested here. | 
| 213 | if (aReason != JS::GCReason::USER_INACTIVE) { | 
| 214 | mWantAtLeastRegularGC = true; | 
| 215 | } | 
| 216 | |
| 217 | // Force full GCs when called from reftests so that we collect dead zones | 
| 218 | // that have not been scheduled for collection. | 
| 219 | if (aReason == JS::GCReason::DOM_WINDOW_UTILS) { | 
| 220 | SetNeedsFullGC(); | 
| 221 | } | 
| 222 | |
| 223 | // USER_INACTIVE trumps everything, | 
| 224 | // FULL_GC_TIMER trumps everything except USER_INACTIVE, | 
| 225 | // all other reasons just use the latest reason. | 
| 226 | switch (aReason) { | 
| 227 | case JS::GCReason::USER_INACTIVE: | 
| 228 | mMajorGCReason = aReason; | 
| 229 | break; | 
| 230 | case JS::GCReason::FULL_GC_TIMER: | 
| 231 | if (mMajorGCReason != JS::GCReason::USER_INACTIVE) { | 
| 232 | mMajorGCReason = aReason; | 
| 233 | } | 
| 234 | break; | 
| 235 | default: | 
| 236 | if (mMajorGCReason != JS::GCReason::USER_INACTIVE && | 
| 237 | mMajorGCReason != JS::GCReason::FULL_GC_TIMER) { | 
| 238 | mMajorGCReason = aReason; | 
| 239 | } | 
| 240 | break; | 
| 241 | } | 
| 242 | } | 
| 243 | |
| 244 | void SetWantEagerMinorGC(JS::GCReason aReason) { | 
| 245 | if (mEagerMinorGCReason == JS::GCReason::NO_REASON) { | 
| 246 | mEagerMinorGCReason = aReason; | 
| 247 | } | 
| 248 | } | 
| 249 | |
| 250 | // Ensure that the current runner does a cycle collection, and trigger a GC | 
| 251 | // after it finishes. | 
| 252 | void EnsureCCThenGC(CCReason aReason) { | 
| 253 |     MOZ_ASSERT(mCCRunnerState != CCRunnerState::Inactive)do { static_assert( mozilla::detail::AssertionConditionType< decltype(mCCRunnerState != CCRunnerState::Inactive)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(mCCRunnerState != CCRunnerState::Inactive))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("mCCRunnerState != CCRunnerState::Inactive" , "/var/lib/jenkins/workspace/firefox-scan-build/dom/base/CCGCScheduler.h" , 253); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mCCRunnerState != CCRunnerState::Inactive" ")"); do { *((volatile int*)__null) = 253; __attribute__((nomerge )) ::abort(); } while (false); } } while (false);  | 
| 254 |     MOZ_ASSERT(aReason != CCReason::NO_REASON)do { static_assert( mozilla::detail::AssertionConditionType< decltype(aReason != CCReason::NO_REASON)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(aReason != CCReason::NO_REASON ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "aReason != CCReason::NO_REASON", "/var/lib/jenkins/workspace/firefox-scan-build/dom/base/CCGCScheduler.h" , 254); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aReason != CCReason::NO_REASON" ")"); do { *((volatile int*)__null) = 254; __attribute__((nomerge )) ::abort(); } while (false); } } while (false);  | 
| 255 | mNeedsFullCC = aReason; | 
| 256 | mNeedsGCAfterCC = true; | 
| 257 | } | 
| 258 | |
| 259 | // Returns false if we started and finished a major GC while waiting for a | 
| 260 | // response. | 
| 261 | [[nodiscard]] bool NoteReadyForMajorGC() { | 
| 262 | if (mMajorGCReason == JS::GCReason::NO_REASON || InIncrementalGC()) { | 
| 263 | return false; | 
| 264 | } | 
| 265 | mReadyForMajorGC = true; | 
| 266 | return true; | 
| 267 | } | 
| 268 | |
| 269 | // Starting a major GC (incremental or non-incremental). | 
| 270 | void NoteGCBegin(JS::GCReason aReason); | 
| 271 | |
| 272 | // Major GC completed. | 
| 273 | void NoteGCEnd(); | 
| 274 | |
| 275 | // A timer fired, but then decided not to run a GC. | 
| 276 | void NoteWontGC(); | 
| 277 | |
| 278 | void NoteMinorGCEnd() { mEagerMinorGCReason = JS::GCReason::NO_REASON; } | 
| 279 | |
| 280 | // This is invoked when we reach the actual cycle collection portion of the | 
| 281 | // overall cycle collection. | 
| 282 | void NoteCCBegin(); | 
| 283 | |
| 284 | // This is invoked when the whole process of collection is done -- i.e., CC | 
| 285 | // preparation (eg ForgetSkippables) in addition to the CC itself. There | 
| 286 | // really ought to be a separate name for the overall CC as opposed to the | 
| 287 | // actual cycle collection portion. | 
| 288 | void NoteCCEnd(const CycleCollectorResults& aResults, TimeStamp aWhen); | 
| 289 | |
| 290 | // A single slice has completed. | 
| 291 | void NoteGCSliceEnd(TimeStamp aStart, TimeStamp aEnd); | 
| 292 | |
| 293 | bool GCRunnerFired(TimeStamp aDeadline); | 
| 294 | bool GCRunnerFiredDoGC(TimeStamp aDeadline, const GCRunnerStep& aStep); | 
| 295 | |
| 296 | using MayGCPromise = | 
| 297 | MozPromise<bool, mozilla::ipc::ResponseRejectReason, true>; | 
| 298 | |
| 299 | // Returns null if we shouldn't GC now (eg a GC is already running). | 
| 300 | static RefPtr<MayGCPromise> MayGCNow(JS::GCReason reason); | 
| 301 | |
| 302 | // Check all of the various collector timers/runners and see if they are | 
| 303 | // waiting to fire. This does not check the Full GC Timer, as that's a | 
| 304 | // more expensive collection we run on a long timer. | 
| 305 | void RunNextCollectorTimer(JS::GCReason aReason, | 
| 306 | mozilla::TimeStamp aDeadline); | 
| 307 | |
| 308 | // When we decide to do a cycle collection but we're in the middle of an | 
| 309 | // incremental GC, the CC is "locked out" until the GC completes -- unless | 
| 310 | // the wait is too long, and we decide to finish the incremental GC early. | 
| 311 | void BlockCC(TimeStamp aNow) { | 
| 312 |     MOZ_ASSERT(mInIncrementalGC)do { static_assert( mozilla::detail::AssertionConditionType< decltype(mInIncrementalGC)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(mInIncrementalGC))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("mInIncrementalGC" , "/var/lib/jenkins/workspace/firefox-scan-build/dom/base/CCGCScheduler.h" , 312); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mInIncrementalGC" ")"); do { *((volatile int*)__null) = 312; __attribute__((nomerge )) ::abort(); } while (false); } } while (false);  | 
| 313 |     MOZ_ASSERT(mCCBlockStart.IsNull())do { static_assert( mozilla::detail::AssertionConditionType< decltype(mCCBlockStart.IsNull())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(mCCBlockStart.IsNull()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("mCCBlockStart.IsNull()" , "/var/lib/jenkins/workspace/firefox-scan-build/dom/base/CCGCScheduler.h" , 313); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mCCBlockStart.IsNull()" ")"); do { *((volatile int*)__null) = 313; __attribute__((nomerge )) ::abort(); } while (false); } } while (false);  | 
| 314 | mCCBlockStart = aNow; | 
| 315 | } | 
| 316 | |
| 317 | void UnblockCC() { mCCBlockStart = TimeStamp(); } | 
| 318 | |
| 319 | // Returns the number of purple buffer items that were processed and removed. | 
| 320 | void NoteForgetSkippableComplete(TimeStamp aNow, | 
| 321 | uint32_t aSuspectedCCObjects) { | 
| 322 | mLastForgetSkippableEndTime = aNow; | 
| 323 | mPreviousSuspectedCount = aSuspectedCCObjects; | 
| 324 | mCleanupsSinceLastGC++; | 
| 325 | } | 
| 326 | |
| 327 | // Test if we are in the NoteCCBegin .. NoteCCEnd interval. | 
| 328 | bool IsCollectingCycles() const { return mIsCollectingCycles; } | 
| 329 | |
| 330 | // The CC was abandoned without running a slice, so we only did forget | 
| 331 | // skippables. Prevent running another cycle soon. | 
| 332 | void NoteForgetSkippableOnlyCycle(TimeStamp aNow) { | 
| 333 | mLastForgetSkippableCycleEndTime = aNow; | 
| 334 | } | 
| 335 | |
| 336 | void Shutdown() { | 
| 337 | mDidShutdown = true; | 
| 338 | KillAllTimersAndRunners(); | 
| 339 | } | 
| 340 | |
| 341 | // Scheduling | 
| 342 | |
| 343 | // Return a budget along with a boolean saying whether to prefer to run short | 
| 344 | // slices and stop rather than continuing to the next phase of cycle | 
| 345 | // collection. | 
| 346 | JS::SliceBudget ComputeCCSliceBudget(TimeStamp aDeadline, | 
| 347 | TimeStamp aCCBeginTime, | 
| 348 | TimeStamp aPrevSliceEndTime, | 
| 349 | TimeStamp aNow, | 
| 350 | bool* aPreferShorterSlices) const; | 
| 351 | |
| 352 | JS::SliceBudget ComputeInterSliceGCBudget(TimeStamp aDeadline, | 
| 353 | TimeStamp aNow); | 
| 354 | |
| 355 | TimeDuration ComputeMinimumBudgetForRunner(TimeDuration aBaseValue); | 
| 356 | |
| 357 | bool ShouldForgetSkippable(uint32_t aSuspectedCCObjects) const { | 
| 358 | // Only do a forget skippable if there are more than a few new objects | 
| 359 | // or we're doing the initial forget skippables. | 
| 360 | return ((mPreviousSuspectedCount + 100) <= aSuspectedCCObjects) || | 
| 361 | mCleanupsSinceLastGC < kMajorForgetSkippableCalls; | 
| 362 | } | 
| 363 | |
| 364 | // There is reason to suspect that there may be a significant amount of | 
| 365 | // garbage to cycle collect: either we just finished a GC, or the purple | 
| 366 | // buffer is getting really big, or it's getting somewhat big and it has been | 
| 367 | // too long since the last CC. | 
| 368 | CCReason IsCCNeeded(TimeStamp aNow, uint32_t aSuspectedCCObjects) const { | 
| 369 | if (mNeedsFullCC != CCReason::NO_REASON) { | 
| 370 | return mNeedsFullCC; | 
| 371 | } | 
| 372 | if (aSuspectedCCObjects > kCCPurpleLimit) { | 
| 373 | return CCReason::MANY_SUSPECTED; | 
| 374 | } | 
| 375 | if (aSuspectedCCObjects > kCCForcedPurpleLimit && mLastCCEndTime && | 
| 376 | aNow - mLastCCEndTime > kCCForced) { | 
| 377 | return CCReason::TIMED; | 
| 378 | } | 
| 379 | return CCReason::NO_REASON; | 
| 380 | } | 
| 381 | |
| 382 | mozilla::CCReason ShouldScheduleCC(TimeStamp aNow, | 
| 383 | uint32_t aSuspectedCCObjects) const; | 
| 384 | |
| 385 | // If we collected a substantial amount of cycles, poke the GC since more | 
| 386 | // objects might be unreachable now. | 
| 387 | bool NeedsGCAfterCC() const { | 
| 388 | return mCCollectedWaitingForGC > 250 || mCCollectedZonesWaitingForGC > 0 || | 
| 389 | mLikelyShortLivingObjectsNeedingGC > 2500 || mNeedsGCAfterCC; | 
| 390 | } | 
| 391 | |
| 392 | bool IsLastEarlyCCTimer(int32_t aCurrentFireCount) const { | 
| 393 | int32_t numEarlyTimerFires = | 
| 394 | std::max(int32_t(mCCDelay / kCCSkippableDelay) - 2, 1); | 
| 395 | |
| 396 | return aCurrentFireCount >= numEarlyTimerFires; | 
| 397 | } | 
| 398 | |
| 399 | enum class CCRunnerState { | 
| 400 | Inactive, | 
| 401 | ReducePurple, | 
| 402 | CleanupChildless, | 
| 403 | CleanupContentUnbinder, | 
| 404 | CleanupDeferred, | 
| 405 | StartCycleCollection, | 
| 406 | CycleCollecting, | 
| 407 | Canceled, | 
| 408 | NumStates | 
| 409 | }; | 
| 410 | |
| 411 | void InitCCRunnerStateMachine(CCRunnerState initialState, CCReason aReason) { | 
| 412 | if (mCCRunner) { | 
| 413 | return; | 
| 414 | } | 
| 415 | |
| 416 |     MOZ_ASSERT(mCCReason == CCReason::NO_REASON)do { static_assert( mozilla::detail::AssertionConditionType< decltype(mCCReason == CCReason::NO_REASON)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(mCCReason == CCReason::NO_REASON ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "mCCReason == CCReason::NO_REASON", "/var/lib/jenkins/workspace/firefox-scan-build/dom/base/CCGCScheduler.h" , 416); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mCCReason == CCReason::NO_REASON" ")"); do { *((volatile int*)__null) = 416; __attribute__((nomerge )) ::abort(); } while (false); } } while (false);  | 
| 417 | mCCReason = aReason; | 
| 418 | |
| 419 | // The state machine should always have been deactivated after the previous | 
| 420 | // collection, however far that collection may have gone. | 
| 421 |     MOZ_ASSERT(mCCRunnerState == CCRunnerState::Inactive,do { static_assert( mozilla::detail::AssertionConditionType< decltype(mCCRunnerState == CCRunnerState::Inactive)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(mCCRunnerState == CCRunnerState::Inactive))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("mCCRunnerState == CCRunnerState::Inactive" " (" "DeactivateCCRunner should have been called" ")", "/var/lib/jenkins/workspace/firefox-scan-build/dom/base/CCGCScheduler.h" , 422); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mCCRunnerState == CCRunnerState::Inactive" ") (" "DeactivateCCRunner should have been called" ")"); do { *((volatile int*)__null) = 422; __attribute__((nomerge)) ::abort (); } while (false); } } while (false)  | 
| 422 |                "DeactivateCCRunner should have been called")do { static_assert( mozilla::detail::AssertionConditionType< decltype(mCCRunnerState == CCRunnerState::Inactive)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(mCCRunnerState == CCRunnerState::Inactive))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("mCCRunnerState == CCRunnerState::Inactive" " (" "DeactivateCCRunner should have been called" ")", "/var/lib/jenkins/workspace/firefox-scan-build/dom/base/CCGCScheduler.h" , 422); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mCCRunnerState == CCRunnerState::Inactive" ") (" "DeactivateCCRunner should have been called" ")"); do { *((volatile int*)__null) = 422; __attribute__((nomerge)) ::abort (); } while (false); } } while (false);  | 
| 423 | mCCRunnerState = initialState; | 
| 424 | |
| 425 | // Currently, there are only two entry points to the non-Inactive part of | 
| 426 | // the state machine. | 
| 427 | if (initialState == CCRunnerState::ReducePurple) { | 
| 428 | mCCDelay = kCCDelay; | 
| 429 | mCCRunnerEarlyFireCount = 0; | 
| 430 | } else if (initialState == CCRunnerState::CycleCollecting) { | 
| 431 | // Nothing needed. | 
| 432 | } else { | 
| 433 |       MOZ_CRASH("Invalid initial state")do { do { } while (false); MOZ_ReportCrash("" "Invalid initial state" , "/var/lib/jenkins/workspace/firefox-scan-build/dom/base/CCGCScheduler.h" , 433); AnnotateMozCrashReason("MOZ_CRASH(" "Invalid initial state" ")"); do { *((volatile int*)__null) = 433; __attribute__((nomerge )) ::abort(); } while (false); } while (false);  | 
| 434 | } | 
| 435 | } | 
| 436 | |
| 437 | void DeactivateCCRunner() { | 
| 438 | mCCRunnerState = CCRunnerState::Inactive; | 
| 439 | mCCReason = CCReason::NO_REASON; | 
| 440 | } | 
| 441 | |
| 442 | bool HasMoreIdleGCRunnerWork() const { | 
| 443 | return mMajorGCReason != JS::GCReason::NO_REASON || | 
| 444 | mEagerMajorGCReason != JS::GCReason::NO_REASON || | 
| 445 | mEagerMinorGCReason != JS::GCReason::NO_REASON; | 
| 446 | } | 
| 447 | |
| 448 | GCRunnerStep GetNextGCRunnerAction(TimeStamp aDeadline) const; | 
| 449 | |
| 450 | CCRunnerStep AdvanceCCRunner(TimeStamp aDeadline, TimeStamp aNow, | 
| 451 | uint32_t aSuspectedCCObjects); | 
| 452 | |
| 453 | // aStartTimeStamp : when the ForgetSkippable timer fired. This may be some | 
| 454 | // time ago, if an incremental GC needed to be finished. | 
| 455 | JS::SliceBudget ComputeForgetSkippableBudget(TimeStamp aStartTimeStamp, | 
| 456 | TimeStamp aDeadline); | 
| 457 | |
| 458 | bool PreferFasterCollection() const { return mPreferFasterCollection; } | 
| 459 | |
| 460 | private: | 
| 461 | // State | 
| 462 | |
| 463 | // An incremental GC is in progress, which blocks the CC from running for its | 
| 464 | // duration (or until it goes too long and is finished synchronously.) | 
| 465 | bool mInIncrementalGC = false; | 
| 466 | |
| 467 | // Whether to ask the parent process if now is a good time to GC (false for | 
| 468 | // the parent process.) | 
| 469 | const bool mAskParentBeforeMajorGC; | 
| 470 | |
| 471 | // We've asked the parent process if now is a good time to GC (do not ask | 
| 472 | // again). | 
| 473 | bool mHaveAskedParent = false; | 
| 474 | |
| 475 | // The parent process is ready for us to do a major GC. | 
| 476 | bool mReadyForMajorGC; | 
| 477 | |
| 478 | // Set when the IdleTaskRunner requests the current task be interrupted. | 
| 479 | // Cleared when the GC slice budget has detected the interrupt request. | 
| 480 | JS::SliceBudget::InterruptRequestFlag mInterruptRequested; | 
| 481 | |
| 482 | // When a shrinking GC has been requested but we back-out, if this is true | 
| 483 | // we run a non-shrinking GC. | 
| 484 | bool mWantAtLeastRegularGC = false; | 
| 485 | |
| 486 | // When the CC started actually waiting for the GC to finish. This will be | 
| 487 | // set to non-null at a later time than mCCLockedOut. | 
| 488 | TimeStamp mCCBlockStart; | 
| 489 | |
| 490 | bool mDidShutdown = false; | 
| 491 | |
| 492 | TimeStamp mLastForgetSkippableEndTime; | 
| 493 | uint32_t mForgetSkippableCounter = 0; | 
| 494 | TimeStamp mForgetSkippableFrequencyStartTime; | 
| 495 | TimeStamp mLastCCEndTime; | 
| 496 | TimeStamp mLastForgetSkippableCycleEndTime; | 
| 497 | |
| 498 | CCRunnerState mCCRunnerState = CCRunnerState::Inactive; | 
| 499 | int32_t mCCRunnerEarlyFireCount = 0; | 
| 500 | TimeDuration mCCDelay = kCCDelay; | 
| 501 | |
| 502 | // Prevent the very first CC from running before we have GC'd and set the | 
| 503 | // gray bits. | 
| 504 | bool mHasRunGC = false; | 
| 505 | |
| 506 | mozilla::CCReason mNeedsFullCC = CCReason::NO_REASON; | 
| 507 | bool mNeedsFullGC = true; | 
| 508 | bool mNeedsGCAfterCC = false; | 
| 509 | uint32_t mPreviousSuspectedCount = 0; | 
| 510 | |
| 511 | uint32_t mCleanupsSinceLastGC = UINT32_MAX(4294967295U); | 
| 512 | |
| 513 | // If the GC runner triggers a GC slice, this will be set to the idle deadline | 
| 514 | // or the null timestamp if non-idle. It will be Nothing at the end of an | 
| 515 | // internally-triggered slice. | 
| 516 | mozilla::Maybe<TimeStamp> mTriggeredGCDeadline; | 
| 517 | |
| 518 | RefPtr<IdleTaskRunner> mGCRunner; | 
| 519 | RefPtr<IdleTaskRunner> mCCRunner; | 
| 520 | nsITimer* mShrinkingGCTimer = nullptr; | 
| 521 | nsITimer* mFullGCTimer = nullptr; | 
| 522 | |
| 523 | mozilla::CCReason mCCReason = mozilla::CCReason::NO_REASON; | 
| 524 | JS::GCReason mMajorGCReason = JS::GCReason::NO_REASON; | 
| 525 | JS::GCReason mEagerMajorGCReason = JS::GCReason::NO_REASON; | 
| 526 | JS::GCReason mEagerMinorGCReason = JS::GCReason::NO_REASON; | 
| 527 | |
| 528 | bool mIsCompactingOnUserInactive = false; | 
| 529 | bool mIsCollectingCycles = false; | 
| 530 | bool mUserIsActive = true; | 
| 531 | |
| 532 | bool mCurrentCollectionHasSeenNonIdle = false; | 
| 533 | bool mPreferFasterCollection = false; | 
| 534 | |
| 535 | public: | 
| 536 | uint32_t mCCollectedWaitingForGC = 0; | 
| 537 | uint32_t mCCollectedZonesWaitingForGC = 0; | 
| 538 | uint32_t mLikelyShortLivingObjectsNeedingGC = 0; | 
| 539 | |
| 540 | // Configuration parameters | 
| 541 | |
| 542 | TimeDuration mActiveIntersliceGCBudget = TimeDuration::FromMilliseconds(5); | 
| 543 | }; | 
| 544 | |
| 545 | } // namespace mozilla |