File: | var/lib/jenkins/workspace/firefox-scan-build/js/src/gc/GCRuntime.h |
Warning: | line 268, column 7 Excessive padding in 'class js::gc::GCRuntime' (82 padding bytes, where 2 is optimal). Optimal fields order: rt, numActiveZoneIters, queuePos, lastLastDitchTime, systemZone, sharedAtomsZone_, delayedMarkingList, markLaterArenas, helperThreadRatio, maxHelperThreads, helperThreadCount, maxMarkingThreads, markingThreadCount, maxParallelThreads, dispatchedParallelTasks, createBudgetCallback, nextCellUniqueId_, verifyPreData, lastGCStartTime_, lastGCEndTime_, initialized, minorGCNumber, majorGCNumber, number, sliceNumber, reservedMarkingThreads, sweepGroups, currentSweepGroup, sweepActions, sweepZone, foregroundFinalizedZone, zonesCompacted, relocatedArenasToRelease, markingValidator, defaultTimeBudgetMS_, maybeMarkStackLimit, inPageLoadCount, lastAllocRateUpdateTime, collectorTimeSinceAllocRateUpdate, permanentAtoms, permanentWellKnownSymbols, emptyChunks_, availableChunks_, fullChunks_, backgroundSweepZones, zonesToMaybeCompact, gcCallback, gcDoCycleCollectionCallback, tenuredCallback, hostCleanupFinalizationRegistryCallback, grayRootTracer, stringStats, heapSize, queuedParallelTasks, weakCachesToSweep, markers, sweepingTracer, rootsHash, buffersToFreeAfterMinorGC, cellsToAssertNotGray, atomMarking, testMarkQueue, mainThreadContext, zones_, selectedForMarking, lock, storeBufferLock, delayedMarkingLock, maybeAtomsToSweep, sweepTask, freeTask, decommitTask, lifoBlocksToFree, lifoBlocksToFreeAfterFullMinorGC, lifoBlocksToFreeAfterNextMinorGC, finalizeCallbacks, updateWeakPointerZonesCallbacks, updateWeakPointerCompartmentCallbacks, nurseryCollectionCallbacks, blackRootTracers, allocTask, markTask, unmarkTask, tunables, storeBuffer_, foregroundFinalizedArenas, nursery_, stats_, schedulingState, numArenasFreeCommitted, majorGCTriggerReason, heapState_, minEmptyChunkCount_, maxEmptyChunkCount_, initialReason, incrementalState, initialState, sweepGroupIndex, sweepMarkResult, zealModeBits, zealFrequency, nextScheduled, zealSliceBudget, gcCallbackDepth, maybeGcOptions, delayedMarkingWorkAdded, fullGCRequested, incrementalGCEnabled, perZoneGCEnabled, cleanUpEverything, grayBitsValid, isIncremental, isFull, isCompacting, useParallelMarking, useZeal, lastMarkSlice, safeToYield, markOnBackgroundThreadDuringSweeping, useBackgroundThreads, haveDiscardedJITCodeThisSlice, hadShutdownGC, requestSliceAfterBackgroundTask, sweepAllocKind, abortSweepAfterCurrentGroup, foregroundFinalizedAllocKind, queueMarkColor, startedCompacting, compactingEnabled, nurseryEnabled, parallelMarkingEnabled, rootsRemoved, deterministicOnly, fullCompartmentChecks, alwaysPreserveCode, lowMemoryState, consider reordering the fields or adding explicit padding members |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- |
2 | * vim: set ts=8 sts=2 et sw=2 tw=80: |
3 | * This Source Code Form is subject to the terms of the Mozilla Public |
4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | |
7 | #ifndef gc_GCRuntime_h |
8 | #define gc_GCRuntime_h |
9 | |
10 | #include "mozilla/Atomics.h" |
11 | #include "mozilla/DoublyLinkedList.h" |
12 | #include "mozilla/EnumSet.h" |
13 | #include "mozilla/Maybe.h" |
14 | #include "mozilla/TimeStamp.h" |
15 | |
16 | #include "gc/ArenaList.h" |
17 | #include "gc/AtomMarking.h" |
18 | #include "gc/GCContext.h" |
19 | #include "gc/GCMarker.h" |
20 | #include "gc/GCParallelTask.h" |
21 | #include "gc/IteratorUtils.h" |
22 | #include "gc/Nursery.h" |
23 | #include "gc/Scheduling.h" |
24 | #include "gc/Statistics.h" |
25 | #include "gc/StoreBuffer.h" |
26 | #include "js/friend/PerformanceHint.h" |
27 | #include "js/GCAnnotations.h" |
28 | #include "js/UniquePtr.h" |
29 | #include "vm/AtomsTable.h" |
30 | |
31 | namespace js { |
32 | |
33 | class AutoLockGC; |
34 | class AutoLockGCBgAlloc; |
35 | class AutoLockHelperThreadState; |
36 | class FinalizationRegistryObject; |
37 | class FinalizationRecordObject; |
38 | class FinalizationQueueObject; |
39 | class GlobalObject; |
40 | class VerifyPreTracer; |
41 | class WeakRefObject; |
42 | |
43 | namespace gc { |
44 | |
45 | using BlackGrayEdgeVector = Vector<TenuredCell*, 0, SystemAllocPolicy>; |
46 | using ZoneVector = Vector<JS::Zone*, 4, SystemAllocPolicy>; |
47 | |
48 | class AutoCallGCCallbacks; |
49 | class AutoGCSession; |
50 | class AutoHeapSession; |
51 | class AutoTraceSession; |
52 | struct FinalizePhase; |
53 | class MarkingValidator; |
54 | struct MovingTracer; |
55 | class ParallelMarkTask; |
56 | enum class ShouldCheckThresholds; |
57 | class SweepGroupsIter; |
58 | |
59 | // Interface to a sweep action. |
60 | struct SweepAction { |
61 | // The arguments passed to each action. |
62 | struct Args { |
63 | GCRuntime* gc; |
64 | JS::GCContext* gcx; |
65 | JS::SliceBudget& budget; |
66 | }; |
67 | |
68 | virtual ~SweepAction() = default; |
69 | virtual IncrementalProgress run(Args& state) = 0; |
70 | virtual void assertFinished() const = 0; |
71 | virtual bool shouldSkip() { return false; } |
72 | }; |
73 | |
74 | class ChunkPool { |
75 | TenuredChunk* head_; |
76 | size_t count_; |
77 | |
78 | public: |
79 | ChunkPool() : head_(nullptr), count_(0) {} |
80 | ChunkPool(const ChunkPool& other) = delete; |
81 | ChunkPool(ChunkPool&& other) { *this = std::move(other); } |
82 | |
83 | ~ChunkPool() { |
84 | MOZ_ASSERT(!head_)do { static_assert( mozilla::detail::AssertionConditionType< decltype(!head_)>::isValid, "invalid assertion condition") ; if ((__builtin_expect(!!(!(!!(!head_))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!head_", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/gc/GCRuntime.h" , 84); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!head_" ")"); do { *((volatile int*)__null) = 84; __attribute__((nomerge)) ::abort(); } while (false); } } while (false); |
85 | MOZ_ASSERT(count_ == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(count_ == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(count_ == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("count_ == 0", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/gc/GCRuntime.h" , 85); AnnotateMozCrashReason("MOZ_ASSERT" "(" "count_ == 0" ")" ); do { *((volatile int*)__null) = 85; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
86 | } |
87 | |
88 | ChunkPool& operator=(const ChunkPool& other) = delete; |
89 | ChunkPool& operator=(ChunkPool&& other) { |
90 | head_ = other.head_; |
91 | other.head_ = nullptr; |
92 | count_ = other.count_; |
93 | other.count_ = 0; |
94 | return *this; |
95 | } |
96 | |
97 | bool empty() const { return !head_; } |
98 | size_t count() const { return count_; } |
99 | |
100 | TenuredChunk* head() { |
101 | MOZ_ASSERT(head_)do { static_assert( mozilla::detail::AssertionConditionType< decltype(head_)>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(!!(head_))), 0))) { do { } while ( false); MOZ_ReportAssertionFailure("head_", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/gc/GCRuntime.h" , 101); AnnotateMozCrashReason("MOZ_ASSERT" "(" "head_" ")"); do { *((volatile int*)__null) = 101; __attribute__((nomerge) ) ::abort(); } while (false); } } while (false); |
102 | return head_; |
103 | } |
104 | TenuredChunk* pop(); |
105 | void push(TenuredChunk* chunk); |
106 | TenuredChunk* remove(TenuredChunk* chunk); |
107 | |
108 | void sort(); |
109 | |
110 | private: |
111 | TenuredChunk* mergeSort(TenuredChunk* list, size_t count); |
112 | bool isSorted() const; |
113 | |
114 | #ifdef DEBUG1 |
115 | public: |
116 | bool contains(TenuredChunk* chunk) const; |
117 | bool verify() const; |
118 | void verifyChunks() const; |
119 | #endif |
120 | |
121 | public: |
122 | // Pool mutation does not invalidate an Iter unless the mutation |
123 | // is of the TenuredChunk currently being visited by the Iter. |
124 | class Iter { |
125 | public: |
126 | explicit Iter(ChunkPool& pool) : current_(pool.head_) {} |
127 | bool done() const { return !current_; } |
128 | void next(); |
129 | TenuredChunk* get() const { return current_; } |
130 | operator TenuredChunk*() const { return get(); } |
131 | TenuredChunk* operator->() const { return get(); } |
132 | |
133 | private: |
134 | TenuredChunk* current_; |
135 | }; |
136 | }; |
137 | |
138 | class BackgroundMarkTask : public GCParallelTask { |
139 | public: |
140 | explicit BackgroundMarkTask(GCRuntime* gc); |
141 | void setBudget(const JS::SliceBudget& budget) { this->budget = budget; } |
142 | void run(AutoLockHelperThreadState& lock) override; |
143 | |
144 | private: |
145 | JS::SliceBudget budget; |
146 | }; |
147 | |
148 | class BackgroundUnmarkTask : public GCParallelTask { |
149 | public: |
150 | explicit BackgroundUnmarkTask(GCRuntime* gc); |
151 | void initZones(); |
152 | void run(AutoLockHelperThreadState& lock) override; |
153 | |
154 | ZoneVector zones; |
155 | }; |
156 | |
157 | class BackgroundSweepTask : public GCParallelTask { |
158 | public: |
159 | explicit BackgroundSweepTask(GCRuntime* gc); |
160 | void run(AutoLockHelperThreadState& lock) override; |
161 | }; |
162 | |
163 | class BackgroundFreeTask : public GCParallelTask { |
164 | public: |
165 | explicit BackgroundFreeTask(GCRuntime* gc); |
166 | void run(AutoLockHelperThreadState& lock) override; |
167 | }; |
168 | |
169 | // Performs extra allocation off thread so that when memory is required on the |
170 | // main thread it will already be available and waiting. |
171 | class BackgroundAllocTask : public GCParallelTask { |
172 | // Guarded by the GC lock. |
173 | GCLockData<ChunkPool&> chunkPool_; |
174 | |
175 | const bool enabled_; |
176 | |
177 | public: |
178 | BackgroundAllocTask(GCRuntime* gc, ChunkPool& pool); |
179 | bool enabled() const { return enabled_; } |
180 | |
181 | void run(AutoLockHelperThreadState& lock) override; |
182 | }; |
183 | |
184 | // Search the provided chunks for free arenas and decommit them. |
185 | class BackgroundDecommitTask : public GCParallelTask { |
186 | public: |
187 | explicit BackgroundDecommitTask(GCRuntime* gc); |
188 | void run(AutoLockHelperThreadState& lock) override; |
189 | }; |
190 | |
191 | template <typename F> |
192 | struct Callback { |
193 | F op; |
194 | void* data; |
195 | |
196 | Callback() : op(nullptr), data(nullptr) {} |
197 | Callback(F op, void* data) : op(op), data(data) {} |
198 | }; |
199 | |
200 | template <typename F> |
201 | using CallbackVector = Vector<Callback<F>, 4, SystemAllocPolicy>; |
202 | |
203 | using RootedValueMap = |
204 | HashMap<Value*, const char*, DefaultHasher<Value*>, SystemAllocPolicy>; |
205 | |
206 | using AllocKinds = mozilla::EnumSet<AllocKind, uint64_t>; |
207 | |
208 | // A singly linked list of zones. |
209 | class ZoneList { |
210 | static Zone* const End; |
211 | |
212 | Zone* head; |
213 | Zone* tail; |
214 | |
215 | public: |
216 | ZoneList(); |
217 | ~ZoneList(); |
218 | |
219 | bool isEmpty() const; |
220 | Zone* front() const; |
221 | |
222 | void prepend(Zone* zone); |
223 | void append(Zone* zone); |
224 | void prependList(ZoneList&& other); |
225 | void appendList(ZoneList&& other); |
226 | Zone* removeFront(); |
227 | void clear(); |
228 | |
229 | private: |
230 | explicit ZoneList(Zone* singleZone); |
231 | void check() const; |
232 | |
233 | ZoneList(const ZoneList& other) = delete; |
234 | ZoneList& operator=(const ZoneList& other) = delete; |
235 | }; |
236 | |
237 | struct WeakCacheToSweep { |
238 | JS::detail::WeakCacheBase* cache; |
239 | JS::Zone* zone; |
240 | }; |
241 | |
242 | class WeakCacheSweepIterator { |
243 | using WeakCacheBase = JS::detail::WeakCacheBase; |
244 | |
245 | JS::Zone* sweepZone; |
246 | WeakCacheBase* sweepCache; |
247 | |
248 | public: |
249 | explicit WeakCacheSweepIterator(JS::Zone* sweepGroup); |
250 | |
251 | bool done() const; |
252 | WeakCacheToSweep get() const; |
253 | void next(); |
254 | |
255 | private: |
256 | void settle(); |
257 | }; |
258 | |
259 | struct SweepingTracer final : public GenericTracerImpl<SweepingTracer> { |
260 | explicit SweepingTracer(JSRuntime* rt); |
261 | |
262 | private: |
263 | template <typename T> |
264 | void onEdge(T** thingp, const char* name); |
265 | friend class GenericTracerImpl<SweepingTracer>; |
266 | }; |
267 | |
268 | class GCRuntime { |
Excessive padding in 'class js::gc::GCRuntime' (82 padding bytes, where 2 is optimal). Optimal fields order: rt, numActiveZoneIters, queuePos, lastLastDitchTime, systemZone, sharedAtomsZone_, delayedMarkingList, markLaterArenas, helperThreadRatio, maxHelperThreads, helperThreadCount, maxMarkingThreads, markingThreadCount, maxParallelThreads, dispatchedParallelTasks, createBudgetCallback, nextCellUniqueId_, verifyPreData, lastGCStartTime_, lastGCEndTime_, initialized, minorGCNumber, majorGCNumber, number, sliceNumber, reservedMarkingThreads, sweepGroups, currentSweepGroup, sweepActions, sweepZone, foregroundFinalizedZone, zonesCompacted, relocatedArenasToRelease, markingValidator, defaultTimeBudgetMS_, maybeMarkStackLimit, inPageLoadCount, lastAllocRateUpdateTime, collectorTimeSinceAllocRateUpdate, permanentAtoms, permanentWellKnownSymbols, emptyChunks_, availableChunks_, fullChunks_, backgroundSweepZones, zonesToMaybeCompact, gcCallback, gcDoCycleCollectionCallback, tenuredCallback, hostCleanupFinalizationRegistryCallback, grayRootTracer, stringStats, heapSize, queuedParallelTasks, weakCachesToSweep, markers, sweepingTracer, rootsHash, buffersToFreeAfterMinorGC, cellsToAssertNotGray, atomMarking, testMarkQueue, mainThreadContext, zones_, selectedForMarking, lock, storeBufferLock, delayedMarkingLock, maybeAtomsToSweep, sweepTask, freeTask, decommitTask, lifoBlocksToFree, lifoBlocksToFreeAfterFullMinorGC, lifoBlocksToFreeAfterNextMinorGC, finalizeCallbacks, updateWeakPointerZonesCallbacks, updateWeakPointerCompartmentCallbacks, nurseryCollectionCallbacks, blackRootTracers, allocTask, markTask, unmarkTask, tunables, storeBuffer_, foregroundFinalizedArenas, nursery_, stats_, schedulingState, numArenasFreeCommitted, majorGCTriggerReason, heapState_, minEmptyChunkCount_, maxEmptyChunkCount_, initialReason, incrementalState, initialState, sweepGroupIndex, sweepMarkResult, zealModeBits, zealFrequency, nextScheduled, zealSliceBudget, gcCallbackDepth, maybeGcOptions, delayedMarkingWorkAdded, fullGCRequested, incrementalGCEnabled, perZoneGCEnabled, cleanUpEverything, grayBitsValid, isIncremental, isFull, isCompacting, useParallelMarking, useZeal, lastMarkSlice, safeToYield, markOnBackgroundThreadDuringSweeping, useBackgroundThreads, haveDiscardedJITCodeThisSlice, hadShutdownGC, requestSliceAfterBackgroundTask, sweepAllocKind, abortSweepAfterCurrentGroup, foregroundFinalizedAllocKind, queueMarkColor, startedCompacting, compactingEnabled, nurseryEnabled, parallelMarkingEnabled, rootsRemoved, deterministicOnly, fullCompartmentChecks, alwaysPreserveCode, lowMemoryState, consider reordering the fields or adding explicit padding members | |
269 | public: |
270 | explicit GCRuntime(JSRuntime* rt); |
271 | [[nodiscard]] bool init(uint32_t maxbytes); |
272 | bool wasInitialized() const { return initialized; } |
273 | void finishRoots(); |
274 | void finish(); |
275 | |
276 | Zone* atomsZone() { |
277 | Zone* zone = zones()[0]; |
278 | MOZ_ASSERT(JS::shadow::Zone::from(zone)->isAtomsZone())do { static_assert( mozilla::detail::AssertionConditionType< decltype(JS::shadow::Zone::from(zone)->isAtomsZone())>:: isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(JS::shadow::Zone::from(zone)->isAtomsZone()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("JS::shadow::Zone::from(zone)->isAtomsZone()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/gc/GCRuntime.h" , 278); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JS::shadow::Zone::from(zone)->isAtomsZone()" ")"); do { *((volatile int*)__null) = 278; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
279 | return zone; |
280 | } |
281 | Zone* maybeSharedAtomsZone() { return sharedAtomsZone_; } |
282 | |
283 | [[nodiscard]] bool freezeSharedAtomsZone(); |
284 | void restoreSharedAtomsZone(); |
285 | |
286 | JS::HeapState heapState() const { return heapState_; } |
287 | |
288 | bool hasZealMode(ZealMode mode) const; |
289 | bool hasAnyZealModeOf(mozilla::EnumSet<ZealMode> mode) const; |
290 | void clearZealMode(ZealMode mode); |
291 | bool needZealousGC(); |
292 | bool zealModeControlsYieldPoint() const; |
293 | |
294 | [[nodiscard]] bool addRoot(Value* vp, const char* name); |
295 | void removeRoot(Value* vp); |
296 | |
297 | [[nodiscard]] bool setParameter(JSContext* cx, JSGCParamKey key, |
298 | uint32_t value); |
299 | void resetParameter(JSContext* cx, JSGCParamKey key); |
300 | uint32_t getParameter(JSGCParamKey key); |
301 | |
302 | void setPerformanceHint(PerformanceHint hint); |
303 | bool isInPageLoad() const { return inPageLoadCount != 0; } |
304 | |
305 | [[nodiscard]] bool triggerGC(JS::GCReason reason); |
306 | // Check whether to trigger a zone GC after allocating GC cells. |
307 | void maybeTriggerGCAfterAlloc(Zone* zone); |
308 | // Check whether to trigger a zone GC after malloc memory. |
309 | void maybeTriggerGCAfterMalloc(Zone* zone); |
310 | bool maybeTriggerGCAfterMalloc(Zone* zone, const HeapSize& heap, |
311 | const HeapThreshold& threshold, |
312 | JS::GCReason reason); |
313 | // The return value indicates if we were able to do the GC. |
314 | bool triggerZoneGC(Zone* zone, JS::GCReason reason, size_t usedBytes, |
315 | size_t thresholdBytes); |
316 | |
317 | void maybeGC(); |
318 | |
319 | // Return whether we want to run a major GC. If eagerOk is true, include eager |
320 | // triggers (eg EAGER_ALLOC_TRIGGER) in this determination, and schedule all |
321 | // zones that exceed the eager thresholds. |
322 | JS::GCReason wantMajorGC(bool eagerOk); |
323 | bool checkEagerAllocTrigger(const HeapSize& size, |
324 | const HeapThreshold& threshold); |
325 | |
326 | // Do a minor GC if requested, followed by a major GC if requested. The return |
327 | // value indicates whether a major GC was performed. |
328 | bool gcIfRequested() { return gcIfRequestedImpl(false); } |
329 | |
330 | // Internal function to do a GC if previously requested. But if not and |
331 | // eagerOk, do an eager GC for all Zones that have exceeded the eager |
332 | // thresholds. |
333 | // |
334 | // Return whether a major GC was performed or started. |
335 | bool gcIfRequestedImpl(bool eagerOk); |
336 | |
337 | void gc(JS::GCOptions options, JS::GCReason reason); |
338 | void startGC(JS::GCOptions options, JS::GCReason reason, |
339 | const JS::SliceBudget& budget); |
340 | void gcSlice(JS::GCReason reason, const JS::SliceBudget& budget); |
341 | void finishGC(JS::GCReason reason); |
342 | void abortGC(); |
343 | void startDebugGC(JS::GCOptions options, const JS::SliceBudget& budget); |
344 | void debugGCSlice(const JS::SliceBudget& budget); |
345 | |
346 | void runDebugGC(); |
347 | void notifyRootsRemoved(); |
348 | |
349 | enum TraceOrMarkRuntime { TraceRuntime, MarkRuntime }; |
350 | void traceRuntime(JSTracer* trc, AutoTraceSession& session); |
351 | void traceRuntimeForMinorGC(JSTracer* trc, AutoGCSession& session); |
352 | |
353 | void purgeRuntimeForMinorGC(); |
354 | |
355 | void shrinkBuffers(); |
356 | void onOutOfMallocMemory(); |
357 | void onOutOfMallocMemory(const AutoLockGC& lock); |
358 | |
359 | Nursery& nursery() { return nursery_.ref(); } |
360 | gc::StoreBuffer& storeBuffer() { return storeBuffer_.ref(); } |
361 | |
362 | void minorGC(JS::GCReason reason, |
363 | gcstats::PhaseKind phase = gcstats::PhaseKind::MINOR_GC) |
364 | JS_HAZ_GC_CALL; |
365 | void evictNursery(JS::GCReason reason = JS::GCReason::EVICT_NURSERY) { |
366 | minorGC(reason, gcstats::PhaseKind::EVICT_NURSERY); |
367 | } |
368 | |
369 | void* addressOfNurseryPosition() { |
370 | return nursery_.refNoCheck().addressOfPosition(); |
371 | } |
372 | |
373 | const void* addressOfLastBufferedWholeCell() { |
374 | return storeBuffer_.refNoCheck().addressOfLastBufferedWholeCell(); |
375 | } |
376 | |
377 | #ifdef JS_GC_ZEAL1 |
378 | const uint32_t* addressOfZealModeBits() { return &zealModeBits.refNoCheck(); } |
379 | void getZealBits(uint32_t* zealBits, uint32_t* frequency, |
380 | uint32_t* nextScheduled); |
381 | void setZeal(uint8_t zeal, uint32_t frequency); |
382 | void unsetZeal(uint8_t zeal); |
383 | bool parseAndSetZeal(const char* str); |
384 | void setNextScheduled(uint32_t count); |
385 | void verifyPreBarriers(); |
386 | void maybeVerifyPreBarriers(bool always); |
387 | bool selectForMarking(JSObject* object); |
388 | void clearSelectedForMarking(); |
389 | void setDeterministic(bool enable); |
390 | void setMarkStackLimit(size_t limit, AutoLockGC& lock); |
391 | #endif |
392 | |
393 | uint64_t nextCellUniqueId() { |
394 | MOZ_ASSERT(nextCellUniqueId_ > 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(nextCellUniqueId_ > 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(nextCellUniqueId_ > 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("nextCellUniqueId_ > 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/gc/GCRuntime.h" , 394); AnnotateMozCrashReason("MOZ_ASSERT" "(" "nextCellUniqueId_ > 0" ")"); do { *((volatile int*)__null) = 394; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
395 | uint64_t uid = ++nextCellUniqueId_; |
396 | return uid; |
397 | } |
398 | |
399 | void setLowMemoryState(bool newState) { lowMemoryState = newState; } |
400 | bool systemHasLowMemory() const { return lowMemoryState; } |
401 | |
402 | public: |
403 | // Internal public interface |
404 | ZoneVector& zones() { return zones_.ref(); } |
405 | gcstats::Statistics& stats() { return stats_.ref(); } |
406 | const gcstats::Statistics& stats() const { return stats_.ref(); } |
407 | State state() const { return incrementalState; } |
408 | bool isHeapCompacting() const { return state() == State::Compact; } |
409 | bool isForegroundSweeping() const { return state() == State::Sweep; } |
410 | bool isBackgroundSweeping() const { return sweepTask.wasStarted(); } |
411 | bool isBackgroundMarking() const { return markTask.wasStarted(); } |
412 | void waitBackgroundSweepEnd(); |
413 | void waitBackgroundAllocEnd() { allocTask.cancelAndWait(); } |
414 | void waitBackgroundFreeEnd(); |
415 | void waitForBackgroundTasks(); |
416 | bool isWaitingOnBackgroundTask() const; |
417 | |
418 | void lockGC() { lock.lock(); } |
419 | void unlockGC() { lock.unlock(); } |
420 | |
421 | void lockStoreBuffer() { storeBufferLock.lock(); } |
422 | void unlockStoreBuffer() { storeBufferLock.unlock(); } |
423 | |
424 | #ifdef DEBUG1 |
425 | void assertCurrentThreadHasLockedGC() const { |
426 | lock.assertOwnedByCurrentThread(); |
427 | } |
428 | void assertCurrentThreadHasLockedStoreBuffer() const { |
429 | storeBufferLock.assertOwnedByCurrentThread(); |
430 | } |
431 | #endif // DEBUG |
432 | |
433 | void setAlwaysPreserveCode() { alwaysPreserveCode = true; } |
434 | |
435 | void setIncrementalGCEnabled(bool enabled); |
436 | void setNurseryEnabled(bool enabled); |
437 | |
438 | bool isIncrementalGCEnabled() const { return incrementalGCEnabled; } |
439 | bool isPerZoneGCEnabled() const { return perZoneGCEnabled; } |
440 | bool isCompactingGCEnabled() const; |
441 | bool isParallelMarkingEnabled() const { return parallelMarkingEnabled; } |
442 | |
443 | bool isIncrementalGCInProgress() const { |
444 | return state() != State::NotActive && !isVerifyPreBarriersEnabled(); |
445 | } |
446 | |
447 | bool hasForegroundWork() const; |
448 | |
449 | bool isShrinkingGC() const { return gcOptions() == JS::GCOptions::Shrink; } |
450 | |
451 | bool isShutdownGC() const { return gcOptions() == JS::GCOptions::Shutdown; } |
452 | |
453 | #ifdef DEBUG1 |
454 | bool isShuttingDown() const { return hadShutdownGC; } |
455 | #endif |
456 | |
457 | bool initSweepActions(); |
458 | |
459 | void setGrayRootsTracer(JSGrayRootsTracer traceOp, void* data); |
460 | [[nodiscard]] bool addBlackRootsTracer(JSTraceDataOp traceOp, void* data); |
461 | void removeBlackRootsTracer(JSTraceDataOp traceOp, void* data); |
462 | void clearBlackAndGrayRootTracers(); |
463 | |
464 | void setGCCallback(JSGCCallback callback, void* data); |
465 | void callGCCallback(JSGCStatus status, JS::GCReason reason) const; |
466 | void setObjectsTenuredCallback(JSObjectsTenuredCallback callback, void* data); |
467 | void callObjectsTenuredCallback(); |
468 | [[nodiscard]] bool addFinalizeCallback(JSFinalizeCallback callback, |
469 | void* data); |
470 | void removeFinalizeCallback(JSFinalizeCallback callback); |
471 | void setHostCleanupFinalizationRegistryCallback( |
472 | JSHostCleanupFinalizationRegistryCallback callback, void* data); |
473 | void callHostCleanupFinalizationRegistryCallback( |
474 | JSFunction* doCleanup, GlobalObject* incumbentGlobal); |
475 | [[nodiscard]] bool addWeakPointerZonesCallback( |
476 | JSWeakPointerZonesCallback callback, void* data); |
477 | void removeWeakPointerZonesCallback(JSWeakPointerZonesCallback callback); |
478 | [[nodiscard]] bool addWeakPointerCompartmentCallback( |
479 | JSWeakPointerCompartmentCallback callback, void* data); |
480 | void removeWeakPointerCompartmentCallback( |
481 | JSWeakPointerCompartmentCallback callback); |
482 | JS::GCSliceCallback setSliceCallback(JS::GCSliceCallback callback); |
483 | bool addNurseryCollectionCallback(JS::GCNurseryCollectionCallback callback, |
484 | void* data); |
485 | void removeNurseryCollectionCallback(JS::GCNurseryCollectionCallback callback, |
486 | void* data); |
487 | JS::DoCycleCollectionCallback setDoCycleCollectionCallback( |
488 | JS::DoCycleCollectionCallback callback); |
489 | void callNurseryCollectionCallbacks(JS::GCNurseryProgress progress, |
490 | JS::GCReason reason); |
491 | |
492 | bool addFinalizationRegistry(JSContext* cx, |
493 | Handle<FinalizationRegistryObject*> registry); |
494 | bool registerWithFinalizationRegistry(JSContext* cx, HandleObject target, |
495 | HandleObject record); |
496 | void queueFinalizationRegistryForCleanup(FinalizationQueueObject* queue); |
497 | |
498 | void nukeFinalizationRecordWrapper(JSObject* wrapper, |
499 | FinalizationRecordObject* record); |
500 | void nukeWeakRefWrapper(JSObject* wrapper, WeakRefObject* weakRef); |
501 | |
502 | void setFullCompartmentChecks(bool enable); |
503 | |
504 | // Get the main marking tracer. |
505 | GCMarker& marker() { return *markers[0]; } |
506 | |
507 | JS::Zone* getCurrentSweepGroup() { return currentSweepGroup; } |
508 | unsigned getCurrentSweepGroupIndex() { |
509 | MOZ_ASSERT_IF(unsigned(state()) < unsigned(State::Sweep),do { if (unsigned(state()) < unsigned(State::Sweep)) { do { static_assert( mozilla::detail::AssertionConditionType<decltype (sweepGroupIndex == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(sweepGroupIndex == 0))), 0)) ) { do { } while (false); MOZ_ReportAssertionFailure("sweepGroupIndex == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/gc/GCRuntime.h" , 510); AnnotateMozCrashReason("MOZ_ASSERT" "(" "sweepGroupIndex == 0" ")"); do { *((volatile int*)__null) = 510; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); } } while ( false) |
510 | sweepGroupIndex == 0)do { if (unsigned(state()) < unsigned(State::Sweep)) { do { static_assert( mozilla::detail::AssertionConditionType<decltype (sweepGroupIndex == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(sweepGroupIndex == 0))), 0)) ) { do { } while (false); MOZ_ReportAssertionFailure("sweepGroupIndex == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/gc/GCRuntime.h" , 510); AnnotateMozCrashReason("MOZ_ASSERT" "(" "sweepGroupIndex == 0" ")"); do { *((volatile int*)__null) = 510; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); } } while ( false); |
511 | return sweepGroupIndex; |
512 | } |
513 | |
514 | uint64_t gcNumber() const { return number; } |
515 | void incGcNumber() { ++number; } |
516 | |
517 | uint64_t minorGCCount() const { return minorGCNumber; } |
518 | void incMinorGcNumber() { ++minorGCNumber; } |
519 | |
520 | uint64_t majorGCCount() const { return majorGCNumber; } |
521 | void incMajorGcNumber() { ++majorGCNumber; } |
522 | |
523 | uint64_t gcSliceCount() const { return sliceNumber; } |
524 | void incGcSliceNumber() { ++sliceNumber; } |
525 | |
526 | int64_t defaultSliceBudgetMS() const { return defaultTimeBudgetMS_; } |
527 | |
528 | bool isIncrementalGc() const { return isIncremental; } |
529 | bool isFullGc() const { return isFull; } |
530 | bool isCompactingGc() const { return isCompacting; } |
531 | bool didCompactZones() const { return isCompacting && zonesCompacted; } |
532 | |
533 | bool areGrayBitsValid() const { return grayBitsValid; } |
534 | void setGrayBitsInvalid() { grayBitsValid = false; } |
535 | |
536 | mozilla::TimeStamp lastGCStartTime() const { return lastGCStartTime_; } |
537 | mozilla::TimeStamp lastGCEndTime() const { return lastGCEndTime_; } |
538 | |
539 | bool majorGCRequested() const { |
540 | return majorGCTriggerReason != JS::GCReason::NO_REASON; |
541 | } |
542 | |
543 | double computeHeapGrowthFactor(size_t lastBytes); |
544 | size_t computeTriggerBytes(double growthFactor, size_t lastBytes); |
545 | |
546 | inline void updateOnFreeArenaAlloc(const TenuredChunkInfo& info); |
547 | void updateOnArenaFree() { ++numArenasFreeCommitted; } |
548 | |
549 | ChunkPool& fullChunks(const AutoLockGC& lock) { return fullChunks_.ref(); } |
550 | ChunkPool& availableChunks(const AutoLockGC& lock) { |
551 | return availableChunks_.ref(); |
552 | } |
553 | ChunkPool& emptyChunks(const AutoLockGC& lock) { return emptyChunks_.ref(); } |
554 | const ChunkPool& fullChunks(const AutoLockGC& lock) const { |
555 | return fullChunks_.ref(); |
556 | } |
557 | const ChunkPool& availableChunks(const AutoLockGC& lock) const { |
558 | return availableChunks_.ref(); |
559 | } |
560 | const ChunkPool& emptyChunks(const AutoLockGC& lock) const { |
561 | return emptyChunks_.ref(); |
562 | } |
563 | using NonEmptyChunksIter = ChainedIterator<ChunkPool::Iter, 2>; |
564 | NonEmptyChunksIter allNonEmptyChunks(const AutoLockGC& lock) { |
565 | return NonEmptyChunksIter(availableChunks(lock), fullChunks(lock)); |
566 | } |
567 | uint32_t minEmptyChunkCount(const AutoLockGC& lock) const { |
568 | return minEmptyChunkCount_; |
569 | } |
570 | uint32_t maxEmptyChunkCount(const AutoLockGC& lock) const { |
571 | return maxEmptyChunkCount_; |
572 | } |
573 | #ifdef DEBUG1 |
574 | void verifyAllChunks(); |
575 | #endif |
576 | |
577 | TenuredChunk* getOrAllocChunk(AutoLockGCBgAlloc& lock); |
578 | void recycleChunk(TenuredChunk* chunk, const AutoLockGC& lock); |
579 | |
580 | #ifdef JS_GC_ZEAL1 |
581 | void startVerifyPreBarriers(); |
582 | void endVerifyPreBarriers(); |
583 | void finishVerifier(); |
584 | bool isVerifyPreBarriersEnabled() const { return verifyPreData.refNoCheck(); } |
585 | bool shouldYieldForZeal(ZealMode mode); |
586 | #else |
587 | bool isVerifyPreBarriersEnabled() const { return false; } |
588 | #endif |
589 | |
590 | #ifdef JSGC_HASH_TABLE_CHECKS |
591 | void checkHashTablesAfterMovingGC(); |
592 | #endif |
593 | |
594 | // Crawl the heap to check whether an arbitary pointer is within a cell of |
595 | // the given kind. (TraceKind::Null means to ignore the kind.) |
596 | bool isPointerWithinTenuredCell( |
597 | void* ptr, JS::TraceKind traceKind = JS::TraceKind::Null); |
598 | |
599 | #ifdef DEBUG1 |
600 | bool hasZone(Zone* target); |
601 | #endif |
602 | |
603 | // Queue memory memory to be freed on a background thread if possible. |
604 | void queueUnusedLifoBlocksForFree(LifoAlloc* lifo); |
605 | void queueAllLifoBlocksForFreeAfterMinorGC(LifoAlloc* lifo); |
606 | void queueBuffersForFreeAfterMinorGC(Nursery::BufferSet& buffers); |
607 | |
608 | // Public here for ReleaseArenaLists and FinalizeTypedArenas. |
609 | void releaseArena(Arena* arena, const AutoLockGC& lock); |
610 | |
611 | // Allocator internals. |
612 | static void* refillFreeListInGC(Zone* zone, AllocKind thingKind); |
613 | |
614 | // Delayed marking. |
615 | void delayMarkingChildren(gc::Cell* cell, MarkColor color); |
616 | bool hasDelayedMarking() const; |
617 | void markAllDelayedChildren(ShouldReportMarkTime reportTime); |
618 | |
619 | // If we have yielded to the mutator while foreground finalizing arenas from |
620 | // zone |zone| with kind |kind| then return a list of the arenas finalized so |
621 | // far. These will have been removed from the main arena lists at this |
622 | // point. Otherwise return nullptr. |
623 | SortedArenaList* maybeGetForegroundFinalizedArenas(Zone* zone, |
624 | AllocKind kind); |
625 | |
626 | /* |
627 | * Concurrent sweep infrastructure. |
628 | */ |
629 | void startTask(GCParallelTask& task, AutoLockHelperThreadState& lock); |
630 | void joinTask(GCParallelTask& task, AutoLockHelperThreadState& lock); |
631 | void updateHelperThreadCount(); |
632 | size_t parallelWorkerCount() const; |
633 | |
634 | // GC parallel task dispatch infrastructure. |
635 | size_t getMaxParallelThreads() const; |
636 | void dispatchOrQueueParallelTask(GCParallelTask* task, |
637 | const AutoLockHelperThreadState& lock); |
638 | void maybeDispatchParallelTasks(const AutoLockHelperThreadState& lock); |
639 | void onParallelTaskEnd(bool wasDispatched, |
640 | const AutoLockHelperThreadState& lock); |
641 | |
642 | // Parallel marking. |
643 | bool setParallelMarkingEnabled(bool enabled); |
644 | bool initOrDisableParallelMarking(); |
645 | [[nodiscard]] bool updateMarkersVector(); |
646 | size_t markingWorkerCount() const; |
647 | |
648 | // WeakRefs |
649 | bool registerWeakRef(HandleObject target, HandleObject weakRef); |
650 | void traceKeptObjects(JSTracer* trc); |
651 | |
652 | JS::GCReason lastStartReason() const { return initialReason; } |
653 | |
654 | void updateAllocationRates(); |
655 | |
656 | // Allocator internals |
657 | static void* refillFreeList(JSContext* cx, AllocKind thingKind); |
658 | void attemptLastDitchGC(JSContext* cx); |
659 | |
660 | // Test mark queue. |
661 | #ifdef DEBUG1 |
662 | const GCVector<HeapPtr<JS::Value>, 0, SystemAllocPolicy>& getTestMarkQueue() |
663 | const; |
664 | [[nodiscard]] bool appendTestMarkQueue(const JS::Value& value); |
665 | void clearTestMarkQueue(); |
666 | size_t testMarkQueuePos() const; |
667 | #endif |
668 | |
669 | private: |
670 | enum IncrementalResult { ResetIncremental = 0, Ok }; |
671 | |
672 | [[nodiscard]] bool setParameter(JSGCParamKey key, uint32_t value, |
673 | AutoLockGC& lock); |
674 | void resetParameter(JSGCParamKey key, AutoLockGC& lock); |
675 | uint32_t getParameter(JSGCParamKey key, const AutoLockGC& lock); |
676 | bool setThreadParameter(JSGCParamKey key, uint32_t value, AutoLockGC& lock); |
677 | void resetThreadParameter(JSGCParamKey key, AutoLockGC& lock); |
678 | void updateThreadDataStructures(AutoLockGC& lock); |
679 | |
680 | JS::GCOptions gcOptions() const { return maybeGcOptions.ref().ref(); } |
681 | |
682 | TriggerResult checkHeapThreshold(Zone* zone, const HeapSize& heapSize, |
683 | const HeapThreshold& heapThreshold); |
684 | |
685 | void updateSchedulingStateOnGCStart(); |
686 | void updateSchedulingStateAfterCollection(mozilla::TimeStamp currentTime); |
687 | void updateAllGCStartThresholds(); |
688 | |
689 | // For ArenaLists::allocateFromArena() |
690 | friend class ArenaLists; |
691 | TenuredChunk* pickChunk(AutoLockGCBgAlloc& lock); |
692 | Arena* allocateArena(TenuredChunk* chunk, Zone* zone, AllocKind kind, |
693 | ShouldCheckThresholds checkThresholds, |
694 | const AutoLockGC& lock); |
695 | |
696 | /* |
697 | * Return the list of chunks that can be released outside the GC lock. |
698 | * Must be called either during the GC or with the GC lock taken. |
699 | */ |
700 | friend class BackgroundDecommitTask; |
701 | bool tooManyEmptyChunks(const AutoLockGC& lock); |
702 | ChunkPool expireEmptyChunkPool(const AutoLockGC& lock); |
703 | void freeEmptyChunks(const AutoLockGC& lock); |
704 | void prepareToFreeChunk(TenuredChunkInfo& info); |
705 | void setMinEmptyChunkCount(uint32_t value, const AutoLockGC& lock); |
706 | void setMaxEmptyChunkCount(uint32_t value, const AutoLockGC& lock); |
707 | |
708 | friend class BackgroundAllocTask; |
709 | bool wantBackgroundAllocation(const AutoLockGC& lock) const; |
710 | void startBackgroundAllocTaskIfIdle(); |
711 | |
712 | void requestMajorGC(JS::GCReason reason); |
713 | JS::SliceBudget defaultBudget(JS::GCReason reason, int64_t millis); |
714 | bool maybeIncreaseSliceBudget(JS::SliceBudget& budget); |
715 | bool maybeIncreaseSliceBudgetForLongCollections(JS::SliceBudget& budget); |
716 | bool maybeIncreaseSliceBudgetForUrgentCollections(JS::SliceBudget& budget); |
717 | IncrementalResult budgetIncrementalGC(bool nonincrementalByAPI, |
718 | JS::GCReason reason, |
719 | JS::SliceBudget& budget); |
720 | void checkZoneIsScheduled(Zone* zone, JS::GCReason reason, |
721 | const char* trigger); |
722 | IncrementalResult resetIncrementalGC(GCAbortReason reason); |
723 | |
724 | // Assert if the system state is such that we should never |
725 | // receive a request to do GC work. |
726 | void checkCanCallAPI(); |
727 | |
728 | // Check if the system state is such that GC has been supressed |
729 | // or otherwise delayed. |
730 | [[nodiscard]] bool checkIfGCAllowedInCurrentState(JS::GCReason reason); |
731 | |
732 | gcstats::ZoneGCStats scanZonesBeforeGC(); |
733 | |
734 | void setGCOptions(JS::GCOptions options); |
735 | |
736 | void collect(bool nonincrementalByAPI, const JS::SliceBudget& budget, |
737 | JS::GCReason reason) JS_HAZ_GC_CALL; |
738 | |
739 | /* |
740 | * Run one GC "cycle" (either a slice of incremental GC or an entire |
741 | * non-incremental GC). |
742 | * |
743 | * Returns: |
744 | * * ResetIncremental if we "reset" an existing incremental GC, which would |
745 | * force us to run another cycle or |
746 | * * Ok otherwise. |
747 | */ |
748 | [[nodiscard]] IncrementalResult gcCycle(bool nonincrementalByAPI, |
749 | const JS::SliceBudget& budgetArg, |
750 | JS::GCReason reason); |
751 | bool shouldRepeatForDeadZone(JS::GCReason reason); |
752 | |
753 | void incrementalSlice(JS::SliceBudget& budget, JS::GCReason reason, |
754 | bool budgetWasIncreased); |
755 | |
756 | bool mightSweepInThisSlice(bool nonIncremental); |
757 | void collectNurseryFromMajorGC(JS::GCReason reason); |
758 | void collectNursery(JS::GCOptions options, JS::GCReason reason, |
759 | gcstats::PhaseKind phase); |
760 | |
761 | friend class AutoCallGCCallbacks; |
762 | void maybeCallGCCallback(JSGCStatus status, JS::GCReason reason); |
763 | |
764 | void startCollection(JS::GCReason reason); |
765 | |
766 | void purgeRuntime(); |
767 | [[nodiscard]] bool beginPreparePhase(JS::GCReason reason, |
768 | AutoGCSession& session); |
769 | bool prepareZonesForCollection(JS::GCReason reason, bool* isFullOut); |
770 | void unmarkWeakMaps(); |
771 | void endPreparePhase(JS::GCReason reason); |
772 | void beginMarkPhase(AutoGCSession& session); |
773 | bool shouldPreserveJITCode(JS::Realm* realm, |
774 | const mozilla::TimeStamp& currentTime, |
775 | JS::GCReason reason, bool canAllocateMoreCode, |
776 | bool isActiveCompartment); |
777 | void discardJITCodeForGC(); |
778 | void startBackgroundFreeAfterMinorGC(); |
779 | void relazifyFunctionsForShrinkingGC(); |
780 | void purgePropMapTablesForShrinkingGC(); |
781 | void purgeSourceURLsForShrinkingGC(); |
782 | void traceRuntimeForMajorGC(JSTracer* trc, AutoGCSession& session); |
783 | void traceRuntimeAtoms(JSTracer* trc); |
784 | void traceRuntimeCommon(JSTracer* trc, TraceOrMarkRuntime traceOrMark); |
785 | void traceEmbeddingBlackRoots(JSTracer* trc); |
786 | void traceEmbeddingGrayRoots(JSTracer* trc); |
787 | IncrementalProgress traceEmbeddingGrayRoots(JSTracer* trc, |
788 | JS::SliceBudget& budget); |
789 | void checkNoRuntimeRoots(AutoGCSession& session); |
790 | void maybeDoCycleCollection(); |
791 | void findDeadCompartments(); |
792 | |
793 | friend class BackgroundMarkTask; |
794 | enum ParallelMarking : bool { |
795 | SingleThreadedMarking = false, |
796 | AllowParallelMarking = true |
797 | }; |
798 | IncrementalProgress markUntilBudgetExhausted( |
799 | JS::SliceBudget& sliceBudget, |
800 | ParallelMarking allowParallelMarking = SingleThreadedMarking, |
801 | ShouldReportMarkTime reportTime = ReportMarkTime); |
802 | bool canMarkInParallel() const; |
803 | bool initParallelMarking(); |
804 | void finishParallelMarkers(); |
805 | |
806 | bool reserveMarkingThreads(size_t count); |
807 | void releaseMarkingThreads(); |
808 | |
809 | bool hasMarkingWork(MarkColor color) const; |
810 | |
811 | void drainMarkStack(); |
812 | |
813 | #ifdef DEBUG1 |
814 | void assertNoMarkingWork() const; |
815 | #else |
816 | void assertNoMarkingWork() const {} |
817 | #endif |
818 | |
819 | void markDelayedChildren(gc::Arena* arena, MarkColor color); |
820 | void processDelayedMarkingList(gc::MarkColor color); |
821 | void rebuildDelayedMarkingList(); |
822 | void appendToDelayedMarkingList(gc::Arena** listTail, gc::Arena* arena); |
823 | void resetDelayedMarking(); |
824 | template <typename F> |
825 | void forEachDelayedMarkingArena(F&& f); |
826 | |
827 | template <class ZoneIterT> |
828 | IncrementalProgress markWeakReferences(JS::SliceBudget& budget); |
829 | IncrementalProgress markWeakReferencesInCurrentGroup(JS::SliceBudget& budget); |
830 | IncrementalProgress markGrayRoots(JS::SliceBudget& budget, |
831 | gcstats::PhaseKind phase); |
832 | void markBufferedGrayRoots(JS::Zone* zone); |
833 | IncrementalProgress markAllWeakReferences(); |
834 | void markAllGrayReferences(gcstats::PhaseKind phase); |
835 | |
836 | // The mark queue is a testing-only feature for controlling mark ordering and |
837 | // yield timing. |
838 | enum MarkQueueProgress { |
839 | QueueYielded, // End this incremental GC slice, if possible |
840 | QueueComplete, // Done with the queue |
841 | QueueSuspended // Continue the GC without ending the slice |
842 | }; |
843 | MarkQueueProgress processTestMarkQueue(); |
844 | |
845 | // GC Sweeping. Implemented in Sweeping.cpp. |
846 | void beginSweepPhase(JS::GCReason reason, AutoGCSession& session); |
847 | void dropStringWrappers(); |
848 | void groupZonesForSweeping(JS::GCReason reason); |
849 | [[nodiscard]] bool findSweepGroupEdges(); |
850 | [[nodiscard]] bool addEdgesForMarkQueue(); |
851 | void moveToNextSweepGroup(); |
852 | void resetGrayList(Compartment* comp); |
853 | IncrementalProgress beginMarkingSweepGroup(JS::GCContext* gcx, |
854 | JS::SliceBudget& budget); |
855 | IncrementalProgress markGrayRootsInCurrentGroup(JS::GCContext* gcx, |
856 | JS::SliceBudget& budget); |
857 | IncrementalProgress markGray(JS::GCContext* gcx, JS::SliceBudget& budget); |
858 | IncrementalProgress endMarkingSweepGroup(JS::GCContext* gcx, |
859 | JS::SliceBudget& budget); |
860 | void markIncomingGrayCrossCompartmentPointers(); |
861 | IncrementalProgress beginSweepingSweepGroup(JS::GCContext* gcx, |
862 | JS::SliceBudget& budget); |
863 | void initBackgroundSweep(Zone* zone, JS::GCContext* gcx, |
864 | const FinalizePhase& phase); |
865 | IncrementalProgress markDuringSweeping(JS::GCContext* gcx, |
866 | JS::SliceBudget& budget); |
867 | void updateAtomsBitmap(); |
868 | void sweepCCWrappers(); |
869 | void sweepRealmGlobals(); |
870 | void sweepEmbeddingWeakPointers(JS::GCContext* gcx); |
871 | void sweepMisc(); |
872 | void sweepCompressionTasks(); |
873 | void sweepWeakMaps(); |
874 | void sweepUniqueIds(); |
875 | void sweepObjectsWithWeakPointers(); |
876 | void sweepDebuggerOnMainThread(JS::GCContext* gcx); |
877 | void sweepJitDataOnMainThread(JS::GCContext* gcx); |
878 | void sweepFinalizationObserversOnMainThread(); |
879 | void traceWeakFinalizationObserverEdges(JSTracer* trc, Zone* zone); |
880 | void sweepWeakRefs(); |
881 | IncrementalProgress endSweepingSweepGroup(JS::GCContext* gcx, |
882 | JS::SliceBudget& budget); |
883 | IncrementalProgress performSweepActions(JS::SliceBudget& sliceBudget); |
884 | void startSweepingAtomsTable(); |
885 | IncrementalProgress sweepAtomsTable(JS::GCContext* gcx, |
886 | JS::SliceBudget& budget); |
887 | IncrementalProgress sweepWeakCaches(JS::GCContext* gcx, |
888 | JS::SliceBudget& budget); |
889 | IncrementalProgress finalizeAllocKind(JS::GCContext* gcx, |
890 | JS::SliceBudget& budget); |
891 | bool foregroundFinalize(JS::GCContext* gcx, Zone* zone, AllocKind thingKind, |
892 | JS::SliceBudget& sliceBudget, |
893 | SortedArenaList& sweepList); |
894 | IncrementalProgress sweepPropMapTree(JS::GCContext* gcx, |
895 | JS::SliceBudget& budget); |
896 | void endSweepPhase(bool destroyingRuntime); |
897 | void queueZonesAndStartBackgroundSweep(ZoneList&& zones); |
898 | void sweepFromBackgroundThread(AutoLockHelperThreadState& lock); |
899 | void startBackgroundFree(); |
900 | void freeFromBackgroundThread(AutoLockHelperThreadState& lock); |
901 | void sweepBackgroundThings(ZoneList& zones); |
902 | void backgroundFinalize(JS::GCContext* gcx, Zone* zone, AllocKind kind, |
903 | Arena** empty); |
904 | void prepareForSweepSlice(JS::GCReason reason); |
905 | void assertBackgroundSweepingFinished(); |
906 | #ifdef DEBUG1 |
907 | bool zoneInCurrentSweepGroup(Zone* zone) const; |
908 | #endif |
909 | |
910 | bool allCCVisibleZonesWereCollected(); |
911 | void sweepZones(JS::GCContext* gcx, bool destroyingRuntime); |
912 | bool shouldDecommit() const; |
913 | void startDecommit(); |
914 | void decommitEmptyChunks(const bool& cancel, AutoLockGC& lock); |
915 | void decommitFreeArenas(const bool& cancel, AutoLockGC& lock); |
916 | void decommitFreeArenasWithoutUnlocking(const AutoLockGC& lock); |
917 | |
918 | // Compacting GC. Implemented in Compacting.cpp. |
919 | bool shouldCompact(); |
920 | void beginCompactPhase(); |
921 | IncrementalProgress compactPhase(JS::GCReason reason, |
922 | JS::SliceBudget& sliceBudget, |
923 | AutoGCSession& session); |
924 | void endCompactPhase(); |
925 | void sweepZoneAfterCompacting(MovingTracer* trc, Zone* zone); |
926 | bool canRelocateZone(Zone* zone) const; |
927 | [[nodiscard]] bool relocateArenas(Zone* zone, JS::GCReason reason, |
928 | Arena*& relocatedListOut, |
929 | JS::SliceBudget& sliceBudget); |
930 | void updateCellPointers(Zone* zone, AllocKinds kinds); |
931 | void updateAllCellPointers(MovingTracer* trc, Zone* zone); |
932 | void updateZonePointersToRelocatedCells(Zone* zone); |
933 | void updateRuntimePointersToRelocatedCells(AutoGCSession& session); |
934 | void clearRelocatedArenas(Arena* arenaList, JS::GCReason reason); |
935 | void clearRelocatedArenasWithoutUnlocking(Arena* arenaList, |
936 | JS::GCReason reason, |
937 | const AutoLockGC& lock); |
938 | void releaseRelocatedArenas(Arena* arenaList); |
939 | void releaseRelocatedArenasWithoutUnlocking(Arena* arenaList, |
940 | const AutoLockGC& lock); |
941 | #ifdef DEBUG1 |
942 | void protectOrReleaseRelocatedArenas(Arena* arenaList, JS::GCReason reason); |
943 | void protectAndHoldArenas(Arena* arenaList); |
944 | void unprotectHeldRelocatedArenas(const AutoLockGC& lock); |
945 | void releaseHeldRelocatedArenas(); |
946 | void releaseHeldRelocatedArenasWithoutUnlocking(const AutoLockGC& lock); |
947 | #endif |
948 | |
949 | /* |
950 | * Whether to immediately trigger a slice after a background task |
951 | * finishes. This may not happen at a convenient time, so the consideration is |
952 | * whether the slice will run quickly or may take a long time. |
953 | */ |
954 | enum ShouldTriggerSliceWhenFinished : bool { |
955 | DontTriggerSliceWhenFinished = false, |
956 | TriggerSliceWhenFinished = true |
957 | }; |
958 | |
959 | IncrementalProgress waitForBackgroundTask( |
960 | GCParallelTask& task, const JS::SliceBudget& budget, |
961 | bool shouldPauseMutator, ShouldTriggerSliceWhenFinished triggerSlice); |
962 | |
963 | void maybeRequestGCAfterBackgroundTask(const AutoLockHelperThreadState& lock); |
964 | void cancelRequestedGCAfterBackgroundTask(); |
965 | void finishCollection(JS::GCReason reason); |
966 | void maybeStopPretenuring(); |
967 | void checkGCStateNotInUse(); |
968 | IncrementalProgress joinBackgroundMarkTask(); |
969 | |
970 | #ifdef JS_GC_ZEAL1 |
971 | void computeNonIncrementalMarkingForValidation(AutoGCSession& session); |
972 | void validateIncrementalMarking(); |
973 | void finishMarkingValidation(); |
974 | #endif |
975 | |
976 | #ifdef DEBUG1 |
977 | void checkForCompartmentMismatches(); |
978 | #endif |
979 | |
980 | void callFinalizeCallbacks(JS::GCContext* gcx, JSFinalizeStatus status) const; |
981 | void callWeakPointerZonesCallbacks(JSTracer* trc) const; |
982 | void callWeakPointerCompartmentCallbacks(JSTracer* trc, |
983 | JS::Compartment* comp) const; |
984 | void callDoCycleCollectionCallback(JSContext* cx); |
985 | |
986 | public: |
987 | JSRuntime* const rt; |
988 | |
989 | // Embedders can use this zone however they wish. |
990 | MainThreadData<JS::Zone*> systemZone; |
991 | |
992 | MainThreadData<JS::GCContext> mainThreadContext; |
993 | |
994 | private: |
995 | // For parent runtimes, a zone containing atoms that is shared by child |
996 | // runtimes. |
997 | MainThreadData<Zone*> sharedAtomsZone_; |
998 | |
999 | // All zones in the runtime. The first element is always the atoms zone. |
1000 | MainThreadOrGCTaskData<ZoneVector> zones_; |
1001 | |
1002 | // Any activity affecting the heap. |
1003 | MainThreadOrGCTaskData<JS::HeapState> heapState_; |
1004 | friend class AutoHeapSession; |
1005 | friend class JS::AutoEnterCycleCollection; |
1006 | |
1007 | UnprotectedData<gcstats::Statistics> stats_; |
1008 | |
1009 | public: |
1010 | js::StringStats stringStats; |
1011 | |
1012 | Vector<UniquePtr<GCMarker>, 1, SystemAllocPolicy> markers; |
1013 | |
1014 | // Delayed marking support in case we OOM pushing work onto the mark stack. |
1015 | MainThreadOrGCTaskData<js::gc::Arena*> delayedMarkingList; |
1016 | MainThreadOrGCTaskData<bool> delayedMarkingWorkAdded; |
1017 | #ifdef DEBUG1 |
1018 | /* Count of arenas that are currently in the stack. */ |
1019 | MainThreadOrGCTaskData<size_t> markLaterArenas; |
1020 | #endif |
1021 | |
1022 | SweepingTracer sweepingTracer; |
1023 | |
1024 | /* Track total GC heap size for this runtime. */ |
1025 | HeapSize heapSize; |
1026 | |
1027 | /* GC scheduling state and parameters. */ |
1028 | GCSchedulingTunables tunables; |
1029 | GCSchedulingState schedulingState; |
1030 | MainThreadData<bool> fullGCRequested; |
1031 | |
1032 | // Helper thread configuration. |
1033 | MainThreadData<double> helperThreadRatio; |
1034 | MainThreadData<size_t> maxHelperThreads; |
1035 | MainThreadOrGCTaskData<size_t> helperThreadCount; |
1036 | MainThreadData<size_t> maxMarkingThreads; |
1037 | MainThreadData<size_t> markingThreadCount; |
1038 | |
1039 | // Per-runtime helper thread task queue. Can be accessed from helper threads |
1040 | // in maybeDispatchParallelTasks(). |
1041 | HelperThreadLockData<size_t> maxParallelThreads; |
1042 | HelperThreadLockData<size_t> dispatchedParallelTasks; |
1043 | HelperThreadLockData<GCParallelTaskList> queuedParallelTasks; |
1044 | |
1045 | // State used for managing atom mark bitmaps in each zone. |
1046 | AtomMarkingRuntime atomMarking; |
1047 | |
1048 | /* |
1049 | * Pointer to a callback that, if set, will be used to create a |
1050 | * budget for internally-triggered GCs. |
1051 | */ |
1052 | MainThreadData<JS::CreateSliceBudgetCallback> createBudgetCallback; |
1053 | |
1054 | private: |
1055 | // Arenas used for permanent things created at startup and shared by child |
1056 | // runtimes. |
1057 | MainThreadData<ArenaList> permanentAtoms; |
1058 | MainThreadData<ArenaList> permanentWellKnownSymbols; |
1059 | |
1060 | // When chunks are empty, they reside in the emptyChunks pool and are |
1061 | // re-used as needed or eventually expired if not re-used. The emptyChunks |
1062 | // pool gets refilled from the background allocation task heuristically so |
1063 | // that empty chunks should always be available for immediate allocation |
1064 | // without syscalls. |
1065 | GCLockData<ChunkPool> emptyChunks_; |
1066 | |
1067 | // Chunks which have had some, but not all, of their arenas allocated live |
1068 | // in the available chunk lists. When all available arenas in a chunk have |
1069 | // been allocated, the chunk is removed from the available list and moved |
1070 | // to the fullChunks pool. During a GC, if all arenas are free, the chunk |
1071 | // is moved back to the emptyChunks pool and scheduled for eventual |
1072 | // release. |
1073 | GCLockData<ChunkPool> availableChunks_; |
1074 | |
1075 | // When all arenas in a chunk are used, it is moved to the fullChunks pool |
1076 | // so as to reduce the cost of operations on the available lists. |
1077 | GCLockData<ChunkPool> fullChunks_; |
1078 | |
1079 | /* |
1080 | * JSGC_MIN_EMPTY_CHUNK_COUNT |
1081 | * JSGC_MAX_EMPTY_CHUNK_COUNT |
1082 | * |
1083 | * Controls the number of empty chunks reserved for future allocation. |
1084 | * |
1085 | * They can be read off main thread by the background allocation task and the |
1086 | * background decommit task. |
1087 | */ |
1088 | GCLockData<uint32_t> minEmptyChunkCount_; |
1089 | GCLockData<uint32_t> maxEmptyChunkCount_; |
1090 | |
1091 | MainThreadData<RootedValueMap> rootsHash; |
1092 | |
1093 | // An incrementing id used to assign unique ids to cells that require one. |
1094 | MainThreadData<uint64_t> nextCellUniqueId_; |
1095 | |
1096 | /* |
1097 | * Number of the committed arenas in all GC chunks including empty chunks. |
1098 | */ |
1099 | mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> numArenasFreeCommitted; |
1100 | MainThreadData<VerifyPreTracer*> verifyPreData; |
1101 | |
1102 | MainThreadData<mozilla::TimeStamp> lastGCStartTime_; |
1103 | MainThreadData<mozilla::TimeStamp> lastGCEndTime_; |
1104 | |
1105 | WriteOnceData<bool> initialized; |
1106 | MainThreadData<bool> incrementalGCEnabled; |
1107 | MainThreadData<bool> perZoneGCEnabled; |
1108 | |
1109 | mozilla::Atomic<size_t, mozilla::ReleaseAcquire> numActiveZoneIters; |
1110 | |
1111 | /* During shutdown, the GC needs to clean up every possible object. */ |
1112 | MainThreadData<bool> cleanUpEverything; |
1113 | |
1114 | /* |
1115 | * The gray bits can become invalid if UnmarkGray overflows the stack. A |
1116 | * full GC will reset this bit, since it fills in all the gray bits. |
1117 | */ |
1118 | UnprotectedData<bool> grayBitsValid; |
1119 | |
1120 | mozilla::Atomic<JS::GCReason, mozilla::ReleaseAcquire> majorGCTriggerReason; |
1121 | |
1122 | /* Incremented at the start of every minor GC. */ |
1123 | MainThreadData<uint64_t> minorGCNumber; |
1124 | |
1125 | /* Incremented at the start of every major GC. */ |
1126 | MainThreadData<uint64_t> majorGCNumber; |
1127 | |
1128 | /* Incremented on every GC slice or minor collection. */ |
1129 | MainThreadData<uint64_t> number; |
1130 | |
1131 | /* Incremented on every GC slice. */ |
1132 | MainThreadData<uint64_t> sliceNumber; |
1133 | |
1134 | /* |
1135 | * This runtime's current contribution to the global number of helper threads |
1136 | * 'reserved' for parallel marking. Does not affect other uses of helper |
1137 | * threads. |
1138 | */ |
1139 | MainThreadData<size_t> reservedMarkingThreads; |
1140 | |
1141 | /* Whether the currently running GC can finish in multiple slices. */ |
1142 | MainThreadOrGCTaskData<bool> isIncremental; |
1143 | |
1144 | /* Whether all zones are being collected in first GC slice. */ |
1145 | MainThreadData<bool> isFull; |
1146 | |
1147 | /* Whether the heap will be compacted at the end of GC. */ |
1148 | MainThreadData<bool> isCompacting; |
1149 | |
1150 | /* Whether to use parallel marking. */ |
1151 | MainThreadData<ParallelMarking> useParallelMarking; |
1152 | |
1153 | /* The invocation kind of the current GC, set at the start of collection. */ |
1154 | MainThreadOrGCTaskData<mozilla::Maybe<JS::GCOptions>> maybeGcOptions; |
1155 | |
1156 | /* The initial GC reason, taken from the first slice. */ |
1157 | MainThreadData<JS::GCReason> initialReason; |
1158 | |
1159 | /* |
1160 | * The current incremental GC phase. This is also used internally in |
1161 | * non-incremental GC. |
1162 | */ |
1163 | MainThreadOrGCTaskData<State> incrementalState; |
1164 | |
1165 | /* The incremental state at the start of this slice. */ |
1166 | MainThreadOrGCTaskData<State> initialState; |
1167 | |
1168 | /* Whether to pay attention the zeal settings in this incremental slice. */ |
1169 | #ifdef JS_GC_ZEAL1 |
1170 | MainThreadData<bool> useZeal; |
1171 | #else |
1172 | const bool useZeal; |
1173 | #endif |
1174 | |
1175 | /* Indicates that the last incremental slice exhausted the mark stack. */ |
1176 | MainThreadData<bool> lastMarkSlice; |
1177 | |
1178 | // Whether it's currently safe to yield to the mutator in an incremental GC. |
1179 | MainThreadData<bool> safeToYield; |
1180 | |
1181 | // Whether to do any marking caused by barriers on a background thread during |
1182 | // incremental sweeping, while also sweeping zones which have finished |
1183 | // marking. |
1184 | MainThreadData<bool> markOnBackgroundThreadDuringSweeping; |
1185 | |
1186 | // Whether any sweeping and decommitting will run on a separate GC helper |
1187 | // thread. |
1188 | MainThreadData<bool> useBackgroundThreads; |
1189 | |
1190 | // Whether we have already discarded JIT code for all collected zones in this |
1191 | // slice. |
1192 | MainThreadData<bool> haveDiscardedJITCodeThisSlice; |
1193 | |
1194 | #ifdef DEBUG1 |
1195 | /* Shutdown has started. Further collections must be shutdown collections. */ |
1196 | MainThreadData<bool> hadShutdownGC; |
1197 | #endif |
1198 | |
1199 | /* Singly linked list of zones to be swept in the background. */ |
1200 | HelperThreadLockData<ZoneList> backgroundSweepZones; |
1201 | |
1202 | /* |
1203 | * Whether to trigger a GC slice after a background task is complete, so that |
1204 | * the collector can continue or finsish collecting. This is only used for the |
1205 | * tasks that run concurrently with the mutator, which are background |
1206 | * finalization and background decommit. |
1207 | */ |
1208 | HelperThreadLockData<bool> requestSliceAfterBackgroundTask; |
1209 | |
1210 | /* |
1211 | * Free LIFO blocks are transferred to these allocators before being freed on |
1212 | * a background thread. |
1213 | */ |
1214 | HelperThreadLockData<LifoAlloc> lifoBlocksToFree; |
1215 | MainThreadData<LifoAlloc> lifoBlocksToFreeAfterFullMinorGC; |
1216 | MainThreadData<LifoAlloc> lifoBlocksToFreeAfterNextMinorGC; |
1217 | HelperThreadLockData<Nursery::BufferSet> buffersToFreeAfterMinorGC; |
1218 | |
1219 | /* Index of current sweep group (for stats). */ |
1220 | MainThreadData<unsigned> sweepGroupIndex; |
1221 | |
1222 | /* |
1223 | * Incremental sweep state. |
1224 | */ |
1225 | MainThreadData<JS::Zone*> sweepGroups; |
1226 | MainThreadOrGCTaskData<JS::Zone*> currentSweepGroup; |
1227 | MainThreadData<UniquePtr<SweepAction>> sweepActions; |
1228 | MainThreadOrGCTaskData<JS::Zone*> sweepZone; |
1229 | MainThreadOrGCTaskData<AllocKind> sweepAllocKind; |
1230 | MainThreadData<mozilla::Maybe<AtomsTable::SweepIterator>> maybeAtomsToSweep; |
1231 | MainThreadOrGCTaskData<mozilla::Maybe<WeakCacheSweepIterator>> |
1232 | weakCachesToSweep; |
1233 | MainThreadData<bool> abortSweepAfterCurrentGroup; |
1234 | MainThreadOrGCTaskData<IncrementalProgress> sweepMarkResult; |
1235 | |
1236 | /* |
1237 | * During incremental foreground finalization, we may have a list of arenas of |
1238 | * the current AllocKind and Zone whose contents have been finalized but which |
1239 | * have not yet been merged back into the main arena lists. |
1240 | */ |
1241 | MainThreadOrGCTaskData<JS::Zone*> foregroundFinalizedZone; |
1242 | MainThreadOrGCTaskData<AllocKind> foregroundFinalizedAllocKind; |
1243 | MainThreadData<mozilla::Maybe<SortedArenaList>> foregroundFinalizedArenas; |
1244 | |
1245 | #ifdef DEBUG1 |
1246 | /* |
1247 | * List of objects to mark at the beginning of a GC for testing purposes. May |
1248 | * also contain string directives to change mark color or wait until different |
1249 | * phases of the GC. |
1250 | * |
1251 | * This is a WeakCache because not everything in this list is guaranteed to |
1252 | * end up marked (eg if you insert an object from an already-processed sweep |
1253 | * group in the middle of an incremental GC). Also, the mark queue is not |
1254 | * used during shutdown GCs. In either case, unmarked objects may need to be |
1255 | * discarded. |
1256 | */ |
1257 | JS::WeakCache<GCVector<HeapPtr<JS::Value>, 0, SystemAllocPolicy>> |
1258 | testMarkQueue; |
1259 | |
1260 | /* Position within the test mark queue. */ |
1261 | size_t queuePos = 0; |
1262 | |
1263 | /* The test marking queue might want to be marking a particular color. */ |
1264 | mozilla::Maybe<js::gc::MarkColor> queueMarkColor; |
1265 | |
1266 | // During gray marking, delay AssertCellIsNotGray checks by |
1267 | // recording the cell pointers here and checking after marking has |
1268 | // finished. |
1269 | MainThreadData<Vector<const Cell*, 0, SystemAllocPolicy>> |
1270 | cellsToAssertNotGray; |
1271 | friend void js::gc::detail::AssertCellIsNotGray(const Cell*); |
1272 | #endif |
1273 | |
1274 | friend class SweepGroupsIter; |
1275 | |
1276 | /* |
1277 | * Incremental compacting state. |
1278 | */ |
1279 | MainThreadData<bool> startedCompacting; |
1280 | MainThreadData<ZoneList> zonesToMaybeCompact; |
1281 | MainThreadData<size_t> zonesCompacted; |
1282 | #ifdef DEBUG1 |
1283 | GCLockData<Arena*> relocatedArenasToRelease; |
1284 | #endif |
1285 | |
1286 | #ifdef JS_GC_ZEAL1 |
1287 | MainThreadData<MarkingValidator*> markingValidator; |
1288 | #endif |
1289 | |
1290 | /* |
1291 | * Default budget for incremental GC slice. See js/SliceBudget.h. |
1292 | * |
1293 | * JSGC_SLICE_TIME_BUDGET_MS |
1294 | * pref: javascript.options.mem.gc_incremental_slice_ms, |
1295 | */ |
1296 | MainThreadData<int64_t> defaultTimeBudgetMS_; |
1297 | |
1298 | /* |
1299 | * Whether compacting GC is enabled globally. |
1300 | * |
1301 | * JSGC_COMPACTING_ENABLED |
1302 | * pref: javascript.options.mem.gc_compacting |
1303 | */ |
1304 | MainThreadData<bool> compactingEnabled; |
1305 | |
1306 | /* |
1307 | * Whether generational GC is enabled globally. |
1308 | * |
1309 | * JSGC_NURSERY_ENABLED |
1310 | * pref: javascript.options.mem.gc_generational |
1311 | */ |
1312 | MainThreadData<bool> nurseryEnabled; |
1313 | |
1314 | /* |
1315 | * Whether parallel marking is enabled globally. |
1316 | * |
1317 | * JSGC_PARALLEL_MARKING_ENABLED |
1318 | * pref: javascript.options.mem.gc_parallel_marking |
1319 | */ |
1320 | MainThreadData<bool> parallelMarkingEnabled; |
1321 | |
1322 | MainThreadData<bool> rootsRemoved; |
1323 | |
1324 | /* |
1325 | * These options control the zealousness of the GC. At every allocation, |
1326 | * nextScheduled is decremented. When it reaches zero we do a full GC. |
1327 | * |
1328 | * At this point, if zeal_ is one of the types that trigger periodic |
1329 | * collection, then nextScheduled is reset to the value of zealFrequency. |
1330 | * Otherwise, no additional GCs take place. |
1331 | * |
1332 | * You can control these values in several ways: |
1333 | * - Set the JS_GC_ZEAL environment variable |
1334 | * - Call gczeal() or schedulegc() from inside shell-executed JS code |
1335 | * (see the help for details) |
1336 | * |
1337 | * If gcZeal_ == 1 then we perform GCs in select places (during MaybeGC and |
1338 | * whenever we are notified that GC roots have been removed). This option is |
1339 | * mainly useful to embedders. |
1340 | * |
1341 | * We use zeal_ == 4 to enable write barrier verification. See the comment |
1342 | * in gc/Verifier.cpp for more information about this. |
1343 | * |
1344 | * zeal_ values from 8 to 10 periodically run different types of |
1345 | * incremental GC. |
1346 | * |
1347 | * zeal_ value 14 performs periodic shrinking collections. |
1348 | */ |
1349 | #ifdef JS_GC_ZEAL1 |
1350 | static_assert(size_t(ZealMode::Count) <= 32, |
1351 | "Too many zeal modes to store in a uint32_t"); |
1352 | MainThreadData<uint32_t> zealModeBits; |
1353 | MainThreadData<int> zealFrequency; |
1354 | MainThreadData<int> nextScheduled; |
1355 | MainThreadData<bool> deterministicOnly; |
1356 | MainThreadData<int> zealSliceBudget; |
1357 | MainThreadData<size_t> maybeMarkStackLimit; |
1358 | |
1359 | MainThreadData<PersistentRooted<GCVector<JSObject*, 0, SystemAllocPolicy>>> |
1360 | selectedForMarking; |
1361 | #endif |
1362 | |
1363 | MainThreadData<bool> fullCompartmentChecks; |
1364 | |
1365 | MainThreadData<uint32_t> gcCallbackDepth; |
1366 | |
1367 | MainThreadData<Callback<JSGCCallback>> gcCallback; |
1368 | MainThreadData<Callback<JS::DoCycleCollectionCallback>> |
1369 | gcDoCycleCollectionCallback; |
1370 | MainThreadData<Callback<JSObjectsTenuredCallback>> tenuredCallback; |
1371 | MainThreadData<CallbackVector<JSFinalizeCallback>> finalizeCallbacks; |
1372 | MainThreadOrGCTaskData<Callback<JSHostCleanupFinalizationRegistryCallback>> |
1373 | hostCleanupFinalizationRegistryCallback; |
1374 | MainThreadData<CallbackVector<JSWeakPointerZonesCallback>> |
1375 | updateWeakPointerZonesCallbacks; |
1376 | MainThreadData<CallbackVector<JSWeakPointerCompartmentCallback>> |
1377 | updateWeakPointerCompartmentCallbacks; |
1378 | MainThreadData<CallbackVector<JS::GCNurseryCollectionCallback>> |
1379 | nurseryCollectionCallbacks; |
1380 | |
1381 | /* |
1382 | * The trace operations to trace embedding-specific GC roots. One is for |
1383 | * tracing through black roots and the other is for tracing through gray |
1384 | * roots. The black/gray distinction is only relevant to the cycle |
1385 | * collector. |
1386 | */ |
1387 | MainThreadData<CallbackVector<JSTraceDataOp>> blackRootTracers; |
1388 | MainThreadOrGCTaskData<Callback<JSGrayRootsTracer>> grayRootTracer; |
1389 | |
1390 | /* Always preserve JIT code during GCs, for testing. */ |
1391 | MainThreadData<bool> alwaysPreserveCode; |
1392 | |
1393 | /* Count of the number of zones that are currently in page load. */ |
1394 | MainThreadData<size_t> inPageLoadCount; |
1395 | |
1396 | MainThreadData<bool> lowMemoryState; |
1397 | |
1398 | /* |
1399 | * General purpose GC lock, used for synchronising operations on |
1400 | * arenas and during parallel marking. |
1401 | */ |
1402 | friend class js::AutoLockGC; |
1403 | friend class js::AutoLockGCBgAlloc; |
1404 | Mutex lock MOZ_UNANNOTATED; |
1405 | |
1406 | /* |
1407 | * Lock used to synchronise access to the store buffer during parallel |
1408 | * sweeping. |
1409 | */ |
1410 | Mutex storeBufferLock MOZ_UNANNOTATED; |
1411 | |
1412 | /* Lock used to synchronise access to delayed marking state. */ |
1413 | Mutex delayedMarkingLock MOZ_UNANNOTATED; |
1414 | |
1415 | friend class BackgroundSweepTask; |
1416 | friend class BackgroundFreeTask; |
1417 | |
1418 | BackgroundAllocTask allocTask; |
1419 | BackgroundUnmarkTask unmarkTask; |
1420 | BackgroundMarkTask markTask; |
1421 | BackgroundSweepTask sweepTask; |
1422 | BackgroundFreeTask freeTask; |
1423 | BackgroundDecommitTask decommitTask; |
1424 | |
1425 | MainThreadData<Nursery> nursery_; |
1426 | |
1427 | // The store buffer used to track tenured to nursery edges for generational |
1428 | // GC. This is accessed off main thread when sweeping WeakCaches. |
1429 | MainThreadOrGCTaskData<gc::StoreBuffer> storeBuffer_; |
1430 | |
1431 | mozilla::TimeStamp lastLastDitchTime; |
1432 | |
1433 | // The last time per-zone allocation rates were updated. |
1434 | MainThreadData<mozilla::TimeStamp> lastAllocRateUpdateTime; |
1435 | |
1436 | // Total collector time since per-zone allocation rates were last updated. |
1437 | MainThreadData<mozilla::TimeDuration> collectorTimeSinceAllocRateUpdate; |
1438 | |
1439 | friend class MarkingValidator; |
1440 | friend class AutoEnterIteration; |
1441 | }; |
1442 | |
1443 | #ifndef JS_GC_ZEAL1 |
1444 | inline bool GCRuntime::hasZealMode(ZealMode mode) const { return false; } |
1445 | inline void GCRuntime::clearZealMode(ZealMode mode) {} |
1446 | inline bool GCRuntime::needZealousGC() { return false; } |
1447 | inline bool GCRuntime::zealModeControlsYieldPoint() const { return false; } |
1448 | #endif |
1449 | |
1450 | /* Prevent compartments and zones from being collected during iteration. */ |
1451 | class MOZ_RAII AutoEnterIteration { |
1452 | GCRuntime* gc; |
1453 | |
1454 | public: |
1455 | explicit AutoEnterIteration(GCRuntime* gc_) : gc(gc_) { |
1456 | ++gc->numActiveZoneIters; |
1457 | } |
1458 | |
1459 | ~AutoEnterIteration() { |
1460 | MOZ_ASSERT(gc->numActiveZoneIters)do { static_assert( mozilla::detail::AssertionConditionType< decltype(gc->numActiveZoneIters)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(gc->numActiveZoneIters))) , 0))) { do { } while (false); MOZ_ReportAssertionFailure("gc->numActiveZoneIters" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/gc/GCRuntime.h" , 1460); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gc->numActiveZoneIters" ")"); do { *((volatile int*)__null) = 1460; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
1461 | --gc->numActiveZoneIters; |
1462 | } |
1463 | }; |
1464 | |
1465 | bool IsCurrentlyAnimating(const mozilla::TimeStamp& lastAnimationTime, |
1466 | const mozilla::TimeStamp& currentTime); |
1467 | |
1468 | } /* namespace gc */ |
1469 | } /* namespace js */ |
1470 | |
1471 | #endif |