no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / js / src / gc / ZoneAllocator.h
blobde2dd7da280f67061fb1bded45a15cbb10d89b45
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/. */
7 /*
8 * Public header for allocating memory associated with GC things.
9 */
11 #ifndef gc_ZoneAllocator_h
12 #define gc_ZoneAllocator_h
14 #include "mozilla/Maybe.h"
16 #include "jsfriendapi.h"
17 #include "jstypes.h"
18 #include "gc/Cell.h"
19 #include "gc/Scheduling.h"
20 #include "js/GCAPI.h"
21 #include "js/shadow/Zone.h" // JS::shadow::Zone
22 #include "vm/MallocProvider.h"
24 namespace JS {
25 class JS_PUBLIC_API Zone;
26 } // namespace JS
28 namespace js {
30 class ZoneAllocator;
32 #ifdef DEBUG
33 bool CurrentThreadIsGCFinalizing();
34 #endif
36 namespace gc {
37 void MaybeMallocTriggerZoneGC(JSRuntime* rt, ZoneAllocator* zoneAlloc,
38 const HeapSize& heap,
39 const HeapThreshold& threshold,
40 JS::GCReason reason);
43 // Base class of JS::Zone that provides malloc memory allocation and accounting.
44 class ZoneAllocator : public JS::shadow::Zone,
45 public js::MallocProvider<JS::Zone> {
46 protected:
47 explicit ZoneAllocator(JSRuntime* rt, Kind kind);
48 ~ZoneAllocator();
49 void fixupAfterMovingGC();
51 public:
52 static ZoneAllocator* from(JS::Zone* zone) {
53 // This is a safe upcast, but the compiler hasn't seen the definition yet.
54 return reinterpret_cast<ZoneAllocator*>(zone);
57 [[nodiscard]] void* onOutOfMemory(js::AllocFunction allocFunc,
58 arena_id_t arena, size_t nbytes,
59 void* reallocPtr = nullptr);
60 void reportAllocationOverflow() const;
62 void updateSchedulingStateOnGCStart();
63 void updateGCStartThresholds(gc::GCRuntime& gc);
64 void setGCSliceThresholds(gc::GCRuntime& gc, bool waitingOnBGTask);
65 void clearGCSliceThresholds();
67 // Memory accounting APIs for malloc memory owned by GC cells.
69 void addCellMemory(js::gc::Cell* cell, size_t nbytes, js::MemoryUse use) {
70 MOZ_ASSERT(cell);
71 MOZ_ASSERT(nbytes);
73 mallocHeapSize.addBytes(nbytes);
75 #ifdef DEBUG
76 mallocTracker.trackGCMemory(cell, nbytes, use);
77 #endif
79 maybeTriggerGCOnMalloc();
82 void removeCellMemory(js::gc::Cell* cell, size_t nbytes, js::MemoryUse use,
83 bool updateRetainedSize = false) {
84 MOZ_ASSERT(cell);
85 MOZ_ASSERT(nbytes);
86 MOZ_ASSERT_IF(CurrentThreadIsGCFinalizing(), updateRetainedSize);
88 mallocHeapSize.removeBytes(nbytes, updateRetainedSize);
90 #ifdef DEBUG
91 mallocTracker.untrackGCMemory(cell, nbytes, use);
92 #endif
95 void swapCellMemory(js::gc::Cell* a, js::gc::Cell* b, js::MemoryUse use) {
96 #ifdef DEBUG
97 mallocTracker.swapGCMemory(a, b, use);
98 #endif
101 void registerNonGCMemory(void* mem, MemoryUse use) {
102 #ifdef DEBUG
103 return mallocTracker.registerNonGCMemory(mem, use);
104 #endif
106 void unregisterNonGCMemory(void* mem, MemoryUse use) {
107 #ifdef DEBUG
108 return mallocTracker.unregisterNonGCMemory(mem, use);
109 #endif
111 void moveOtherMemory(void* dst, void* src, MemoryUse use) {
112 #ifdef DEBUG
113 return mallocTracker.moveNonGCMemory(dst, src, use);
114 #endif
117 void incNonGCMemory(void* mem, size_t nbytes, MemoryUse use) {
118 MOZ_ASSERT(nbytes);
119 mallocHeapSize.addBytes(nbytes);
121 #ifdef DEBUG
122 mallocTracker.incNonGCMemory(mem, nbytes, use);
123 #endif
125 maybeTriggerGCOnMalloc();
127 void decNonGCMemory(void* mem, size_t nbytes, MemoryUse use,
128 bool updateRetainedSize) {
129 MOZ_ASSERT(nbytes);
131 mallocHeapSize.removeBytes(nbytes, updateRetainedSize);
133 #ifdef DEBUG
134 mallocTracker.decNonGCMemory(mem, nbytes, use);
135 #endif
138 // Account for allocations that may be referenced by more than one GC thing.
139 bool addSharedMemory(void* mem, size_t nbytes, MemoryUse use);
140 void removeSharedMemory(void* mem, size_t nbytes, MemoryUse use);
142 void incJitMemory(size_t nbytes) {
143 MOZ_ASSERT(nbytes);
144 jitHeapSize.addBytes(nbytes);
145 maybeTriggerZoneGC(jitHeapSize, jitHeapThreshold,
146 JS::GCReason::TOO_MUCH_JIT_CODE);
148 void decJitMemory(size_t nbytes) {
149 MOZ_ASSERT(nbytes);
150 jitHeapSize.removeBytes(nbytes, true);
153 // Check malloc allocation threshold and trigger a zone GC if necessary.
154 void maybeTriggerGCOnMalloc() {
155 maybeTriggerZoneGC(mallocHeapSize, mallocHeapThreshold,
156 JS::GCReason::TOO_MUCH_MALLOC);
159 private:
160 void maybeTriggerZoneGC(const js::gc::HeapSize& heap,
161 const js::gc::HeapThreshold& threshold,
162 JS::GCReason reason) {
163 if (heap.bytes() >= threshold.startBytes()) {
164 gc::MaybeMallocTriggerZoneGC(runtimeFromAnyThread(), this, heap,
165 threshold, reason);
169 void updateCollectionRate(mozilla::TimeDuration mainThreadGCTime,
170 size_t initialBytesForAllZones);
172 void updateAllocationRate(mozilla::TimeDuration mutatorTime);
174 public:
175 // The size of allocated GC arenas in this zone.
176 gc::PerZoneGCHeapSize gcHeapSize;
178 // Threshold used to trigger GC based on GC heap size.
179 gc::GCHeapThreshold gcHeapThreshold;
181 // Amount of malloc data owned by tenured GC things in this zone, including
182 // external allocations supplied by JS::AddAssociatedMemory.
183 gc::HeapSize mallocHeapSize;
185 // Threshold used to trigger GC based on malloc allocations.
186 gc::MallocHeapThreshold mallocHeapThreshold;
188 // Amount of exectuable JIT code owned by GC things in this zone.
189 gc::HeapSize jitHeapSize;
191 // Threshold used to trigger GC based on JIT allocations.
192 gc::JitHeapThreshold jitHeapThreshold;
194 // Use counts for memory that can be referenced by more than one GC thing.
195 // Memory recorded here is also recorded in mallocHeapSize. This structure
196 // is used to avoid over-counting in mallocHeapSize.
197 gc::SharedMemoryMap sharedMemoryUseCounts;
199 // Collection rate estimate for this zone in MB/s, and state used to calculate
200 // it. Updated every time this zone is collected.
201 MainThreadData<mozilla::Maybe<double>> smoothedCollectionRate;
202 MainThreadOrGCTaskData<mozilla::TimeDuration> perZoneGCTime;
204 // Allocation rate estimate in MB/s of mutator time, and state used to
205 // calculate it.
206 MainThreadData<mozilla::Maybe<double>> smoothedAllocationRate;
207 MainThreadData<size_t> prevGCHeapSize;
209 private:
210 #ifdef DEBUG
211 // In debug builds, malloc allocations can be tracked to make debugging easier
212 // (possible?) if allocation and free sizes don't balance.
213 gc::MemoryTracker mallocTracker;
214 #endif
216 friend class gc::GCRuntime;
219 // Whether memory is associated with a single cell or whether it is associated
220 // with the zone as a whole (for memory used by the system).
221 enum class TrackingKind { Cell, Zone };
224 * Allocation policy that performs memory tracking for malloced memory. This
225 * should be used for all containers associated with a GC thing or a zone.
227 * Since it doesn't hold a JSContext (those may not live long enough), it can't
228 * report out-of-memory conditions itself; the caller must check for OOM and
229 * take the appropriate action.
231 * FIXME bug 647103 - replace these *AllocPolicy names.
233 template <TrackingKind kind>
234 class TrackedAllocPolicy : public MallocProvider<TrackedAllocPolicy<kind>> {
235 ZoneAllocator* zone_;
237 #ifdef DEBUG
238 friend class js::gc::MemoryTracker; // Can clear |zone_| on merge.
239 #endif
241 public:
242 MOZ_IMPLICIT TrackedAllocPolicy(ZoneAllocator* z) : zone_(z) {
243 zone()->registerNonGCMemory(this, MemoryUse::TrackedAllocPolicy);
245 MOZ_IMPLICIT TrackedAllocPolicy(JS::Zone* z)
246 : TrackedAllocPolicy(ZoneAllocator::from(z)) {}
247 TrackedAllocPolicy(TrackedAllocPolicy& other)
248 : TrackedAllocPolicy(other.zone_) {}
249 TrackedAllocPolicy(TrackedAllocPolicy&& other) : zone_(other.zone_) {
250 zone()->moveOtherMemory(this, &other, MemoryUse::TrackedAllocPolicy);
251 other.zone_ = nullptr;
253 ~TrackedAllocPolicy() {
254 if (zone_) {
255 zone_->unregisterNonGCMemory(this, MemoryUse::TrackedAllocPolicy);
259 TrackedAllocPolicy& operator=(const TrackedAllocPolicy& other) {
260 zone()->unregisterNonGCMemory(this, MemoryUse::TrackedAllocPolicy);
261 zone_ = other.zone();
262 zone()->registerNonGCMemory(this, MemoryUse::TrackedAllocPolicy);
263 return *this;
265 TrackedAllocPolicy& operator=(TrackedAllocPolicy&& other) {
266 MOZ_ASSERT(this != &other);
267 zone()->unregisterNonGCMemory(this, MemoryUse::TrackedAllocPolicy);
268 zone_ = other.zone();
269 zone()->moveOtherMemory(this, &other, MemoryUse::TrackedAllocPolicy);
270 other.zone_ = nullptr;
271 return *this;
274 // Public methods required to fulfill the AllocPolicy interface.
276 template <typename T>
277 void free_(T* p, size_t numElems) {
278 if (p) {
279 decMemory(numElems * sizeof(T));
280 js_free(p);
284 [[nodiscard]] bool checkSimulatedOOM() const {
285 return !js::oom::ShouldFailWithOOM();
288 void reportAllocOverflow() const { reportAllocationOverflow(); }
290 // Internal methods called by the MallocProvider implementation.
292 [[nodiscard]] void* onOutOfMemory(js::AllocFunction allocFunc,
293 arena_id_t arena, size_t nbytes,
294 void* reallocPtr = nullptr) {
295 return zone()->onOutOfMemory(allocFunc, arena, nbytes, reallocPtr);
297 void reportAllocationOverflow() const { zone()->reportAllocationOverflow(); }
298 void updateMallocCounter(size_t nbytes) {
299 zone()->incNonGCMemory(this, nbytes, MemoryUse::TrackedAllocPolicy);
302 private:
303 ZoneAllocator* zone() const {
304 MOZ_ASSERT(zone_);
305 return zone_;
307 void decMemory(size_t nbytes);
310 using ZoneAllocPolicy = TrackedAllocPolicy<TrackingKind::Zone>;
311 using CellAllocPolicy = TrackedAllocPolicy<TrackingKind::Cell>;
313 // Functions for memory accounting on the zone.
315 // Associate malloc memory with a GC thing. This call should be matched by a
316 // following call to RemoveCellMemory with the same size and use. The total
317 // amount of malloc memory associated with a zone is used to trigger GC.
319 // You should use InitReservedSlot / InitObjectPrivate in preference to this
320 // where possible.
322 inline void AddCellMemory(gc::TenuredCell* cell, size_t nbytes, MemoryUse use) {
323 if (nbytes) {
324 ZoneAllocator::from(cell->zone())->addCellMemory(cell, nbytes, use);
327 inline void AddCellMemory(gc::Cell* cell, size_t nbytes, MemoryUse use) {
328 if (cell->isTenured()) {
329 AddCellMemory(&cell->asTenured(), nbytes, use);
333 // Remove association between malloc memory and a GC thing. This call should
334 // follow a call to AddCellMemory with the same size and use.
336 inline void RemoveCellMemory(gc::TenuredCell* cell, size_t nbytes,
337 MemoryUse use) {
338 MOZ_ASSERT(!CurrentThreadIsGCFinalizing(),
339 "Use GCContext methods to remove associated memory in finalizers");
341 if (nbytes) {
342 auto zoneBase = ZoneAllocator::from(cell->zoneFromAnyThread());
343 zoneBase->removeCellMemory(cell, nbytes, use, false);
346 inline void RemoveCellMemory(gc::Cell* cell, size_t nbytes, MemoryUse use) {
347 if (cell->isTenured()) {
348 RemoveCellMemory(&cell->asTenured(), nbytes, use);
352 } // namespace js
354 #endif // gc_ZoneAllocator_h