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