tor-browser

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

Heap-inl.h (7787B)


      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_Heap_inl_h
      8 #define gc_Heap_inl_h
      9 
     10 #include "gc/Heap.h"
     11 
     12 #include "gc/StoreBuffer.h"
     13 #include "gc/Zone.h"
     14 #include "util/Poison.h"
     15 #include "vm/Runtime.h"
     16 
     17 inline void js::gc::Arena::init(GCRuntime* gc, JS::Zone* zone, AllocKind kind) {
     18  MOZ_ASSERT(zone);
     19  MOZ_ASSERT(IsValidAllocKind(kind));
     20 
     21  MOZ_MAKE_MEM_UNDEFINED(this, ArenaSize);
     22 
     23  allocKind = kind;
     24  zone_ = zone;
     25  next = nullptr;
     26  isNewlyCreated_ = 1;
     27  onDelayedMarkingList_ = 0;
     28  hasDelayedBlackMarking_ = 0;
     29  hasDelayedGrayMarking_ = 0;
     30  nextDelayedMarkingArena_ = 0;
     31  if (zone_->isAtomsZone()) {
     32    atomBitmapStart() = gc->atomMarking.allocateIndex(gc);
     33  } else {
     34    bufferedCells() = &ArenaCellSet::Empty;
     35  }
     36 
     37  setAsFullyUnused();  // Initializes firstFreeSpan.
     38 
     39 #ifdef DEBUG
     40  checkNoMarkedCells();
     41 #endif
     42 }
     43 
     44 inline void js::gc::Arena::freeAtomMarkingBitmapIndex(GCRuntime* gc,
     45                                                      const AutoLockGC& lock) {
     46  MOZ_ASSERT(zone_->isAtomsZone());
     47  gc->atomMarking.freeIndex(atomBitmapStart(), lock);
     48 #ifdef DEBUG
     49  atomBitmapStart() = 0;  // Also zeroed by write to bufferedCells_ in release.
     50 #endif
     51 }
     52 
     53 inline void js::gc::Arena::release() {
     54  MOZ_ASSERT(allocated());
     55 
     56  // Clients should call freeAtomMarkingBitmapIndex() if necessary.
     57  MOZ_ASSERT_IF(zone_->isAtomsZone(), atomBitmapStart_ == 0);
     58 
     59  // Poison zone pointer to highlight UAF on released arenas in crash data.
     60  AlwaysPoison(&zone_, JS_FREED_ARENA_PATTERN, sizeof(zone_),
     61               MemCheckKind::MakeNoAccess);
     62 
     63  firstFreeSpan.initAsEmpty();
     64  allocKind = AllocKind::LIMIT;
     65  onDelayedMarkingList_ = 0;
     66  hasDelayedBlackMarking_ = 0;
     67  hasDelayedGrayMarking_ = 0;
     68  nextDelayedMarkingArena_ = 0;
     69  bufferedCells_ = nullptr;
     70 
     71  MOZ_ASSERT(!allocated());
     72 }
     73 
     74 inline js::gc::ArenaCellSet*& js::gc::Arena::bufferedCells() {
     75  MOZ_ASSERT(zone_ && !zone_->isAtomsZone());
     76  return bufferedCells_;
     77 }
     78 
     79 inline size_t& js::gc::Arena::atomBitmapStart() {
     80  MOZ_ASSERT(zone_ && zone_->isAtomsZone());
     81  return atomBitmapStart_;
     82 }
     83 
     84 // Mark bitmap API:
     85 
     86 // Unless noted otherwise, the following methods that update the mark bits are
     87 // not thread safe and must not be called in parallel with each other.
     88 //
     89 // They use separate read and write operations to avoid an unnecessarily strict
     90 // atomic update on the marking bitmap.
     91 //
     92 // They may be called in parallel with read operations on the mark bitmap where
     93 // there is no required ordering between the operations. This happens when gray
     94 // unmarking occurs in parallel with background sweeping.
     95 
     96 // The return value indicates if the cell went from unmarked to marked.
     97 MOZ_ALWAYS_INLINE bool js::gc::ChunkMarkBitmap::markIfUnmarked(
     98    const void* cell, MarkColor color) {
     99  Word* word;
    100  uintptr_t mask;
    101  getMarkWordAndMask(cell, ColorBit::BlackBit, &word, &mask);
    102  if (*word & mask) {
    103    return false;
    104  }
    105  if (color == MarkColor::Black) {
    106    uintptr_t bits = *word;
    107    *word = bits | mask;
    108  } else {
    109    // We use getMarkWordAndMask to recalculate both mask and word as doing just
    110    // mask << color may overflow the mask.
    111    getMarkWordAndMask(cell, ColorBit::GrayOrBlackBit, &word, &mask);
    112    if (*word & mask) {
    113      return false;
    114    }
    115    uintptr_t bits = *word;
    116    *word = bits | mask;
    117  }
    118  return true;
    119 }
    120 
    121 // This version of the method is safe in the face of concurrent writes to the
    122 // mark bitmap but if two threads attempt to mark the same cell at the same time
    123 // then both calls can succeed and return true.
    124 //
    125 // This method is used for parallel marking where the extra synchronization
    126 // required to avoid this results in worse performance overall.
    127 MOZ_ALWAYS_INLINE bool js::gc::ChunkMarkBitmap::markIfUnmarkedThreadSafe(
    128    const void* cell, MarkColor color) {
    129  Word* word;
    130  uintptr_t mask;
    131  getMarkWordAndMask(cell, ColorBit::BlackBit, &word, &mask);
    132  if (*word & mask) {
    133    return false;
    134  }
    135  if (color == MarkColor::Black) {
    136    *word |= mask;
    137  } else {
    138    // We use getMarkWordAndMask to recalculate both mask and word as doing just
    139    // mask << color may overflow the mask.
    140    getMarkWordAndMask(cell, ColorBit::GrayOrBlackBit, &word, &mask);
    141    if (*word & mask) {
    142      return false;
    143    }
    144    *word |= mask;
    145  }
    146  return true;
    147 }
    148 
    149 MOZ_ALWAYS_INLINE void js::gc::ChunkMarkBitmap::markBlack(const void* cell) {
    150  Word* word;
    151  uintptr_t mask;
    152  getMarkWordAndMask(cell, ColorBit::BlackBit, &word, &mask);
    153  uintptr_t bits = *word;
    154  *word = bits | mask;
    155 }
    156 
    157 MOZ_ALWAYS_INLINE void js::gc::ChunkMarkBitmap::markBlackAtomic(
    158    const void* cell) {
    159  Word* word;
    160  uintptr_t mask;
    161  getMarkWordAndMask(cell, ColorBit::BlackBit, &word, &mask);
    162  *word |= mask;
    163 }
    164 
    165 MOZ_ALWAYS_INLINE void js::gc::ChunkMarkBitmap::copyMarkBit(
    166    TenuredCell* dst, const TenuredCell* src, ColorBit colorBit) {
    167  ArenaChunkBase* srcChunk = detail::GetCellChunkBase(src);
    168  Word* srcWord;
    169  uintptr_t srcMask;
    170  srcChunk->markBits.getMarkWordAndMask(src, colorBit, &srcWord, &srcMask);
    171 
    172  Word* dstWord;
    173  uintptr_t dstMask;
    174  getMarkWordAndMask(dst, colorBit, &dstWord, &dstMask);
    175 
    176  uintptr_t bits = *dstWord;
    177  bits &= ~dstMask;
    178  if (*srcWord & srcMask) {
    179    bits |= dstMask;
    180  }
    181  *dstWord = bits;
    182 }
    183 
    184 MOZ_ALWAYS_INLINE void js::gc::ChunkMarkBitmap::unmark(const void* cell) {
    185  unmarkOneBit(cell, ColorBit::BlackBit);
    186  unmarkOneBit(cell, ColorBit::GrayOrBlackBit);
    187 }
    188 
    189 MOZ_ALWAYS_INLINE void js::gc::ChunkMarkBitmap::unmarkOneBit(
    190    const void* cell, ColorBit colorBit) {
    191  Word* word;
    192  uintptr_t mask;
    193  uintptr_t bits;
    194  getMarkWordAndMask(cell, colorBit, &word, &mask);
    195  bits = *word;
    196  *word = bits & ~mask;
    197 }
    198 
    199 inline js::gc::AtomicBitmapWord* js::gc::ChunkMarkBitmap::arenaBits(
    200    Arena* arena) {
    201  static_assert(
    202      ArenaBitmapBits == ArenaBitmapWords * JS_BITS_PER_WORD,
    203      "We assume that the part of the bitmap corresponding to the arena "
    204      "has the exact number of words so we do not need to deal with a word "
    205      "that covers bits from two arenas.");
    206 
    207  Word* word;
    208  uintptr_t unused;
    209  getMarkWordAndMask(arena, ColorBit::BlackBit, &word, &unused);
    210  return word;
    211 }
    212 
    213 inline void js::gc::ChunkMarkBitmap::copyFrom(const ChunkMarkBitmap& other) {
    214  Bitmap::copyFrom(other);
    215 }
    216 
    217 template <size_t N>
    218 void js::gc::AtomicBitmap<N>::copyFrom(const AtomicBitmap& other) {
    219  for (size_t i = 0; i < WordCount; i++) {
    220    bitmap[i] = uintptr_t(other.bitmap[i]);
    221  }
    222 }
    223 
    224 template <size_t N>
    225 void js::gc::AtomicBitmap<N>::clear() {
    226  for (size_t i = 0; i < WordCount; i++) {
    227    bitmap[i] = 0;
    228  }
    229 }
    230 
    231 template <size_t N>
    232 bool js::gc::AtomicBitmap<N>::isEmpty() const {
    233  for (size_t i = 0; i < WordCount; i++) {
    234    if (bitmap[i]) {
    235      return false;
    236    }
    237  }
    238 
    239  return true;
    240 }
    241 
    242 bool js::gc::TenuredCell::markIfUnmarked(MarkColor color /* = Black */) const {
    243  return chunk()->markBits.markIfUnmarked(this, color);
    244 }
    245 
    246 bool js::gc::TenuredCell::markIfUnmarkedThreadSafe(MarkColor color) const {
    247  return chunk()->markBits.markIfUnmarkedThreadSafe(this, color);
    248 }
    249 
    250 void js::gc::TenuredCell::markBlack() const {
    251  chunk()->markBits.markBlack(this);
    252 }
    253 
    254 void js::gc::TenuredCell::markBlackAtomic() const {
    255  chunk()->markBits.markBlackAtomic(this);
    256 }
    257 
    258 void js::gc::TenuredCell::copyMarkBitsFrom(const TenuredCell* src) {
    259  ChunkMarkBitmap& markBits = chunk()->markBits;
    260  markBits.copyMarkBit(this, src, ColorBit::BlackBit);
    261  markBits.copyMarkBit(this, src, ColorBit::GrayOrBlackBit);
    262 }
    263 
    264 void js::gc::TenuredCell::unmark() { chunk()->markBits.unmark(this); }
    265 
    266 #endif