tor-browser

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

ArenaList-inl.h (8044B)


      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_ArenaList_inl_h
      8 #define gc_ArenaList_inl_h
      9 
     10 #include "gc/ArenaList.h"
     11 
     12 #include "gc/Heap.h"
     13 #include "gc/Zone.h"
     14 
     15 bool js::gc::ArenaList::hasNonFullArenas() const {
     16  // Non-full arenas are kept at the start so we can check the first one.
     17  return !isEmpty() && !getFirst()->isFull();
     18 }
     19 
     20 js::gc::Arena* js::gc::ArenaList::takeInitialNonFullArena() {
     21  Arena* arena = getFirst();
     22  if (!arena || arena->isFull()) {
     23    return nullptr;
     24  }
     25 
     26  moveFrontToBack();
     27 
     28  return arena;
     29 }
     30 
     31 js::gc::SortedArenaList::SortedArenaList(js::gc::AllocKind allocKind)
     32    : thingsPerArena_(Arena::thingsPerArena(allocKind)) {
     33 #ifdef DEBUG
     34  MOZ_ASSERT(thingsPerArena_ <= MaxThingsPerArena);
     35  MOZ_ASSERT(emptyIndex() < BucketCount);
     36  allocKind_ = allocKind;
     37 #endif
     38 }
     39 
     40 size_t js::gc::SortedArenaList::index(size_t nfree, bool* frontOut) const {
     41  // Get the bucket index to use for arenas with |nfree| free things and set
     42  // |frontOut| to indicate whether to prepend or append to the bucket.
     43 
     44  MOZ_ASSERT(nfree <= thingsPerArena_);
     45 
     46  // Full arenas go in the first bucket on their own.
     47  if (nfree == 0) {
     48    *frontOut = false;
     49    return 0;
     50  }
     51 
     52  // Empty arenas go in the last bucket on their own.
     53  if (nfree == thingsPerArena_) {
     54    *frontOut = false;
     55    return emptyIndex();
     56  }
     57 
     58  // All other arenas are alternately added to the front and back of successive
     59  // buckets as |nfree| increases.
     60  *frontOut = (nfree % 2) != 0;
     61  size_t index = (nfree + 1) / 2;
     62  MOZ_ASSERT(index != 0);
     63  MOZ_ASSERT(index != emptyIndex());
     64  return index;
     65 }
     66 
     67 size_t js::gc::SortedArenaList::emptyIndex() const {
     68  // Get the bucket index to use for empty arenas. This must have its own
     69  // bucket so they can be removed with extractEmptyTo.
     70  return bucketsUsed() - 1;
     71 }
     72 
     73 size_t js::gc::SortedArenaList::bucketsUsed() const {
     74  // Get the total number of buckets used for the current alloc kind.
     75  return HowMany(thingsPerArena_ - 1, 2) + 2;
     76 }
     77 
     78 void js::gc::SortedArenaList::insertAt(Arena* arena, size_t nfree) {
     79  MOZ_ASSERT(!isConvertedToArenaList);
     80  MOZ_ASSERT(nfree <= thingsPerArena_);
     81 
     82  bool front;
     83  size_t i = index(nfree, &front);
     84  MOZ_ASSERT(i < BucketCount);
     85  if (front) {
     86    buckets[i].pushFront(arena);
     87  } else {
     88    buckets[i].pushBack(arena);
     89  }
     90 }
     91 
     92 bool js::gc::SortedArenaList::hasEmptyArenas() const {
     93  return !buckets[emptyIndex()].isEmpty();
     94 }
     95 
     96 void js::gc::SortedArenaList::extractEmptyTo(Arena** destListHeadPtr) {
     97  MOZ_ASSERT(!isConvertedToArenaList);
     98  MOZ_ASSERT(destListHeadPtr);
     99  check();
    100 
    101  Bucket& bucket = buckets[emptyIndex()];
    102  if (!bucket.isEmpty()) {
    103    Arena* tail = *destListHeadPtr;
    104    Arena* bucketLast = bucket.getLast();
    105    *destListHeadPtr = bucket.release();
    106    bucketLast->next = tail;
    107  }
    108 
    109  MOZ_ASSERT(bucket.isEmpty());
    110 }
    111 
    112 js::gc::ArenaList js::gc::SortedArenaList::convertToArenaList(
    113    Arena* maybeBucketLastOut[BucketCount]) {
    114 #ifdef DEBUG
    115  MOZ_ASSERT(!isConvertedToArenaList);
    116  isConvertedToArenaList = true;
    117  check();
    118 #endif
    119 
    120  if (maybeBucketLastOut) {
    121    for (size_t i = 0; i < BucketCount; i++) {
    122      maybeBucketLastOut[i] = buckets[i].getLast();
    123    }
    124  }
    125 
    126  // The returned ArenaList needs to contain all non-full arenas in order
    127  // of increasing free space, followed by all full arenas.
    128  ArenaList result;
    129  size_t used = bucketsUsed();
    130  for (size_t i = 1; i <= used; ++i) {
    131    size_t bucket = i % used;  // [1, used) then 0.
    132    result.append(std::move(buckets[bucket]));
    133  }
    134  return result;
    135 }
    136 
    137 void js::gc::SortedArenaList::restoreFromArenaList(
    138    ArenaList& list, Arena* bucketLast[BucketCount]) {
    139 #ifdef DEBUG
    140  MOZ_ASSERT(isConvertedToArenaList);
    141  isConvertedToArenaList = false;
    142 #endif
    143 
    144  // Group the ArenaList elements into SinglyLinkedList buckets, where the
    145  // boundaries between buckets are retrieved from |bucketLast|.
    146 
    147  Arena* remaining = list.release();
    148 
    149  size_t used = bucketsUsed();
    150  for (size_t i = 1; i <= used; ++i) {
    151    size_t bucket = i % used;  // [1, used) then 0.
    152    MOZ_ASSERT(buckets[bucket].isEmpty());
    153    if (bucketLast[bucket]) {
    154      MOZ_ASSERT(remaining);
    155      Arena* first = remaining;
    156      Arena* last = bucketLast[bucket];
    157      remaining = last->next;
    158      last->next = nullptr;
    159      new (&buckets[bucket]) Bucket(first, last);
    160    }
    161  }
    162 
    163  MOZ_ASSERT(!remaining);
    164  check();
    165 }
    166 
    167 void js::gc::SortedArenaList::check() const {
    168 #ifdef DEBUG
    169  const auto& fullBucket = buckets[0];
    170  for (auto arena = fullBucket.iter(); !arena.done(); arena.next()) {
    171    MOZ_ASSERT(arena->getAllocKind() == allocKind());
    172    MOZ_ASSERT(!arena->hasFreeThings());
    173  }
    174 
    175  for (size_t i = 1; i < emptyIndex(); i++) {
    176    const auto& bucket = buckets[i];
    177    size_t lastFree = 0;
    178    for (auto arena = bucket.iter(); !arena.done(); arena.next()) {
    179      MOZ_ASSERT(arena->getAllocKind() == allocKind());
    180      size_t nfree = arena->countFreeCells();
    181      MOZ_ASSERT(nfree == i * 2 - 1 || nfree == i * 2);
    182      MOZ_ASSERT(nfree >= lastFree);
    183      lastFree = nfree;
    184    }
    185  }
    186 
    187  const auto& emptyBucket = buckets[emptyIndex()];
    188  for (auto arena = emptyBucket.iter(); !arena.done(); arena.next()) {
    189    MOZ_ASSERT(arena->getAllocKind() == allocKind());
    190    MOZ_ASSERT(arena->isEmpty());
    191  }
    192 
    193  for (size_t i = emptyIndex() + 1; i < BucketCount; i++) {
    194    MOZ_ASSERT(buckets[i].isEmpty());
    195  }
    196 #endif
    197 }
    198 
    199 #ifdef DEBUG
    200 
    201 bool js::gc::FreeLists::allEmpty() const {
    202  for (auto i : AllAllocKinds()) {
    203    if (!isEmpty(i)) {
    204      return false;
    205    }
    206  }
    207  return true;
    208 }
    209 
    210 bool js::gc::FreeLists::isEmpty(AllocKind kind) const {
    211  return freeLists_[kind]->isEmpty();
    212 }
    213 
    214 #endif
    215 
    216 void js::gc::FreeLists::clear() {
    217  for (auto i : AllAllocKinds()) {
    218 #ifdef DEBUG
    219    auto old = freeLists_[i];
    220    if (!old->isEmpty()) {
    221      old->getArena()->checkNoMarkedFreeCells();
    222    }
    223 #endif
    224    freeLists_[i] = &emptySentinel;
    225  }
    226 }
    227 
    228 js::gc::TenuredCell* js::gc::FreeLists::allocate(AllocKind kind) {
    229  return freeLists_[kind]->allocate(Arena::thingSize(kind));
    230 }
    231 
    232 void js::gc::FreeLists::unmarkPreMarkedFreeCells(AllocKind kind) {
    233  FreeSpan* freeSpan = freeLists_[kind];
    234  if (!freeSpan->isEmpty()) {
    235    freeSpan->getArena()->unmarkPreMarkedFreeCells();
    236  }
    237 }
    238 
    239 JSRuntime* js::gc::ArenaLists::runtime() {
    240  return zone_->runtimeFromMainThread();
    241 }
    242 
    243 JSRuntime* js::gc::ArenaLists::runtimeFromAnyThread() {
    244  return zone_->runtimeFromAnyThread();
    245 }
    246 
    247 js::gc::Arena* js::gc::ArenaLists::getFirstArena(AllocKind thingKind) const {
    248  return arenaList(thingKind).getFirst();
    249 }
    250 
    251 js::gc::Arena* js::gc::ArenaLists::getFirstCollectingArena(
    252    AllocKind thingKind) const {
    253  return collectingArenaList(thingKind).getFirst();
    254 }
    255 
    256 bool js::gc::ArenaLists::arenaListsAreEmpty() const {
    257  for (auto i : AllAllocKinds()) {
    258    /*
    259     * The arena cannot be empty if the background finalization is not yet
    260     * done.
    261     */
    262    if (concurrentUse(i) == ConcurrentUse::BackgroundFinalize) {
    263      return false;
    264    }
    265    if (!arenaList(i).isEmpty()) {
    266      return false;
    267    }
    268  }
    269  return true;
    270 }
    271 
    272 bool js::gc::ArenaLists::doneBackgroundFinalize(AllocKind kind) const {
    273  return concurrentUse(kind) == ConcurrentUse::None;
    274 }
    275 
    276 void js::gc::ArenaLists::clearFreeLists() { freeLists().clear(); }
    277 
    278 MOZ_ALWAYS_INLINE js::gc::TenuredCell* js::gc::ArenaLists::allocateFromFreeList(
    279    AllocKind thingKind) {
    280  return freeLists().allocate(thingKind);
    281 }
    282 
    283 void js::gc::ArenaLists::unmarkPreMarkedFreeCells() {
    284  for (auto i : AllAllocKinds()) {
    285    freeLists().unmarkPreMarkedFreeCells(i);
    286  }
    287 }
    288 
    289 void js::gc::ArenaLists::checkEmptyFreeLists() {
    290  MOZ_ASSERT(freeLists().allEmpty());
    291 }
    292 
    293 void js::gc::ArenaLists::checkEmptyArenaLists() {
    294 #ifdef DEBUG
    295  for (auto i : AllAllocKinds()) {
    296    checkEmptyArenaList(i);
    297  }
    298 #endif
    299 }
    300 
    301 #endif  // gc_ArenaList_inl_h