Bug Summary

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

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -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=/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dom/base -fcoverage-compilation-dir=/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dom/base -resource-dir /usr/lib/llvm-20/lib/clang/20 -include /var/lib/jenkins/workspace/firefox-scan-build/config/gcc_hidden.h -include /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/mozilla-config.h -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/stl_wrappers -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D _GLIBCXX_ASSERTIONS -D DEBUG=1 -D MOZ_HAS_MOZGLUE -D MOZILLA_INTERNAL_API -D IMPL_LIBXUL -D MOZ_SUPPORT_LEAKCHECKING -D STATIC_EXPORTABLE_JS_API -I /var/lib/jenkins/workspace/firefox-scan-build/dom/base -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dom/base -I /var/lib/jenkins/workspace/firefox-scan-build/dom/battery -I /var/lib/jenkins/workspace/firefox-scan-build/dom/events -I /var/lib/jenkins/workspace/firefox-scan-build/dom/media -I /var/lib/jenkins/workspace/firefox-scan-build/dom/network -I /var/lib/jenkins/workspace/firefox-scan-build/caps -I /var/lib/jenkins/workspace/firefox-scan-build/docshell/base -I /var/lib/jenkins/workspace/firefox-scan-build/dom/base -I /var/lib/jenkins/workspace/firefox-scan-build/dom/file -I /var/lib/jenkins/workspace/firefox-scan-build/dom/geolocation -I /var/lib/jenkins/workspace/firefox-scan-build/dom/html -I /var/lib/jenkins/workspace/firefox-scan-build/dom/ipc -I /var/lib/jenkins/workspace/firefox-scan-build/dom/storage -I /var/lib/jenkins/workspace/firefox-scan-build/dom/svg -I /var/lib/jenkins/workspace/firefox-scan-build/dom/xml -I /var/lib/jenkins/workspace/firefox-scan-build/dom/xslt/xpath -I /var/lib/jenkins/workspace/firefox-scan-build/dom/xul -I /var/lib/jenkins/workspace/firefox-scan-build/extensions/spellcheck/src -I /var/lib/jenkins/workspace/firefox-scan-build/gfx/2d -I /var/lib/jenkins/workspace/firefox-scan-build/image -I /var/lib/jenkins/workspace/firefox-scan-build/js/xpconnect/loader -I /var/lib/jenkins/workspace/firefox-scan-build/js/xpconnect/src -I /var/lib/jenkins/workspace/firefox-scan-build/js/xpconnect/wrappers -I /var/lib/jenkins/workspace/firefox-scan-build/layout/base -I /var/lib/jenkins/workspace/firefox-scan-build/layout/forms -I /var/lib/jenkins/workspace/firefox-scan-build/layout/generic -I /var/lib/jenkins/workspace/firefox-scan-build/layout/style -I /var/lib/jenkins/workspace/firefox-scan-build/layout/xul -I /var/lib/jenkins/workspace/firefox-scan-build/netwerk/base -I /var/lib/jenkins/workspace/firefox-scan-build/netwerk/protocol/http -I /var/lib/jenkins/workspace/firefox-scan-build/netwerk/url-classifier -I /var/lib/jenkins/workspace/firefox-scan-build/parser/htmlparser -I /var/lib/jenkins/workspace/firefox-scan-build/security/manager/ssl -I /var/lib/jenkins/workspace/firefox-scan-build/third_party/xsimd/include -I /var/lib/jenkins/workspace/firefox-scan-build/widget -I /var/lib/jenkins/workspace/firefox-scan-build/xpcom/build -I /var/lib/jenkins/workspace/firefox-scan-build/xpcom/ds -I /var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/ipc/ipdl/_ipdlheaders -I /var/lib/jenkins/workspace/firefox-scan-build/ipc/chromium/src -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -I /usr/include/gtk-3.0 -I /usr/include/pango-1.0 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/sysprof-6 -I /usr/include/harfbuzz -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/libmount -I /usr/include/blkid -I /usr/include/fribidi -I /usr/include/cairo -I /usr/include/pixman-1 -I /usr/include/gdk-pixbuf-2.0 -I /usr/include/x86_64-linux-gnu -I /usr/include/webp -I /usr/include/gio-unix-2.0 -I /usr/include/cloudproviders -I /usr/include/atk-1.0 -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/gtk-3.0/unix-print -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/x86_64-linux-gnu/c++/14 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/backward -internal-isystem /usr/lib/llvm-20/lib/clang/20/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -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-deprecated-this-capture -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 -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 -fskip-odr-check-in-gmf -fno-sized-deallocation -fno-aligned-allocation -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2025-01-20-090804-167946-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#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
17namespace mozilla {
18
19extern 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.
23extern const TimeDuration kCCDelay;
24
25extern 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.
30extern 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.
35extern const TimeDuration kForgetSkippableSliceDuration;
36
37// Maximum amount of time that should elapse between incremental CC slices
38extern const TimeDuration kICCIntersliceDelay;
39
40// Time budget for an incremental CC slice when using timer to run it.
41extern const TimeDuration kICCSliceBudget;
42// Minimum budget for an incremental CC slice when using idle time to run it.
43extern const TimeDuration kIdleICCSliceBudget;
44
45// Maximum total duration for an ICC
46extern 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.
50extern const TimeDuration kCCForced;
51constexpr uint32_t kCCForcedPurpleLimit = 10;
52
53// Don't allow an incremental GC to lock out the CC for too long.
54extern const TimeDuration kMaxCCLockedoutTime;
55
56// Trigger a CC if the purple buffer exceeds this size when we check it.
57constexpr uint32_t kCCPurpleLimit = 200;
58
59// Actions performed by the GCRunner state machine.
60enum 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
68struct GCRunnerStep {
69 GCRunnerAction mAction;
70 JS::GCReason mReason;
71};
72
73// Actions that are output from the CCRunner state machine.
74enum 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
94enum CCRunnerYield { Continue, Yield };
95
96enum CCRunnerForgetSkippableRemoveChildless {
97 KeepChildless = false,
98 RemoveChildless = true
99};
100
101struct 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
128class 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