tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 */