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