1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
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 jsgcinlines_h
8 #define jsgcinlines_h
9
10 #include "jsgc.h"
11
12 #include "gc/GCTrace.h"
13 #include "gc/Zone.h"
14
15 namespace js {
16 namespace gc {
17
18 static inline AllocKind
GetGCObjectKind(const Class * clasp)19 GetGCObjectKind(const Class* clasp)
20 {
21 if (clasp == FunctionClassPtr)
22 return AllocKind::FUNCTION;
23 uint32_t nslots = JSCLASS_RESERVED_SLOTS(clasp);
24 if (clasp->flags & JSCLASS_HAS_PRIVATE)
25 nslots++;
26 return GetGCObjectKind(nslots);
27 }
28
29 inline void
poke()30 GCRuntime::poke()
31 {
32 poked = true;
33
34 #ifdef JS_GC_ZEAL
35 /* Schedule a GC to happen "soon" after a GC poke. */
36 if (zealMode == ZealPokeValue)
37 nextScheduled = 1;
38 #endif
39 }
40
41 class ArenaIter
42 {
43 ArenaHeader* aheader;
44 ArenaHeader* unsweptHeader;
45 ArenaHeader* sweptHeader;
46
47 public:
ArenaIter()48 ArenaIter() {
49 aheader = nullptr;
50 unsweptHeader = nullptr;
51 sweptHeader = nullptr;
52 }
53
ArenaIter(JS::Zone * zone,AllocKind kind)54 ArenaIter(JS::Zone* zone, AllocKind kind) {
55 init(zone, kind);
56 }
57
init(JS::Zone * zone,AllocKind kind)58 void init(JS::Zone* zone, AllocKind kind) {
59 aheader = zone->arenas.getFirstArena(kind);
60 unsweptHeader = zone->arenas.getFirstArenaToSweep(kind);
61 sweptHeader = zone->arenas.getFirstSweptArena(kind);
62 if (!unsweptHeader) {
63 unsweptHeader = sweptHeader;
64 sweptHeader = nullptr;
65 }
66 if (!aheader) {
67 aheader = unsweptHeader;
68 unsweptHeader = sweptHeader;
69 sweptHeader = nullptr;
70 }
71 }
72
done()73 bool done() const {
74 return !aheader;
75 }
76
get()77 ArenaHeader* get() const {
78 return aheader;
79 }
80
next()81 void next() {
82 MOZ_ASSERT(!done());
83 aheader = aheader->next;
84 if (!aheader) {
85 aheader = unsweptHeader;
86 unsweptHeader = sweptHeader;
87 sweptHeader = nullptr;
88 }
89 }
90 };
91
92 enum CellIterNeedsBarrier : uint8_t
93 {
94 CellIterDoesntNeedBarrier = 0,
95 CellIterMayNeedBarrier = 1
96 };
97
98 class ArenaCellIterImpl
99 {
100 // These are set in initUnsynchronized().
101 size_t firstThingOffset;
102 size_t thingSize;
103 JS::TraceKind traceKind;
104 bool needsBarrier;
105 #ifdef DEBUG
106 bool isInited;
107 #endif
108
109 // These three are set in reset() (which is called by init()).
110 FreeSpan span;
111 uintptr_t thing;
112 uintptr_t limit;
113
114 // Upon entry, |thing| points to any thing (free or used) and finds the
115 // first used thing, which may be |thing|.
moveForwardIfFree()116 void moveForwardIfFree() {
117 MOZ_ASSERT(!done());
118 MOZ_ASSERT(thing);
119 // Note: if |span| is empty, this test will fail, which is what we want
120 // -- |span| being empty means that we're past the end of the last free
121 // thing, all the remaining things in the arena are used, and we'll
122 // never need to move forward.
123 if (thing == span.first) {
124 thing = span.last + thingSize;
125 span = *span.nextSpan();
126 }
127 }
128
129 public:
ArenaCellIterImpl()130 ArenaCellIterImpl()
131 : firstThingOffset(0) // Squelch
132 , thingSize(0) // warnings
133 , traceKind(JS::TraceKind::Null)
134 , needsBarrier(false)
135 , limit(0)
136 {
137 }
138
initUnsynchronized(ArenaHeader * aheader,CellIterNeedsBarrier mayNeedBarrier)139 void initUnsynchronized(ArenaHeader* aheader, CellIterNeedsBarrier mayNeedBarrier) {
140 AllocKind kind = aheader->getAllocKind();
141 #ifdef DEBUG
142 isInited = true;
143 #endif
144 firstThingOffset = Arena::firstThingOffset(kind);
145 thingSize = Arena::thingSize(kind);
146 traceKind = MapAllocToTraceKind(kind);
147 needsBarrier = mayNeedBarrier && !aheader->zone->runtimeFromMainThread()->isHeapCollecting();
148 reset(aheader);
149 }
150
init(ArenaHeader * aheader,CellIterNeedsBarrier mayNeedBarrier)151 void init(ArenaHeader* aheader, CellIterNeedsBarrier mayNeedBarrier) {
152 #ifdef DEBUG
153 AllocKind kind = aheader->getAllocKind();
154 MOZ_ASSERT(aheader->zone->arenas.isSynchronizedFreeList(kind));
155 #endif
156 initUnsynchronized(aheader, mayNeedBarrier);
157 }
158
159 // Use this to move from an Arena of a particular kind to another Arena of
160 // the same kind.
reset(ArenaHeader * aheader)161 void reset(ArenaHeader* aheader) {
162 MOZ_ASSERT(isInited);
163 span = aheader->getFirstFreeSpan();
164 uintptr_t arenaAddr = aheader->arenaAddress();
165 thing = arenaAddr + firstThingOffset;
166 limit = arenaAddr + ArenaSize;
167 moveForwardIfFree();
168 }
169
done()170 bool done() const {
171 return thing == limit;
172 }
173
getCell()174 TenuredCell* getCell() const {
175 MOZ_ASSERT(!done());
176 TenuredCell* cell = reinterpret_cast<TenuredCell*>(thing);
177
178 // This can result in a a new reference being created to an object that
179 // an ongoing incremental GC may find to be unreachable, so we may need
180 // a barrier here.
181 if (needsBarrier)
182 ExposeGCThingToActiveJS(JS::GCCellPtr(cell, traceKind));
183
184 return cell;
185 }
186
get()187 template<typename T> T* get() const {
188 MOZ_ASSERT(!done());
189 return static_cast<T*>(getCell());
190 }
191
next()192 void next() {
193 MOZ_ASSERT(!done());
194 thing += thingSize;
195 if (thing < limit)
196 moveForwardIfFree();
197 }
198 };
199
200 template<>
201 JSObject*
202 ArenaCellIterImpl::get<JSObject>() const;
203
204 class ArenaCellIterUnderGC : public ArenaCellIterImpl
205 {
206 public:
ArenaCellIterUnderGC(ArenaHeader * aheader)207 explicit ArenaCellIterUnderGC(ArenaHeader* aheader) {
208 MOZ_ASSERT(aheader->zone->runtimeFromAnyThread()->isHeapBusy());
209 init(aheader, CellIterDoesntNeedBarrier);
210 }
211 };
212
213 class ArenaCellIterUnderFinalize : public ArenaCellIterImpl
214 {
215 public:
ArenaCellIterUnderFinalize(ArenaHeader * aheader)216 explicit ArenaCellIterUnderFinalize(ArenaHeader* aheader) {
217 initUnsynchronized(aheader, CellIterDoesntNeedBarrier);
218 }
219 };
220
221 class ZoneCellIterImpl
222 {
223 ArenaIter arenaIter;
224 ArenaCellIterImpl cellIter;
225
226 protected:
ZoneCellIterImpl()227 ZoneCellIterImpl() {}
228
init(JS::Zone * zone,AllocKind kind)229 void init(JS::Zone* zone, AllocKind kind) {
230 MOZ_ASSERT(zone->arenas.isSynchronizedFreeList(kind));
231 arenaIter.init(zone, kind);
232 if (!arenaIter.done())
233 cellIter.init(arenaIter.get(), CellIterMayNeedBarrier);
234 }
235
236 public:
done()237 bool done() const {
238 return arenaIter.done();
239 }
240
get()241 template<typename T> T* get() const {
242 MOZ_ASSERT(!done());
243 return cellIter.get<T>();
244 }
245
getCell()246 Cell* getCell() const {
247 MOZ_ASSERT(!done());
248 return cellIter.getCell();
249 }
250
next()251 void next() {
252 MOZ_ASSERT(!done());
253 cellIter.next();
254 if (cellIter.done()) {
255 MOZ_ASSERT(!arenaIter.done());
256 arenaIter.next();
257 if (!arenaIter.done())
258 cellIter.reset(arenaIter.get());
259 }
260 }
261 };
262
263 class ZoneCellIterUnderGC : public ZoneCellIterImpl
264 {
265 public:
ZoneCellIterUnderGC(JS::Zone * zone,AllocKind kind)266 ZoneCellIterUnderGC(JS::Zone* zone, AllocKind kind) {
267 MOZ_ASSERT(zone->runtimeFromAnyThread()->gc.nursery.isEmpty());
268 MOZ_ASSERT(zone->runtimeFromAnyThread()->isHeapBusy());
269 init(zone, kind);
270 }
271 };
272
273 class ZoneCellIter : public ZoneCellIterImpl
274 {
275 JS::AutoAssertNoAlloc noAlloc;
276 ArenaLists* lists;
277 AllocKind kind;
278
279 public:
ZoneCellIter(JS::Zone * zone,AllocKind kind)280 ZoneCellIter(JS::Zone* zone, AllocKind kind)
281 : lists(&zone->arenas),
282 kind(kind)
283 {
284 JSRuntime* rt = zone->runtimeFromMainThread();
285
286 /*
287 * We have a single-threaded runtime, so there's no need to protect
288 * against other threads iterating or allocating. However, we do have
289 * background finalization; we have to wait for this to finish if it's
290 * currently active.
291 */
292 if (IsBackgroundFinalized(kind) &&
293 zone->arenas.needBackgroundFinalizeWait(kind))
294 {
295 rt->gc.waitBackgroundSweepEnd();
296 }
297
298 /* Evict the nursery before iterating so we can see all things. */
299 rt->gc.evictNursery();
300
301 if (lists->isSynchronizedFreeList(kind)) {
302 lists = nullptr;
303 } else {
304 MOZ_ASSERT(!rt->isHeapBusy());
305 lists->copyFreeListToArena(kind);
306 }
307
308 /* Assert that no GCs can occur while a ZoneCellIter is live. */
309 noAlloc.disallowAlloc(rt);
310
311 init(zone, kind);
312 }
313
~ZoneCellIter()314 ~ZoneCellIter() {
315 if (lists)
316 lists->clearFreeListInArena(kind);
317 }
318 };
319
320 class GCZonesIter
321 {
322 private:
323 ZonesIter zone;
324
325 public:
326 explicit GCZonesIter(JSRuntime* rt, ZoneSelector selector = WithAtoms)
zone(rt,selector)327 : zone(rt, selector)
328 {
329 if (!zone->isCollecting())
330 next();
331 }
332
done()333 bool done() const { return zone.done(); }
334
next()335 void next() {
336 MOZ_ASSERT(!done());
337 do {
338 zone.next();
339 } while (!zone.done() && !zone->isCollectingFromAnyThread());
340 }
341
get()342 JS::Zone* get() const {
343 MOZ_ASSERT(!done());
344 return zone;
345 }
346
347 operator JS::Zone*() const { return get(); }
348 JS::Zone* operator->() const { return get(); }
349 };
350
351 typedef CompartmentsIterT<GCZonesIter> GCCompartmentsIter;
352
353 /* Iterates over all zones in the current zone group. */
354 class GCZoneGroupIter {
355 private:
356 JS::Zone* current;
357
358 public:
GCZoneGroupIter(JSRuntime * rt)359 explicit GCZoneGroupIter(JSRuntime* rt) {
360 MOZ_ASSERT(rt->isHeapBusy());
361 current = rt->gc.getCurrentZoneGroup();
362 }
363
done()364 bool done() const { return !current; }
365
next()366 void next() {
367 MOZ_ASSERT(!done());
368 current = current->nextNodeInGroup();
369 }
370
get()371 JS::Zone* get() const {
372 MOZ_ASSERT(!done());
373 return current;
374 }
375
376 operator JS::Zone*() const { return get(); }
377 JS::Zone* operator->() const { return get(); }
378 };
379
380 typedef CompartmentsIterT<GCZoneGroupIter> GCCompartmentGroupIter;
381
382 } /* namespace gc */
383 } /* namespace js */
384
385 #endif /* jsgcinlines_h */
386