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