GC-inl.h (11770B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef gc_GC_inl_h 8 #define gc_GC_inl_h 9 10 #include "gc/GC.h" 11 12 #include "mozilla/DebugOnly.h" 13 #include "mozilla/Maybe.h" 14 15 #include "gc/IteratorUtils.h" 16 #include "gc/Marking.h" 17 #include "gc/Zone.h" 18 #include "vm/Runtime.h" 19 20 #include "gc/ArenaList-inl.h" 21 22 namespace js::gc { 23 24 class AutoAssertEmptyNursery; 25 26 // Iterate all arenas in a zone of the specified kind, for use by the GC. 27 // 28 // Since the GC never iterates arenas during foreground sweeping we can skip 29 // traversing foreground swept arenas. 30 class ArenaIterInGC : public ChainedIterator<ArenaList::Iterator, 2> { 31 public: 32 ArenaIterInGC(JS::Zone* zone, AllocKind kind) 33 : ChainedIterator(zone->arenas.arenaList(kind), 34 zone->arenas.collectingArenaList(kind)) { 35 #ifdef DEBUG 36 MOZ_ASSERT(JS::RuntimeHeapIsMajorCollecting()); 37 GCRuntime& gc = zone->runtimeFromMainThread()->gc; 38 MOZ_ASSERT(!gc.maybeGetForegroundFinalizedArenas(zone, kind)); 39 #endif 40 } 41 }; 42 43 // Iterate all arenas in a zone of the specified kind. May be called at any 44 // time. 45 // 46 // Most uses of this happen when we are not in incremental GC but the debugger 47 // can iterate scripts at any time. 48 class ArenaIter : public AutoGatherSweptArenas, 49 public ChainedIterator<ArenaList::Iterator, 3> { 50 public: 51 ArenaIter(JS::Zone* zone, AllocKind kind) 52 : AutoGatherSweptArenas(zone, kind), 53 ChainedIterator(zone->arenas.arenaList(kind), 54 zone->arenas.collectingArenaList(kind), sweptArenas()) { 55 } 56 }; 57 58 class ArenaCellIter { 59 size_t firstThingOffset; 60 size_t thingSize; 61 Arena* arenaAddr; 62 FreeSpan span; 63 uint_fast16_t thing; 64 mozilla::DebugOnly<JS::TraceKind> traceKind; 65 66 // Upon entry, |thing| points to any thing (free or used) and finds the 67 // first used thing, which may be |thing|. 68 void settle() { 69 MOZ_ASSERT(!done()); 70 MOZ_ASSERT(thing); 71 // Note: if |span| is empty, this test will fail, which is what we want 72 // -- |span| being empty means that we're past the end of the last free 73 // thing, all the remaining things in the arena are used, and we'll 74 // never need to move forward. 75 if (thing == span.first) { 76 thing = span.last + thingSize; 77 span = *span.nextSpan(arenaAddr); 78 } 79 } 80 81 public: 82 explicit ArenaCellIter(Arena* arena) { 83 MOZ_ASSERT(arena); 84 AllocKind kind = arena->getAllocKind(); 85 firstThingOffset = Arena::firstThingOffset(kind); 86 thingSize = Arena::thingSize(kind); 87 traceKind = MapAllocToTraceKind(kind); 88 arenaAddr = arena; 89 span = *arena->getFirstFreeSpan(); 90 thing = firstThingOffset; 91 settle(); 92 } 93 94 bool done() const { 95 MOZ_ASSERT(thing <= ArenaSize); 96 return thing == ArenaSize; 97 } 98 99 TenuredCell* get() const { 100 MOZ_ASSERT(!done()); 101 return reinterpret_cast<TenuredCell*>(uintptr_t(arenaAddr) + thing); 102 } 103 104 template <typename T> 105 T* as() const { 106 MOZ_ASSERT(!done()); 107 MOZ_ASSERT(JS::MapTypeToTraceKind<T>::kind == traceKind); 108 return reinterpret_cast<T*>(get()); 109 } 110 111 void next() { 112 MOZ_ASSERT(!done()); 113 thing += thingSize; 114 if (thing < ArenaSize) { 115 settle(); 116 } 117 } 118 119 operator TenuredCell*() const { return get(); } 120 TenuredCell* operator->() const { return get(); } 121 }; 122 123 template <typename T> 124 class ZoneAllCellIter; 125 126 template <> 127 class ZoneAllCellIter<TenuredCell> { 128 mozilla::Maybe<NestedIterator<ArenaIter, ArenaCellIter>> iter; 129 mozilla::Maybe<JS::AutoAssertNoGC> nogc; 130 131 protected: 132 // For use when a subclass wants to insert some setup before init(). 133 ZoneAllCellIter() = default; 134 135 void init(JS::Zone* zone, AllocKind kind) { 136 MOZ_ASSERT_IF(IsNurseryAllocable(kind), 137 (zone->isAtomsZone() || 138 zone->runtimeFromMainThread()->gc.nursery().isEmpty())); 139 initForTenuredIteration(zone, kind); 140 } 141 142 void initForTenuredIteration(JS::Zone* zone, AllocKind kind) { 143 JSRuntime* rt = zone->runtimeFromAnyThread(); 144 145 // If called from outside a GC, ensure that the heap is in a state 146 // that allows us to iterate. 147 if (!JS::RuntimeHeapIsBusy()) { 148 // Assert that no GCs can occur while a ZoneAllCellIter is live. 149 nogc.emplace(); 150 } 151 152 // We have a single-threaded runtime, so there's no need to protect 153 // against other threads iterating or allocating. However, we do have 154 // background finalization; we may have to wait for this to finish if 155 // it's currently active. 156 if (IsBackgroundFinalized(kind)) { 157 ArenaLists& arenas = zone->arenas; 158 if (zone->isGCFinished() && !arenas.doneBackgroundFinalize(kind)) { 159 rt->gc.waitBackgroundSweepEnd(); 160 } 161 } 162 iter.emplace(zone, kind); 163 } 164 165 public: 166 ZoneAllCellIter(JS::Zone* zone, AllocKind kind) { 167 // If we are iterating a nursery-allocated kind then we need to 168 // evict first so that we can see all things. 169 if (IsNurseryAllocable(kind)) { 170 zone->runtimeFromMainThread()->gc.evictNursery(); 171 } 172 173 init(zone, kind); 174 } 175 176 ZoneAllCellIter(JS::Zone* zone, AllocKind kind, 177 const js::gc::AutoAssertEmptyNursery&) { 178 // No need to evict the nursery. (This constructor is known statically 179 // to not GC.) 180 init(zone, kind); 181 } 182 183 bool done() const { return iter->done(); } 184 185 template <typename T> 186 T* get() const { 187 return iter->ref().as<T>(); 188 } 189 190 TenuredCell* getCell() const { return iter->get(); } 191 192 void next() { iter->next(); } 193 }; 194 195 /* clang-format off */ 196 // 197 // Iterator over the cells in a Zone, where the GC type (JSString, JSObject) is 198 // known, for a single AllocKind. Example usages: 199 // 200 // for (auto obj = zone->cellIter<JSObject>(AllocKind::OBJECT0); !obj.done(); obj.next()) { 201 // ... 202 // } 203 // 204 // for (auto script = zone->cellIter<JSScript>(); !script.done(); script.next()) { 205 // f(script->code()); 206 // } 207 // 208 // As this code demonstrates, you can use 'script' as if it were a JSScript*. 209 // Its actual type is ZoneAllCellIter<JSScript>, but for most purposes it will 210 // autoconvert to JSScript*. 211 // 212 // Note that in the JSScript case, ZoneAllCellIter is able to infer the AllocKind 213 // from the type 'JSScript', whereas in the JSObject case, the kind must be 214 // given (because there are multiple AllocKinds for objects). 215 // 216 // Also, the static rooting hazard analysis knows that the JSScript case will 217 // not GC during construction. The JSObject case needs to GC, or more precisely 218 // to empty the nursery and clear out the store buffer, so that it can see all 219 // objects to iterate over (the nursery is not iterable) and remove the 220 // possibility of having pointers from the store buffer to data hanging off 221 // stuff we're iterating over that we are going to delete. (The latter should 222 // not be a problem, since such instances should be using RelocatablePtr do 223 // remove themselves from the store buffer on deletion, but currently for 224 // subtle reasons that isn't good enough.) 225 // 226 // If the iterator is used within a GC, then there is no need to evict the 227 // nursery (again). You may select a variant that will skip the eviction either 228 // by specializing on a GCType that is never allocated in the nursery, or 229 // explicitly by passing in a trailing AutoAssertEmptyNursery argument. 230 // 231 // NOTE: This class can return items that are about to be swept/finalized. 232 // You must not keep pointers to such items across GCs. Use 233 // ZoneCellIter below to filter these out. 234 // 235 // NOTE: This class also does not read barrier returned items, so may return 236 // gray cells. You must not store such items anywhere on the heap without 237 // gray-unmarking them. Use ZoneCellIter to automatically unmark them. 238 // 239 /* clang-format on */ 240 template <typename GCType> 241 class ZoneAllCellIter : public ZoneAllCellIter<TenuredCell> { 242 public: 243 // Non-nursery allocated (equivalent to having an entry in 244 // MapTypeToAllocKind). The template declaration here is to discard this 245 // constructor overload if MapTypeToAllocKind<GCType>::kind does not 246 // exist. Note that there will be no remaining overloads that will work, which 247 // makes sense given that you haven't specified which of the AllocKinds to use 248 // for GCType. 249 // 250 // If we later add a nursery allocable GCType with a single AllocKind, we will 251 // want to add an overload of this constructor that does the right thing (ie, 252 // it empties the nursery before iterating.) 253 explicit ZoneAllCellIter(JS::Zone* zone) : ZoneAllCellIter<TenuredCell>() { 254 init(zone, MapTypeToAllocKind<GCType>::kind); 255 } 256 257 // Non-nursery allocated, nursery is known to be empty: same behavior as 258 // above. 259 ZoneAllCellIter(JS::Zone* zone, const js::gc::AutoAssertEmptyNursery&) 260 : ZoneAllCellIter(zone) {} 261 262 // Arbitrary kind, which will be assumed to be nursery allocable (and 263 // therefore the nursery will be emptied before iterating.) 264 ZoneAllCellIter(JS::Zone* zone, AllocKind kind) 265 : ZoneAllCellIter<TenuredCell>(zone, kind) {} 266 267 // Arbitrary kind, which will be assumed to be nursery allocable, but the 268 // nursery is known to be empty already: same behavior as non-nursery types. 269 ZoneAllCellIter(JS::Zone* zone, AllocKind kind, 270 const js::gc::AutoAssertEmptyNursery& empty) 271 : ZoneAllCellIter<TenuredCell>(zone, kind, empty) {} 272 273 GCType* get() const { return ZoneAllCellIter<TenuredCell>::get<GCType>(); } 274 operator GCType*() const { return get(); } 275 GCType* operator->() const { return get(); } 276 }; 277 278 // Like the above class but filter out cells that are about to be finalized. 279 // Also, read barrier all cells returned (unless the Unbarriered variants are 280 // used) to prevent gray cells from escaping. 281 template <typename T> 282 class ZoneCellIter : protected ZoneAllCellIter<T> { 283 using Base = ZoneAllCellIter<T>; 284 285 public: 286 /* 287 * The same constructors as above. 288 */ 289 explicit ZoneCellIter(JS::Zone* zone) : ZoneAllCellIter<T>(zone) { 290 skipDying(); 291 } 292 ZoneCellIter(JS::Zone* zone, const js::gc::AutoAssertEmptyNursery& empty) 293 : ZoneAllCellIter<T>(zone, empty) { 294 skipDying(); 295 } 296 ZoneCellIter(JS::Zone* zone, AllocKind kind) 297 : ZoneAllCellIter<T>(zone, kind) { 298 skipDying(); 299 } 300 ZoneCellIter(JS::Zone* zone, AllocKind kind, 301 const js::gc::AutoAssertEmptyNursery& empty) 302 : ZoneAllCellIter<T>(zone, kind, empty) { 303 skipDying(); 304 } 305 306 using Base::done; 307 308 void next() { 309 ZoneAllCellIter<T>::next(); 310 skipDying(); 311 } 312 313 TenuredCell* getCell() const { 314 TenuredCell* cell = Base::getCell(); 315 316 // This can result in a new reference being created to an object that an 317 // ongoing incremental GC may find to be unreachable, so we may need a 318 // barrier here. 319 JSRuntime* rt = cell->runtimeFromAnyThread(); 320 if (!JS::RuntimeHeapIsCollecting(rt->heapState())) { 321 JS::TraceKind traceKind = JS::MapTypeToTraceKind<T>::kind; 322 ExposeGCThingToActiveJS(JS::GCCellPtr(cell, traceKind)); 323 } 324 325 return cell; 326 } 327 328 T* get() const { return reinterpret_cast<T*>(getCell()); } 329 330 TenuredCell* unbarrieredGetCell() const { return Base::getCell(); } 331 T* unbarrieredGet() const { return Base::get(); } 332 operator T*() const { return get(); } 333 T* operator->() const { return get(); } 334 335 private: 336 void skipDying() { 337 while (!ZoneAllCellIter<T>::done()) { 338 T* current = ZoneAllCellIter<T>::get(); 339 if (!IsAboutToBeFinalizedUnbarriered(current)) { 340 return; 341 } 342 ZoneAllCellIter<T>::next(); 343 } 344 } 345 }; 346 347 } // namespace js::gc 348 349 #endif /* gc_GC_inl_h */