Bug Summary

File:root/firefox-clang/obj-x86_64-pc-linux-gnu/dom/base/./../../../dom/base/CCGCScheduler.h
Warning:line 129, 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

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -O2 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name Unified_cpp_dom_base0.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/dom/base -fcoverage-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/dom/base -resource-dir /usr/lib/llvm-23/lib/clang/23 -include /root/firefox-clang/config/gcc_hidden.h -include /root/firefox-clang/obj-x86_64-pc-linux-gnu/mozilla-config.h -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/stl_wrappers -D _GLIBCXX_ASSERTIONS=1 -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D DEBUG=1 -D MOZ_HAS_MOZGLUE -D MOZILLA_INTERNAL_API -D IMPL_LIBXUL -D MOZ_SUPPORT_LEAKCHECKING -D STATIC_EXPORTABLE_JS_API -I /root/firefox-clang/dom/base -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dom/base -I /root/firefox-clang/dom/battery -I /root/firefox-clang/dom/events -I /root/firefox-clang/dom/media -I /root/firefox-clang/dom/network -I /root/firefox-clang/caps -I /root/firefox-clang/docshell/base -I /root/firefox-clang/dom/base -I /root/firefox-clang/dom/file -I /root/firefox-clang/dom/geolocation -I /root/firefox-clang/dom/html -I /root/firefox-clang/dom/ipc -I /root/firefox-clang/dom/storage -I /root/firefox-clang/dom/svg -I /root/firefox-clang/dom/xml -I /root/firefox-clang/dom/xslt/xpath -I /root/firefox-clang/dom/xul -I /root/firefox-clang/extensions/spellcheck/src -I /root/firefox-clang/gfx/2d -I /root/firefox-clang/image -I /root/firefox-clang/js/xpconnect/loader -I /root/firefox-clang/js/xpconnect/src -I /root/firefox-clang/js/xpconnect/wrappers -I /root/firefox-clang/layout/base -I /root/firefox-clang/layout/forms -I /root/firefox-clang/layout/generic -I /root/firefox-clang/layout/style -I /root/firefox-clang/layout/xul -I /root/firefox-clang/netwerk/base -I /root/firefox-clang/netwerk/protocol/http -I /root/firefox-clang/netwerk/url-classifier -I /root/firefox-clang/parser/htmlparser -I /root/firefox-clang/security/manager/ssl -I /root/firefox-clang/third_party/xsimd/include -I /root/firefox-clang/widget -I /root/firefox-clang/xpcom/build -I /root/firefox-clang/xpcom/ds -I /root/firefox-clang/netwerk/sctp/datachannel -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/ipc/ipdl/_ipdlheaders -I /root/firefox-clang/ipc/chromium/src -I /root/firefox-clang/third_party/abseil-cpp -I /root/firefox-clang/toolkit/components/telemetry -I /root/firefox-clang/xpcom/base -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -I /usr/include/gtk-3.0/unix-print -I /usr/include/gtk-3.0 -I /usr/include/pango-1.0 -I /usr/include/cloudproviders -I /usr/include/at-spi2-atk/2.0 -I /usr/include/at-spi-2.0 -I /usr/include/dbus-1.0 -I /usr/lib/x86_64-linux-gnu/dbus-1.0/include -I /usr/include/fribidi -I /usr/include/harfbuzz -I /usr/include/atk-1.0 -I /usr/include/cairo -I /usr/include/pixman-1 -I /usr/include/gdk-pixbuf-2.0 -I /usr/include/glycin-2 -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/gio-unix-2.0 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/libmount -I /usr/include/blkid -I /usr/include/sysprof-6 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/x86_64-linux-gnu/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16/backward -internal-isystem /usr/lib/llvm-23/lib/clang/23/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-error=pessimizing-move -Wno-error=large-by-value-copy=128 -Wno-error=implicit-int-float-conversion -Wno-error=thread-safety-analysis -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -Wno-character-conversion -std=gnu++20 -fdeprecated-macro -ferror-limit 19 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fno-implicit-modules -fskip-odr-check-in-gmf -fno-sized-deallocation -fno-aligned-allocation -fdiagnostics-absolute-paths -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -mllvm -dwarf-linkage-names=Abstract -faddrsig -fdwarf2-cfi-asm -o /tmp/scan-build-2026-09-01-224014-2642839-1 -x c++ Unified_cpp_dom_base0.cpp
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#ifndef mozilla_dom_CCGCScheduler_h
6#define mozilla_dom_CCGCScheduler_h
7
8#include "js/SliceBudget.h"
9#include "mozilla/CycleCollectedJSContext.h"
10#include "mozilla/IdleTaskRunner.h"
11#include "mozilla/MainThreadIdlePeriod.h"
12#include "mozilla/TimeStamp.h"
13#include "mozilla/ipc/IdleSchedulerChild.h"
14#include "nsCycleCollectionParticipant.h"
15#include "nsCycleCollector.h"
16#include "nsJSEnvironment.h"
17
18namespace mozilla {
19
20extern const TimeDuration kOneMinute;
21
22// The amount of time we wait between a request to CC (after GC ran)
23// and doing the actual CC.
24extern const TimeDuration kCCDelay;
25
26extern const TimeDuration kCCSkippableDelay;
27
28// In case the cycle collector isn't run at all, we don't want forget skippables
29// to run too often. So limit the forget skippable cycle to start at earliest 2
30// seconds after the end of the previous cycle.
31extern const TimeDuration kTimeBetweenForgetSkippableCycles;
32
33// ForgetSkippable is usually fast, so we can use small budgets.
34// This isn't a real budget but a hint to IdleTaskRunner whether there
35// is enough time to call ForgetSkippable.
36extern const TimeDuration kForgetSkippableSliceDuration;
37
38// Maximum amount of time that should elapse between incremental CC slices
39extern const TimeDuration kICCIntersliceDelay;
40
41// Time budget for an incremental CC slice when using timer to run it.
42extern const TimeDuration kICCSliceBudget;
43// Minimum budget for an incremental CC slice when using idle time to run it.
44extern const TimeDuration kIdleICCSliceBudget;
45
46// Maximum total duration for an ICC
47extern const TimeDuration kMaxICCDuration;
48
49// Force a CC after this long if there's more than NS_CC_FORCED_PURPLE_LIMIT
50// objects in the purple buffer.
51extern const TimeDuration kCCForced;
52constexpr uint32_t kCCForcedPurpleLimit = 10;
53
54// Don't allow an incremental GC to lock out the CC for too long.
55extern const TimeDuration kMaxCCLockedoutTime;
56
57// Trigger a CC if the purple buffer exceeds this size when we check it.
58constexpr uint32_t kCCPurpleLimit = 200;
59
60// Actions performed by the GCRunner state machine.
61enum class GCRunnerAction {
62 MinorGC, // Run a minor GC (nursery collection)
63 WaitToMajorGC, // We want to start a new major GC
64 StartMajorGC, // The parent says we may begin our major GC
65 GCSlice, // Run a single slice of a major GC
66 None
67};
68
69struct GCRunnerStep {
70 GCRunnerAction mAction;
71 JS::GCReason mReason;
72};
73
74// Actions that are output from the CCRunner state machine.
75enum class CCRunnerAction {
76 // Do nothing.
77 None,
78
79 // We crossed an eager minor GC threshold in the middle of an incremental CC,
80 // and we have some idle time.
81 MinorGC,
82
83 // Various cleanup actions.
84 ForgetSkippable,
85 CleanupContentUnbinder,
86 CleanupDeferred,
87
88 // Do the actual cycle collection (build the graph etc).
89 CycleCollect,
90
91 // All done.
92 StopRunning
93};
94
95enum CCRunnerYield { Continue, Yield };
96
97enum CCRunnerForgetSkippableRemoveChildless {
98 KeepChildless = false,
99 RemoveChildless = true
100};
101
102struct CCRunnerStep {
103 // The action the scheduler is instructing the caller to perform.
104 CCRunnerAction mAction;
105
106 // Whether to stop processing actions for this invocation of the timer
107 // callback.
108 CCRunnerYield mYield;
109
110 union ActionData {
111 // If the action is ForgetSkippable, then whether to remove childless nodes
112 // or not.
113 CCRunnerForgetSkippableRemoveChildless mRemoveChildless;
114
115 // If the action is CycleCollect, the reason for the collection.
116 CCReason mCCReason;
117
118 // If the action is MinorGC, the reason for the GC.
119 JS::GCReason mReason;
120
121 MOZ_IMPLICIT ActionData(CCRunnerForgetSkippableRemoveChildless v)
122 : mRemoveChildless(v) {}
123 MOZ_IMPLICIT ActionData(CCReason v) : mCCReason(v) {}
124 MOZ_IMPLICIT ActionData(JS::GCReason v) : mReason(v) {}
125 ActionData() = default;
126 } mParam;
127};
128
129class 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
130 public:
131 CCGCScheduler()
132 : mAskParentBeforeMajorGC(XRE_IsContentProcess()),
133 mReadyForMajorGC(!mAskParentBeforeMajorGC),
134 mInterruptRequested(false) {}
135
136 bool CCRunnerFired(TimeStamp aDeadline);
137
138 // Parameter setting
139
140 void SetActiveIntersliceGCBudget(TimeDuration aDuration) {
141 mActiveIntersliceGCBudget = aDuration;
142 }
143
144 // State retrieval
145
146 TimeDuration GetCCBlockedTime(TimeStamp aNow) const {
147 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"
, "./../../../dom/base/CCGCScheduler.h", 147); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "mInIncrementalGC" ")"); do { MOZ_CrashSequence
(__null, 147); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
148 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()"
, "./../../../dom/base/CCGCScheduler.h", 148); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!mCCBlockStart.IsNull()" ")"); do { MOZ_CrashSequence
(__null, 148); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
149 return aNow - mCCBlockStart;
150 }
151
152 bool InIncrementalGC() const { return mInIncrementalGC; }
153
154 TimeStamp GetLastCCEndTime() const { return mLastCCEndTime; }
155
156 bool IsEarlyForgetSkippable(uint32_t aN = kMajorForgetSkippableCalls) const {
157 return mCleanupsSinceLastGC < aN;
158 }
159
160 bool NeedsFullGC() const { return mNeedsFullGC; }
161
162 // Requests
163 void PokeGC(JS::GCReason aReason, JSObject* aObj, TimeDuration aDelay = {});
164 void PokeShrinkingGC();
165 void PokeFullGC();
166 void MaybePokeCC(TimeStamp aNow, uint32_t aSuspectedCCObjects);
167 void PokeMinorGC(JS::GCReason aReason);
168
169 void UserIsInactive();
170 void UserIsActive();
171 bool IsUserActive() const { return mUserIsActive; }
172
173 void KillShrinkingGCTimer();
174 void KillFullGCTimer();
175 void KillGCRunner();
176 void KillCCRunner();
177 void KillAllTimersAndRunners();
178
179 JS::SliceBudget CreateGCSliceBudget(mozilla::TimeDuration aDuration,
180 bool isIdle, bool isExtended) {
181 mInterruptRequested = false;
182 // Don't try to interrupt if we are in a mode where idle time is rare.
183 auto budget = JS::SliceBudget(
184 aDuration, mPreferFasterCollection ? nullptr : &mInterruptRequested);
185 budget.idle = isIdle;
186 budget.extended = isExtended;
187 return budget;
188 }
189
190 /*
191 * aDelay is the delay before the first time the idle task runner runs.
192 * Then it runs every
193 * StaticPrefs::javascript_options_gc_delay_interslice()
194 */
195 void EnsureGCRunner(TimeDuration aDelay = {});
196
197 // If GCRunner isn't active, this calls EnsureGCRunner(). Otherwise the timer
198 // is reset.
199 void EnsureOrResetGCRunner();
200
201 void EnsureCCRunner(TimeDuration aDelay, TimeDuration aBudget);
202
203 // State modification
204
205 void SetNeedsFullGC(bool aNeedGC = true) { mNeedsFullGC = aNeedGC; }
206
207 void SetWantMajorGC(JS::GCReason aReason) {
208 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", "./../../../dom/base/CCGCScheduler.h"
, 208); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aReason != JS::GCReason::NO_REASON"
")"); do { MOZ_CrashSequence(__null, 208); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
209
210 // If the GC being requested is not a shrinking GC set this flag.
211 // If/when the shrinking GC timer fires but the user is active we check
212 // this flag before canceling the GC, so as not to cancel the
213 // non-shrinking GC being requested here.
214 if (aReason != JS::GCReason::USER_INACTIVE) {
215 mWantAtLeastRegularGC = true;
216 }
217
218 // Force full GCs when called from reftests so that we collect dead zones
219 // that have not been scheduled for collection.
220 if (aReason == JS::GCReason::DOM_WINDOW_UTILS) {
221 SetNeedsFullGC();
222 }
223
224 // USER_INACTIVE trumps everything,
225 // FULL_GC_TIMER trumps everything except USER_INACTIVE,
226 // all other reasons just use the latest reason.
227 switch (aReason) {
228 case JS::GCReason::USER_INACTIVE:
229 mMajorGCReason = aReason;
230 break;
231 case JS::GCReason::FULL_GC_TIMER:
232 if (mMajorGCReason != JS::GCReason::USER_INACTIVE) {
233 mMajorGCReason = aReason;
234 }
235 break;
236 default:
237 if (mMajorGCReason != JS::GCReason::USER_INACTIVE &&
238 mMajorGCReason != JS::GCReason::FULL_GC_TIMER) {
239 mMajorGCReason = aReason;
240 }
241 break;
242 }
243 }
244
245 void SetWantEagerMinorGC(JS::GCReason aReason) {
246 if (mEagerMinorGCReason == JS::GCReason::NO_REASON) {
247 mEagerMinorGCReason = aReason;
248 }
249 }
250
251 // Ensure that the current runner does a cycle collection, and trigger a GC
252 // after it finishes.
253 void EnsureCCThenGC(CCReason aReason) {
254 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"
, "./../../../dom/base/CCGCScheduler.h", 254); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "mCCRunnerState != CCRunnerState::Inactive"
")"); do { MOZ_CrashSequence(__null, 254); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
255 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", "./../../../dom/base/CCGCScheduler.h"
, 255); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aReason != CCReason::NO_REASON"
")"); do { MOZ_CrashSequence(__null, 255); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
256 mNeedsFullCC = aReason;
257 mNeedsGCAfterCC = true;
258 }
259
260 // Returns false if we started and finished a major GC while waiting for a
261 // response.
262 [[nodiscard]] bool NoteReadyForMajorGC() {
263 if (mMajorGCReason == JS::GCReason::NO_REASON || InIncrementalGC()) {
264 return false;
265 }
266 mReadyForMajorGC = true;
267 return true;
268 }
269
270 // Starting a major GC (incremental or non-incremental).
271 void NoteGCBegin(JS::GCReason aReason);
272
273 // Major GC completed.
274 void NoteGCEnd();
275
276 // A timer fired, but then decided not to run a GC.
277 void NoteWontGC();
278
279 void NoteMinorGCEnd() { mEagerMinorGCReason = JS::GCReason::NO_REASON; }
280
281 // This is invoked when we reach the actual cycle collection portion of the
282 // overall cycle collection.
283 void NoteCCBegin();
284
285 // This is invoked when the whole process of collection is done -- i.e., CC
286 // preparation (eg ForgetSkippables) in addition to the CC itself. There
287 // really ought to be a separate name for the overall CC as opposed to the
288 // actual cycle collection portion.
289 void NoteCCEnd(const CycleCollectorResults& aResults, TimeStamp aWhen);
290
291 // A single slice has completed.
292 void NoteGCSliceEnd(TimeStamp aStart, TimeStamp aEnd);
293
294 bool GCRunnerFired(TimeStamp aDeadline);
295 bool GCRunnerFiredDoGC(TimeStamp aDeadline, const GCRunnerStep& aStep);
296
297 using MayGCPromise =
298 MozPromise<bool, mozilla::ipc::ResponseRejectReason, true>;
299
300 // Returns null if we shouldn't GC now (eg a GC is already running).
301 static RefPtr<MayGCPromise> MayGCNow(JS::GCReason reason);
302
303 // Check all of the various collector timers/runners and see if they are
304 // waiting to fire. This does not check the Full GC Timer, as that's a
305 // more expensive collection we run on a long timer.
306 void RunNextCollectorTimer(JS::GCReason aReason,
307 mozilla::TimeStamp aDeadline);
308
309 // When we decide to do a cycle collection but we're in the middle of an
310 // incremental GC, the CC is "locked out" until the GC completes -- unless
311 // the wait is too long, and we decide to finish the incremental GC early.
312 void BlockCC(TimeStamp aNow) {
313 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"
, "./../../../dom/base/CCGCScheduler.h", 313); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "mInIncrementalGC" ")"); do { MOZ_CrashSequence
(__null, 313); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
314 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()"
, "./../../../dom/base/CCGCScheduler.h", 314); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "mCCBlockStart.IsNull()" ")"); do { MOZ_CrashSequence
(__null, 314); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
315 mCCBlockStart = aNow;
316 }
317
318 void UnblockCC() { mCCBlockStart = TimeStamp(); }
319
320 // Returns the number of purple buffer items that were processed and removed.
321 void NoteForgetSkippableComplete(TimeStamp aNow,
322 uint32_t aSuspectedCCObjects) {
323 mLastForgetSkippableEndTime = aNow;
324 mPreviousSuspectedCount = aSuspectedCCObjects;
325 mCleanupsSinceLastGC++;
326 }
327
328 // Test if we are in the NoteCCBegin .. NoteCCEnd interval.
329 bool IsCollectingCycles() const { return mIsCollectingCycles; }
330
331 // The CC was abandoned without running a slice, so we only did forget
332 // skippables. Prevent running another cycle soon.
333 void NoteForgetSkippableOnlyCycle(TimeStamp aNow) {
334 mLastForgetSkippableCycleEndTime = aNow;
335 }
336
337 void Shutdown() {
338 mDidShutdown = true;
339 KillAllTimersAndRunners();
340 }
341
342 // Scheduling
343
344 // Return a budget along with a boolean saying whether to prefer to run short
345 // slices and stop rather than continuing to the next phase of cycle
346 // collection.
347 JS::SliceBudget ComputeCCSliceBudget(TimeStamp aDeadline,
348 TimeStamp aCCBeginTime,
349 TimeStamp aPrevSliceEndTime,
350 TimeStamp aNow,
351 bool* aPreferShorterSlices) const;
352
353 JS::SliceBudget ComputeInterSliceGCBudget(TimeStamp aDeadline,
354 TimeStamp aNow);
355
356 TimeDuration ComputeMinimumBudgetForRunner(TimeDuration aBaseValue);
357
358 bool ShouldForgetSkippable(uint32_t aSuspectedCCObjects) const {
359 // Only do a forget skippable if there are more than a few new objects
360 // or we're doing the initial forget skippables.
361 return ((mPreviousSuspectedCount + 100) <= aSuspectedCCObjects) ||
362 mCleanupsSinceLastGC < kMajorForgetSkippableCalls;
363 }
364
365 // There is reason to suspect that there may be a significant amount of
366 // garbage to cycle collect: either we just finished a GC, or the purple
367 // buffer is getting really big, or it's getting somewhat big and it has been
368 // too long since the last CC.
369 CCReason IsCCNeeded(TimeStamp aNow, uint32_t aSuspectedCCObjects) const {
370 if (mNeedsFullCC != CCReason::NO_REASON) {
371 return mNeedsFullCC;
372 }
373 if (aSuspectedCCObjects > kCCPurpleLimit) {
374 return CCReason::MANY_SUSPECTED;
375 }
376 if (aSuspectedCCObjects > kCCForcedPurpleLimit && mLastCCEndTime &&
377 aNow - mLastCCEndTime > kCCForced) {
378 return CCReason::TIMED;
379 }
380 return CCReason::NO_REASON;
381 }
382
383 mozilla::CCReason ShouldScheduleCC(TimeStamp aNow,
384 uint32_t aSuspectedCCObjects) const;
385
386 // If we collected a substantial amount of cycles, poke the GC since more
387 // objects might be unreachable now.
388 bool NeedsGCAfterCC() const {
389 return mCCollectedWaitingForGC > 250 || mCCollectedZonesWaitingForGC > 0 ||
390 mLikelyShortLivingObjectsNeedingGC > 2500 || mNeedsGCAfterCC;
391 }
392
393 bool IsLastEarlyCCTimer(int32_t aCurrentFireCount) const {
394 int32_t numEarlyTimerFires =
395 std::max(int32_t(mCCDelay / kCCSkippableDelay) - 2, 1);
396
397 return aCurrentFireCount >= numEarlyTimerFires;
398 }
399
400 enum class CCRunnerState {
401 Inactive,
402 ReducePurple,
403 CleanupChildless,
404 CleanupContentUnbinder,
405 CleanupDeferred,
406 StartCycleCollection,
407 CycleCollecting,
408 Canceled,
409 NumStates
410 };
411
412 void InitCCRunnerStateMachine(CCRunnerState initialState, CCReason aReason) {
413 if (mCCRunner) {
414 return;
415 }
416
417 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", "./../../../dom/base/CCGCScheduler.h"
, 417); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mCCReason == CCReason::NO_REASON"
")"); do { MOZ_CrashSequence(__null, 417); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
418 mCCReason = aReason;
419
420 // The state machine should always have been deactivated after the previous
421 // collection, however far that collection may have gone.
422 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" ")", "./../../../dom/base/CCGCScheduler.h"
, 423); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mCCRunnerState == CCRunnerState::Inactive"
") (" "DeactivateCCRunner should have been called" ")"); do {
MOZ_CrashSequence(__null, 423); __attribute__((nomerge)) ::abort
(); } while (false); } } while (false)
423 "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" ")", "./../../../dom/base/CCGCScheduler.h"
, 423); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mCCRunnerState == CCRunnerState::Inactive"
") (" "DeactivateCCRunner should have been called" ")"); do {
MOZ_CrashSequence(__null, 423); __attribute__((nomerge)) ::abort
(); } while (false); } } while (false)
;
424 mCCRunnerState = initialState;
425
426 // Currently, there are only two entry points to the non-Inactive part of
427 // the state machine.
428 if (initialState == CCRunnerState::ReducePurple) {
429 mCCDelay = kCCDelay;
430 mCCRunnerEarlyFireCount = 0;
431 } else if (initialState == CCRunnerState::CycleCollecting) {
432 // Nothing needed.
433 } else {
434 MOZ_CRASH("Invalid initial state")do { do { } while (false); MOZ_ReportCrash("" "Invalid initial state"
, "./../../../dom/base/CCGCScheduler.h", 434); AnnotateMozCrashReason
("MOZ_CRASH(" "Invalid initial state" ")"); do { MOZ_CrashSequence
(__null, 434); __attribute__((nomerge)) ::abort(); } while (false
); } while (false)
;
435 }
436 }
437
438 void DeactivateCCRunner() {
439 mCCRunnerState = CCRunnerState::Inactive;
440 mCCReason = CCReason::NO_REASON;
441 }
442
443 bool HasMoreIdleGCRunnerWork() const {
444 return mMajorGCReason != JS::GCReason::NO_REASON ||
445 mEagerMajorGCReason != JS::GCReason::NO_REASON ||
446 mEagerMinorGCReason != JS::GCReason::NO_REASON;
447 }
448
449 GCRunnerStep GetNextGCRunnerAction(TimeStamp aDeadline) const;
450
451 CCRunnerStep AdvanceCCRunner(TimeStamp aDeadline, TimeStamp aNow,
452 uint32_t aSuspectedCCObjects);
453
454 // aStartTimeStamp : when the ForgetSkippable timer fired. This may be some
455 // time ago, if an incremental GC needed to be finished.
456 JS::SliceBudget ComputeForgetSkippableBudget(TimeStamp aStartTimeStamp,
457 TimeStamp aDeadline);
458
459 bool PreferFasterCollection() const { return mPreferFasterCollection; }
460
461 private:
462 // State
463
464 // An incremental GC is in progress, which blocks the CC from running for its
465 // duration (or until it goes too long and is finished synchronously.)
466 bool mInIncrementalGC = false;
467
468 // Whether to ask the parent process if now is a good time to GC (false for
469 // the parent process.)
470 const bool mAskParentBeforeMajorGC;
471
472 // We've asked the parent process if now is a good time to GC (do not ask
473 // again).
474 bool mHaveAskedParent = false;
475
476 // The parent process is ready for us to do a major GC.
477 bool mReadyForMajorGC;
478
479 // Set when the IdleTaskRunner requests the current task be interrupted.
480 // Cleared when the GC slice budget has detected the interrupt request.
481 JS::SliceBudget::InterruptRequestFlag mInterruptRequested;
482
483 // When a shrinking GC has been requested but we back-out, if this is true
484 // we run a non-shrinking GC.
485 bool mWantAtLeastRegularGC = false;
486
487 // When the CC started actually waiting for the GC to finish. This will be
488 // set to non-null at a later time than mCCLockedOut.
489 TimeStamp mCCBlockStart;
490
491 bool mDidShutdown = false;
492
493 TimeStamp mLastForgetSkippableEndTime;
494 uint32_t mForgetSkippableCounter = 0;
495 TimeStamp mForgetSkippableFrequencyStartTime;
496 TimeStamp mLastCCEndTime;
497 TimeStamp mLastForgetSkippableCycleEndTime;
498
499 CCRunnerState mCCRunnerState = CCRunnerState::Inactive;
500 int32_t mCCRunnerEarlyFireCount = 0;
501 TimeDuration mCCDelay = kCCDelay;
502
503 // Prevent the very first CC from running before we have GC'd and set the
504 // gray bits.
505 bool mHasRunGC = false;
506
507 mozilla::CCReason mNeedsFullCC = CCReason::NO_REASON;
508 bool mNeedsFullGC = true;
509 bool mNeedsGCAfterCC = false;
510 uint32_t mPreviousSuspectedCount = 0;
511
512 uint32_t mCleanupsSinceLastGC = UINT32_MAX(4294967295U);
513
514 // If the GC runner triggers a GC slice, this will be set to the idle deadline
515 // or the null timestamp if non-idle. It will be Nothing at the end of an
516 // internally-triggered slice.
517 mozilla::Maybe<TimeStamp> mTriggeredGCDeadline;
518
519 RefPtr<IdleTaskRunner> mGCRunner;
520 RefPtr<IdleTaskRunner> mCCRunner;
521 nsITimer* mShrinkingGCTimer = nullptr;
522 nsITimer* mFullGCTimer = nullptr;
523
524 mozilla::CCReason mCCReason = mozilla::CCReason::NO_REASON;
525 JS::GCReason mMajorGCReason = JS::GCReason::NO_REASON;
526 JS::GCReason mEagerMajorGCReason = JS::GCReason::NO_REASON;
527 JS::GCReason mEagerMinorGCReason = JS::GCReason::NO_REASON;
528
529 bool mIsCompactingOnUserInactive = false;
530 bool mIsCollectingCycles = false;
531 bool mUserIsActive = true;
532
533 bool mCurrentCollectionHasSeenNonIdle = false;
534 bool mPreferFasterCollection = false;
535
536 public:
537 uint32_t mCCollectedWaitingForGC = 0;
538 uint32_t mCCollectedZonesWaitingForGC = 0;
539 uint32_t mLikelyShortLivingObjectsNeedingGC = 0;
540
541 // Configuration parameters
542
543 TimeDuration mActiveIntersliceGCBudget = TimeDuration::FromMilliseconds(5);
544};
545
546} // namespace mozilla
547
548#endif // mozilla_dom_CCGCScheduler_h