ArenaList.h (12314B)
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 /* 8 * GC-internal definitions of ArenaList and associated heap data structures. 9 */ 10 11 #ifndef gc_ArenaList_h 12 #define gc_ArenaList_h 13 14 #include <utility> 15 16 #include "ds/SinglyLinkedList.h" 17 #include "gc/AllocKind.h" 18 #include "gc/Memory.h" 19 #include "js/GCAPI.h" 20 #include "js/HeapAPI.h" 21 #include "js/TypeDecls.h" 22 #include "threading/ProtectedData.h" 23 24 namespace JS { 25 class SliceBudget; 26 } 27 28 namespace js { 29 30 class Nursery; 31 32 namespace gcstats { 33 struct Statistics; 34 } 35 36 namespace gc { 37 38 class Arena; 39 class ArenaIter; 40 class ArenaIterInGC; 41 class AutoGatherSweptArenas; 42 class BackgroundUnmarkTask; 43 struct FinalizePhase; 44 class FreeSpan; 45 class TenuredCell; 46 class TenuringTracer; 47 48 // Whether or not to release empty arenas while sweeping. 49 enum class ReleaseEmpty : bool { No = false, Yes = true }; 50 51 /* 52 * Arena lists contain a singly linked lists of arenas. 53 * 54 * Arenas with free space are positioned at the front of the list, sorted in 55 * order of increasing free space (which is the order we intend to use them): 56 * 57 * For example, the following shows a possible arrangement of arenas with count 58 * of used cells on Y axis: 59 * 60 * | +----+----+----+----+----+ 61 * | | | | | | | 62 * | | | | | | | 63 * +----+----+ | | | | | | 64 * | | +----+ | | | | | | 65 * | | | +----+ | | | | | | 66 * | | | | | | | | | | | 67 * | | | | +----+ | | | | | | 68 * | | | | | | | | | | | | 69 * | | | | | +----+----+ | | | | | | 70 * | | | | | | | +----+ | | | | | 71 * +----+----+----+----+----+----+----+----+----+----+----+----+----+ 72 * 73 * This provides a convenient way of getting the next arena with free space when 74 * allocating: the arena at the front of the list is taken, marked as fully 75 * allocated and moved to the end of the list. 76 * 77 * The ordering is chosen to try and fill up arenas as much as possible and 78 * leave more empty arenas to be reclaimed when their contents die. 79 * 80 * The ordering should not be treated as an invariant, however, as the free 81 * lists may be cleared, leaving arenas previously used for allocation partially 82 * full. Sorting order is restored during sweeping. 83 */ 84 class ArenaList : public SinglyLinkedList<Arena> { 85 public: 86 inline bool hasNonFullArenas() const; 87 88 // This returns the first arena if it has any free space, moving it to the 89 // back of the list. If there are no arenas or if the first one is full then 90 // return nullptr. 91 inline Arena* takeInitialNonFullArena(); 92 93 std::pair<Arena*, Arena*> pickArenasToRelocate(AllocKind kind, 94 size_t& arenaTotalOut, 95 size_t& relocTotalOut); 96 97 Arena* relocateArenas(Arena* toRelocate, Arena* relocated, 98 JS::SliceBudget& sliceBudget, 99 gcstats::Statistics& stats); 100 101 #ifdef DEBUG 102 void dump(); 103 #endif 104 }; 105 106 /* 107 * A class that is used to sort arenas of a single AllocKind into increasing 108 * order of free space. 109 * 110 * It works by adding arenas to a bucket corresponding to the number of free 111 * things in the arena. Each bucket is an independent linked list. 112 * 113 * The buckets can be linked up to form a sorted ArenaList. 114 */ 115 class SortedArenaList { 116 public: 117 static_assert(ArenaSize <= 4096, 118 "When increasing the Arena size, please consider how" 119 " this will affect the size of a SortedArenaList."); 120 121 static_assert(MinCellSize >= 16, 122 "When decreasing the minimum thing size, please consider" 123 " how this will affect the size of a SortedArenaList."); 124 125 // The maximum number of GC things that an arena can hold. 126 static const size_t MaxThingsPerArena = 127 (ArenaSize - ArenaHeaderSize) / MinCellSize; 128 129 // The number of buckets required: one full arenas, one for empty arenas and 130 // half the number of remaining size classes. 131 static const size_t BucketCount = HowMany(MaxThingsPerArena - 1, 2) + 2; 132 133 private: 134 using Bucket = SinglyLinkedList<Arena>; 135 136 const size_t thingsPerArena_; 137 Bucket buckets[BucketCount]; 138 139 #ifdef DEBUG 140 AllocKind allocKind_; 141 bool isConvertedToArenaList = false; 142 #endif 143 144 public: 145 inline explicit SortedArenaList(AllocKind allocKind); 146 147 size_t thingsPerArena() const { return thingsPerArena_; } 148 149 // Inserts an arena, which has room for |nfree| more things, in its bucket. 150 inline void insertAt(Arena* arena, size_t nfree); 151 152 inline bool hasEmptyArenas() const; 153 154 // Remove any empty arenas and prepend them to the list pointed to by 155 // |destListHeadPtr|. 156 inline void extractEmptyTo(Arena** destListHeadPtr); 157 158 // Converts the contents of this data structure to a single list, by linking 159 // up the tail of each non-empty bucket to the head of the next non-empty 160 // bucket. 161 // 162 // Optionally saves internal state to |maybeBucketLastOut| so that it can be 163 // restored later by calling restoreFromArenaList. It is not valid to use this 164 // class in the meantime. 165 inline ArenaList convertToArenaList( 166 Arena* maybeBucketLastOut[BucketCount] = nullptr); 167 168 // Restore the internal state of this class following conversion to an 169 // ArenaList by the previous method. 170 inline void restoreFromArenaList(ArenaList& list, 171 Arena* bucketLast[BucketCount]); 172 173 #ifdef DEBUG 174 AllocKind allocKind() const { return allocKind_; } 175 #endif 176 177 private: 178 inline size_t index(size_t nfree, bool* frontOut) const; 179 inline size_t emptyIndex() const; 180 inline size_t bucketsUsed() const; 181 182 inline void check() const; 183 }; 184 185 // Gather together any swept arenas for the given zone and alloc kind. 186 class MOZ_RAII AutoGatherSweptArenas { 187 SortedArenaList* sortedList = nullptr; 188 189 // Internal state from SortedArenaList so we can restore it later. 190 Arena* bucketLastPointers[SortedArenaList::BucketCount]; 191 192 // Single result list. 193 ArenaList linked; 194 195 public: 196 AutoGatherSweptArenas(JS::Zone* zone, AllocKind kind); 197 ~AutoGatherSweptArenas(); 198 199 ArenaList& sweptArenas() { return linked; } 200 }; 201 202 enum class ShouldCheckThresholds { 203 DontCheckThresholds = 0, 204 CheckThresholds = 1 205 }; 206 207 // For each arena kind its free list is represented as the first span with free 208 // things. Initially all the spans are initialized as empty. After we find a new 209 // arena with available things we move its first free span into the list and set 210 // the arena as fully allocated. That way we do not need to update the arena 211 // after the initial allocation. When starting the GC we only move the head of 212 // the of the list of spans back to the arena only for the arena that was not 213 // fully allocated. 214 class FreeLists { 215 AllAllocKindArray<FreeSpan*> freeLists_; 216 217 public: 218 // Because the JITs can allocate from the free lists, they cannot be null. 219 // We use a placeholder FreeSpan that is empty (and wihout an associated 220 // Arena) so the JITs can fall back gracefully. 221 static FreeSpan emptySentinel; 222 223 FreeLists(); 224 225 #ifdef DEBUG 226 inline bool allEmpty() const; 227 inline bool isEmpty(AllocKind kind) const; 228 #endif 229 230 inline void clear(); 231 232 MOZ_ALWAYS_INLINE TenuredCell* allocate(AllocKind kind); 233 234 inline void* setArenaAndAllocate(Arena* arena, AllocKind kind); 235 236 inline void unmarkPreMarkedFreeCells(AllocKind kind); 237 238 FreeSpan** addressOfFreeList(AllocKind thingKind) { 239 return &freeLists_[thingKind]; 240 } 241 }; 242 243 class ArenaLists { 244 enum class ConcurrentUse : uint32_t { 245 None, 246 BackgroundFinalize, 247 BackgroundFinalizeFinished 248 }; 249 250 using ConcurrentUseState = mozilla::Atomic<ConcurrentUse, mozilla::Relaxed>; 251 252 JS::Zone* zone_; 253 254 // Whether this structure can be accessed by other threads. 255 UnprotectedData<AllAllocKindArray<ConcurrentUseState>> concurrentUseState_; 256 257 MainThreadData<FreeLists> freeLists_; 258 259 /* The main list of arenas for each alloc kind. */ 260 MainThreadOrGCTaskData<AllAllocKindArray<ArenaList>> arenaLists_; 261 262 /* 263 * Arenas which are currently being collected. The collector can move arenas 264 * from arenaLists_ here and back again at various points in collection. 265 */ 266 MainThreadOrGCTaskData<AllAllocKindArray<ArenaList>> collectingArenaLists_; 267 268 // Arena lists which have yet to be swept, but need additional foreground 269 // processing before they are swept. 270 MainThreadData<Arena*> gcCompactPropMapArenasToUpdate; 271 MainThreadData<Arena*> gcNormalPropMapArenasToUpdate; 272 273 // The list of empty arenas which are collected during the sweep phase and 274 // released at the end of sweeping every sweep group. 275 MainThreadOrGCTaskData<Arena*> savedEmptyArenas; 276 277 public: 278 explicit ArenaLists(JS::Zone* zone); 279 ~ArenaLists(); 280 281 FreeLists& freeLists() { return freeLists_.ref(); } 282 const FreeLists& freeLists() const { return freeLists_.ref(); } 283 284 FreeSpan** addressOfFreeList(AllocKind thingKind) { 285 return freeLists_.refNoCheck().addressOfFreeList(thingKind); 286 } 287 288 inline Arena* getFirstArena(AllocKind thingKind) const; 289 inline Arena* getFirstCollectingArena(AllocKind thingKind) const; 290 291 inline bool arenaListsAreEmpty() const; 292 293 inline bool doneBackgroundFinalize(AllocKind kind) const; 294 295 /* Clear the free lists so we won't try to allocate from swept arenas. */ 296 inline void clearFreeLists(); 297 298 inline void unmarkPreMarkedFreeCells(); 299 300 MOZ_ALWAYS_INLINE TenuredCell* allocateFromFreeList(AllocKind thingKind); 301 302 inline void checkEmptyFreeLists(); 303 inline void checkEmptyArenaLists(); 304 inline void checkEmptyFreeList(AllocKind kind); 305 306 void checkEmptyArenaList(AllocKind kind); 307 308 bool relocateArenas(Arena*& relocatedListOut, JS::GCReason reason, 309 JS::SliceBudget& sliceBudget, gcstats::Statistics& stats); 310 311 void queueForegroundObjectsForSweep(JS::GCContext* gcx); 312 void queueForegroundThingsForSweep(); 313 314 bool foregroundFinalize(JS::GCContext* gcx, AllocKind thingKind, 315 JS::SliceBudget& sliceBudget, 316 SortedArenaList& sweepList); 317 template <ReleaseEmpty releaseEmpty> 318 void backgroundFinalize(JS::GCContext* gcx, AllocKind kind, 319 Arena** empty = nullptr); 320 321 Arena* takeSweptEmptyArenas(); 322 323 void mergeBackgroundSweptArenas(); 324 void maybeMergeSweptArenas(AllocKind thingKind); 325 void mergeSweptArenas(AllocKind thingKind, ArenaList& sweptArenas); 326 327 void moveArenasToCollectingLists(); 328 void mergeArenasFromCollectingLists(); 329 330 void checkGCStateNotInUse(); 331 void checkSweepStateNotInUse(); 332 void checkNoArenasToUpdate(); 333 void checkNoArenasToUpdateForKind(AllocKind kind); 334 335 private: 336 ArenaList& arenaList(AllocKind i) { return arenaLists_.ref()[i]; } 337 const ArenaList& arenaList(AllocKind i) const { return arenaLists_.ref()[i]; } 338 339 ArenaList& collectingArenaList(AllocKind i) { 340 return collectingArenaLists_.ref()[i]; 341 } 342 const ArenaList& collectingArenaList(AllocKind i) const { 343 return collectingArenaLists_.ref()[i]; 344 } 345 346 ConcurrentUseState& concurrentUse(AllocKind i) { 347 return concurrentUseState_.ref()[i]; 348 } 349 ConcurrentUse concurrentUse(AllocKind i) const { 350 return concurrentUseState_.ref()[i]; 351 } 352 353 inline JSRuntime* runtime(); 354 inline JSRuntime* runtimeFromAnyThread(); 355 356 void initBackgroundSweep(AllocKind thingKind); 357 358 void* refillFreeListAndAllocate(AllocKind thingKind, 359 ShouldCheckThresholds checkThresholds, 360 StallAndRetry stallAndRetry); 361 362 friend class ArenaIter; 363 friend class ArenaIterInGC; 364 friend class BackgroundUnmarkTask; 365 friend class GCRuntime; 366 friend class js::Nursery; 367 friend class TenuringTracer; 368 }; 369 370 } /* namespace gc */ 371 } /* namespace js */ 372 373 #endif /* gc_ArenaList_h */