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 |