tor-browser

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

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