js/src/jsgc.h

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     2  * vim: set ts=8 sts=4 et sw=4 tw=99:
     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/. */
     7 /* JS Garbage Collector. */
     9 #ifndef jsgc_h
    10 #define jsgc_h
    12 #include "mozilla/DebugOnly.h"
    13 #include "mozilla/MemoryReporting.h"
    15 #include "jslock.h"
    16 #include "jsobj.h"
    18 #include "js/GCAPI.h"
    19 #include "js/SliceBudget.h"
    20 #include "js/Vector.h"
    22 class JSAtom;
    23 struct JSCompartment;
    24 class JSFlatString;
    25 class JSLinearString;
    27 namespace js {
    29 class ArgumentsObject;
    30 class ArrayBufferObject;
    31 class ArrayBufferViewObject;
    32 class SharedArrayBufferObject;
    33 class BaseShape;
    34 class DebugScopeObject;
    35 class GCHelperThread;
    36 class GlobalObject;
    37 class LazyScript;
    38 class Nursery;
    39 class PropertyName;
    40 class ScopeObject;
    41 class Shape;
    42 class UnownedBaseShape;
    44 unsigned GetCPUCount();
    46 enum HeapState {
    47     Idle,             // doing nothing with the GC heap
    48     Tracing,          // tracing the GC heap without collecting, e.g. IterateCompartments()
    49     MajorCollecting,  // doing a GC of the major heap
    50     MinorCollecting   // doing a GC of the minor heap (nursery)
    51 };
    53 namespace jit {
    54     class JitCode;
    55 }
    57 namespace gc {
    59 enum State {
    60     NO_INCREMENTAL,
    61     MARK_ROOTS,
    62     MARK,
    63     SWEEP,
    64     INVALID
    65 };
    67 class ChunkPool {
    68     Chunk   *emptyChunkListHead;
    69     size_t  emptyCount;
    71   public:
    72     ChunkPool()
    73       : emptyChunkListHead(nullptr),
    74         emptyCount(0) { }
    76     size_t getEmptyCount() const {
    77         return emptyCount;
    78     }
    80     inline bool wantBackgroundAllocation(JSRuntime *rt) const;
    82     /* Must be called with the GC lock taken. */
    83     inline Chunk *get(JSRuntime *rt);
    85     /* Must be called either during the GC or with the GC lock taken. */
    86     inline void put(Chunk *chunk);
    88     /*
    89      * Return the list of chunks that can be released outside the GC lock.
    90      * Must be called either during the GC or with the GC lock taken.
    91      */
    92     Chunk *expire(JSRuntime *rt, bool releaseAll);
    94     /* Must be called with the GC lock taken. */
    95     void expireAndFree(JSRuntime *rt, bool releaseAll);
    96 };
    98 static inline JSGCTraceKind
    99 MapAllocToTraceKind(AllocKind kind)
   100 {
   101     static const JSGCTraceKind map[] = {
   102         JSTRACE_OBJECT,     /* FINALIZE_OBJECT0 */
   103         JSTRACE_OBJECT,     /* FINALIZE_OBJECT0_BACKGROUND */
   104         JSTRACE_OBJECT,     /* FINALIZE_OBJECT2 */
   105         JSTRACE_OBJECT,     /* FINALIZE_OBJECT2_BACKGROUND */
   106         JSTRACE_OBJECT,     /* FINALIZE_OBJECT4 */
   107         JSTRACE_OBJECT,     /* FINALIZE_OBJECT4_BACKGROUND */
   108         JSTRACE_OBJECT,     /* FINALIZE_OBJECT8 */
   109         JSTRACE_OBJECT,     /* FINALIZE_OBJECT8_BACKGROUND */
   110         JSTRACE_OBJECT,     /* FINALIZE_OBJECT12 */
   111         JSTRACE_OBJECT,     /* FINALIZE_OBJECT12_BACKGROUND */
   112         JSTRACE_OBJECT,     /* FINALIZE_OBJECT16 */
   113         JSTRACE_OBJECT,     /* FINALIZE_OBJECT16_BACKGROUND */
   114         JSTRACE_SCRIPT,     /* FINALIZE_SCRIPT */
   115         JSTRACE_LAZY_SCRIPT,/* FINALIZE_LAZY_SCRIPT */
   116         JSTRACE_SHAPE,      /* FINALIZE_SHAPE */
   117         JSTRACE_BASE_SHAPE, /* FINALIZE_BASE_SHAPE */
   118         JSTRACE_TYPE_OBJECT,/* FINALIZE_TYPE_OBJECT */
   119         JSTRACE_STRING,     /* FINALIZE_FAT_INLINE_STRING */
   120         JSTRACE_STRING,     /* FINALIZE_STRING */
   121         JSTRACE_STRING,     /* FINALIZE_EXTERNAL_STRING */
   122         JSTRACE_JITCODE,    /* FINALIZE_JITCODE */
   123     };
   124     JS_STATIC_ASSERT(JS_ARRAY_LENGTH(map) == FINALIZE_LIMIT);
   125     return map[kind];
   126 }
   128 template <typename T> struct MapTypeToTraceKind {};
   129 template <> struct MapTypeToTraceKind<ObjectImpl>       { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
   130 template <> struct MapTypeToTraceKind<JSObject>         { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
   131 template <> struct MapTypeToTraceKind<JSFunction>       { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
   132 template <> struct MapTypeToTraceKind<ArgumentsObject>  { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
   133 template <> struct MapTypeToTraceKind<ArrayBufferObject>{ static const JSGCTraceKind kind = JSTRACE_OBJECT; };
   134 template <> struct MapTypeToTraceKind<ArrayBufferViewObject>{ static const JSGCTraceKind kind = JSTRACE_OBJECT; };
   135 template <> struct MapTypeToTraceKind<SharedArrayBufferObject>{ static const JSGCTraceKind kind = JSTRACE_OBJECT; };
   136 template <> struct MapTypeToTraceKind<DebugScopeObject> { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
   137 template <> struct MapTypeToTraceKind<GlobalObject>     { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
   138 template <> struct MapTypeToTraceKind<ScopeObject>      { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
   139 template <> struct MapTypeToTraceKind<JSScript>         { static const JSGCTraceKind kind = JSTRACE_SCRIPT; };
   140 template <> struct MapTypeToTraceKind<LazyScript>       { static const JSGCTraceKind kind = JSTRACE_LAZY_SCRIPT; };
   141 template <> struct MapTypeToTraceKind<Shape>            { static const JSGCTraceKind kind = JSTRACE_SHAPE; };
   142 template <> struct MapTypeToTraceKind<BaseShape>        { static const JSGCTraceKind kind = JSTRACE_BASE_SHAPE; };
   143 template <> struct MapTypeToTraceKind<UnownedBaseShape> { static const JSGCTraceKind kind = JSTRACE_BASE_SHAPE; };
   144 template <> struct MapTypeToTraceKind<types::TypeObject>{ static const JSGCTraceKind kind = JSTRACE_TYPE_OBJECT; };
   145 template <> struct MapTypeToTraceKind<JSAtom>           { static const JSGCTraceKind kind = JSTRACE_STRING; };
   146 template <> struct MapTypeToTraceKind<JSString>         { static const JSGCTraceKind kind = JSTRACE_STRING; };
   147 template <> struct MapTypeToTraceKind<JSFlatString>     { static const JSGCTraceKind kind = JSTRACE_STRING; };
   148 template <> struct MapTypeToTraceKind<JSLinearString>   { static const JSGCTraceKind kind = JSTRACE_STRING; };
   149 template <> struct MapTypeToTraceKind<PropertyName>     { static const JSGCTraceKind kind = JSTRACE_STRING; };
   150 template <> struct MapTypeToTraceKind<jit::JitCode>     { static const JSGCTraceKind kind = JSTRACE_JITCODE; };
   152 /* Map from C++ type to finalize kind. JSObject does not have a 1:1 mapping, so must use Arena::thingSize. */
   153 template <typename T> struct MapTypeToFinalizeKind {};
   154 template <> struct MapTypeToFinalizeKind<JSScript>          { static const AllocKind kind = FINALIZE_SCRIPT; };
   155 template <> struct MapTypeToFinalizeKind<LazyScript>        { static const AllocKind kind = FINALIZE_LAZY_SCRIPT; };
   156 template <> struct MapTypeToFinalizeKind<Shape>             { static const AllocKind kind = FINALIZE_SHAPE; };
   157 template <> struct MapTypeToFinalizeKind<BaseShape>         { static const AllocKind kind = FINALIZE_BASE_SHAPE; };
   158 template <> struct MapTypeToFinalizeKind<types::TypeObject> { static const AllocKind kind = FINALIZE_TYPE_OBJECT; };
   159 template <> struct MapTypeToFinalizeKind<JSFatInlineString> { static const AllocKind kind = FINALIZE_FAT_INLINE_STRING; };
   160 template <> struct MapTypeToFinalizeKind<JSString>          { static const AllocKind kind = FINALIZE_STRING; };
   161 template <> struct MapTypeToFinalizeKind<JSExternalString>  { static const AllocKind kind = FINALIZE_EXTERNAL_STRING; };
   162 template <> struct MapTypeToFinalizeKind<jit::JitCode>      { static const AllocKind kind = FINALIZE_JITCODE; };
   164 #if defined(JSGC_GENERATIONAL) || defined(DEBUG)
   165 static inline bool
   166 IsNurseryAllocable(AllocKind kind)
   167 {
   168     JS_ASSERT(kind >= 0 && unsigned(kind) < FINALIZE_LIMIT);
   169     static const bool map[] = {
   170         false,     /* FINALIZE_OBJECT0 */
   171         true,      /* FINALIZE_OBJECT0_BACKGROUND */
   172         false,     /* FINALIZE_OBJECT2 */
   173         true,      /* FINALIZE_OBJECT2_BACKGROUND */
   174         false,     /* FINALIZE_OBJECT4 */
   175         true,      /* FINALIZE_OBJECT4_BACKGROUND */
   176         false,     /* FINALIZE_OBJECT8 */
   177         true,      /* FINALIZE_OBJECT8_BACKGROUND */
   178         false,     /* FINALIZE_OBJECT12 */
   179         true,      /* FINALIZE_OBJECT12_BACKGROUND */
   180         false,     /* FINALIZE_OBJECT16 */
   181         true,      /* FINALIZE_OBJECT16_BACKGROUND */
   182         false,     /* FINALIZE_SCRIPT */
   183         false,     /* FINALIZE_LAZY_SCRIPT */
   184         false,     /* FINALIZE_SHAPE */
   185         false,     /* FINALIZE_BASE_SHAPE */
   186         false,     /* FINALIZE_TYPE_OBJECT */
   187         false,     /* FINALIZE_FAT_INLINE_STRING */
   188         false,     /* FINALIZE_STRING */
   189         false,     /* FINALIZE_EXTERNAL_STRING */
   190         false,     /* FINALIZE_JITCODE */
   191     };
   192     JS_STATIC_ASSERT(JS_ARRAY_LENGTH(map) == FINALIZE_LIMIT);
   193     return map[kind];
   194 }
   195 #endif
   197 static inline bool
   198 IsBackgroundFinalized(AllocKind kind)
   199 {
   200     JS_ASSERT(kind >= 0 && unsigned(kind) < FINALIZE_LIMIT);
   201     static const bool map[] = {
   202         false,     /* FINALIZE_OBJECT0 */
   203         true,      /* FINALIZE_OBJECT0_BACKGROUND */
   204         false,     /* FINALIZE_OBJECT2 */
   205         true,      /* FINALIZE_OBJECT2_BACKGROUND */
   206         false,     /* FINALIZE_OBJECT4 */
   207         true,      /* FINALIZE_OBJECT4_BACKGROUND */
   208         false,     /* FINALIZE_OBJECT8 */
   209         true,      /* FINALIZE_OBJECT8_BACKGROUND */
   210         false,     /* FINALIZE_OBJECT12 */
   211         true,      /* FINALIZE_OBJECT12_BACKGROUND */
   212         false,     /* FINALIZE_OBJECT16 */
   213         true,      /* FINALIZE_OBJECT16_BACKGROUND */
   214         false,     /* FINALIZE_SCRIPT */
   215         false,     /* FINALIZE_LAZY_SCRIPT */
   216         true,      /* FINALIZE_SHAPE */
   217         true,      /* FINALIZE_BASE_SHAPE */
   218         true,      /* FINALIZE_TYPE_OBJECT */
   219         true,      /* FINALIZE_FAT_INLINE_STRING */
   220         true,      /* FINALIZE_STRING */
   221         false,     /* FINALIZE_EXTERNAL_STRING */
   222         false,     /* FINALIZE_JITCODE */
   223     };
   224     JS_STATIC_ASSERT(JS_ARRAY_LENGTH(map) == FINALIZE_LIMIT);
   225     return map[kind];
   226 }
   228 static inline bool
   229 CanBeFinalizedInBackground(gc::AllocKind kind, const Class *clasp)
   230 {
   231     JS_ASSERT(kind <= gc::FINALIZE_OBJECT_LAST);
   232     /* If the class has no finalizer or a finalizer that is safe to call on
   233      * a different thread, we change the finalize kind. For example,
   234      * FINALIZE_OBJECT0 calls the finalizer on the main thread,
   235      * FINALIZE_OBJECT0_BACKGROUND calls the finalizer on the gcHelperThread.
   236      * IsBackgroundFinalized is called to prevent recursively incrementing
   237      * the finalize kind; kind may already be a background finalize kind.
   238      */
   239     return (!gc::IsBackgroundFinalized(kind) &&
   240             (!clasp->finalize || (clasp->flags & JSCLASS_BACKGROUND_FINALIZE)));
   241 }
   243 inline JSGCTraceKind
   244 GetGCThingTraceKind(const void *thing);
   246 /* Capacity for slotsToThingKind */
   247 const size_t SLOTS_TO_THING_KIND_LIMIT = 17;
   249 extern const AllocKind slotsToThingKind[];
   251 /* Get the best kind to use when making an object with the given slot count. */
   252 static inline AllocKind
   253 GetGCObjectKind(size_t numSlots)
   254 {
   255     if (numSlots >= SLOTS_TO_THING_KIND_LIMIT)
   256         return FINALIZE_OBJECT16;
   257     return slotsToThingKind[numSlots];
   258 }
   260 /* As for GetGCObjectKind, but for dense array allocation. */
   261 static inline AllocKind
   262 GetGCArrayKind(size_t numSlots)
   263 {
   264     /*
   265      * Dense arrays can use their fixed slots to hold their elements array
   266      * (less two Values worth of ObjectElements header), but if more than the
   267      * maximum number of fixed slots is needed then the fixed slots will be
   268      * unused.
   269      */
   270     JS_STATIC_ASSERT(ObjectElements::VALUES_PER_HEADER == 2);
   271     if (numSlots > JSObject::NELEMENTS_LIMIT || numSlots + 2 >= SLOTS_TO_THING_KIND_LIMIT)
   272         return FINALIZE_OBJECT2;
   273     return slotsToThingKind[numSlots + 2];
   274 }
   276 static inline AllocKind
   277 GetGCObjectFixedSlotsKind(size_t numFixedSlots)
   278 {
   279     JS_ASSERT(numFixedSlots < SLOTS_TO_THING_KIND_LIMIT);
   280     return slotsToThingKind[numFixedSlots];
   281 }
   283 static inline AllocKind
   284 GetBackgroundAllocKind(AllocKind kind)
   285 {
   286     JS_ASSERT(!IsBackgroundFinalized(kind));
   287     JS_ASSERT(kind <= FINALIZE_OBJECT_LAST);
   288     return (AllocKind) (kind + 1);
   289 }
   291 /*
   292  * Try to get the next larger size for an object, keeping BACKGROUND
   293  * consistent.
   294  */
   295 static inline bool
   296 TryIncrementAllocKind(AllocKind *kindp)
   297 {
   298     size_t next = size_t(*kindp) + 2;
   299     if (next >= size_t(FINALIZE_OBJECT_LIMIT))
   300         return false;
   301     *kindp = AllocKind(next);
   302     return true;
   303 }
   305 /* Get the number of fixed slots and initial capacity associated with a kind. */
   306 static inline size_t
   307 GetGCKindSlots(AllocKind thingKind)
   308 {
   309     /* Using a switch in hopes that thingKind will usually be a compile-time constant. */
   310     switch (thingKind) {
   311       case FINALIZE_OBJECT0:
   312       case FINALIZE_OBJECT0_BACKGROUND:
   313         return 0;
   314       case FINALIZE_OBJECT2:
   315       case FINALIZE_OBJECT2_BACKGROUND:
   316         return 2;
   317       case FINALIZE_OBJECT4:
   318       case FINALIZE_OBJECT4_BACKGROUND:
   319         return 4;
   320       case FINALIZE_OBJECT8:
   321       case FINALIZE_OBJECT8_BACKGROUND:
   322         return 8;
   323       case FINALIZE_OBJECT12:
   324       case FINALIZE_OBJECT12_BACKGROUND:
   325         return 12;
   326       case FINALIZE_OBJECT16:
   327       case FINALIZE_OBJECT16_BACKGROUND:
   328         return 16;
   329       default:
   330         MOZ_ASSUME_UNREACHABLE("Bad object finalize kind");
   331     }
   332 }
   334 static inline size_t
   335 GetGCKindSlots(AllocKind thingKind, const Class *clasp)
   336 {
   337     size_t nslots = GetGCKindSlots(thingKind);
   339     /* An object's private data uses the space taken by its last fixed slot. */
   340     if (clasp->flags & JSCLASS_HAS_PRIVATE) {
   341         JS_ASSERT(nslots > 0);
   342         nslots--;
   343     }
   345     /*
   346      * Functions have a larger finalize kind than FINALIZE_OBJECT to reserve
   347      * space for the extra fields in JSFunction, but have no fixed slots.
   348      */
   349     if (clasp == FunctionClassPtr)
   350         nslots = 0;
   352     return nslots;
   353 }
   355 /*
   356  * ArenaList::head points to the start of the list. Normally cursor points
   357  * to the first arena in the list with some free things and all arenas
   358  * before cursor are fully allocated. However, as the arena currently being
   359  * allocated from is considered full while its list of free spans is moved
   360  * into the freeList, during the GC or cell enumeration, when an
   361  * unallocated freeList is moved back to the arena, we can see an arena
   362  * with some free cells before the cursor. The cursor is an indirect
   363  * pointer to allow for efficient list insertion at the cursor point and
   364  * other list manipulations.
   365  */
   366 struct ArenaList {
   367     ArenaHeader     *head;
   368     ArenaHeader     **cursor;
   370     ArenaList() {
   371         clear();
   372     }
   374     void clear() {
   375         head = nullptr;
   376         cursor = &head;
   377     }
   379     void insert(ArenaHeader *arena);
   380 };
   382 class ArenaLists
   383 {
   384     /*
   385      * For each arena kind its free list is represented as the first span with
   386      * free things. Initially all the spans are initialized as empty. After we
   387      * find a new arena with available things we move its first free span into
   388      * the list and set the arena as fully allocated. way we do not need to
   389      * update the arena header after the initial allocation. When starting the
   390      * GC we only move the head of the of the list of spans back to the arena
   391      * only for the arena that was not fully allocated.
   392      */
   393     FreeSpan       freeLists[FINALIZE_LIMIT];
   395     ArenaList      arenaLists[FINALIZE_LIMIT];
   397     /*
   398      * The background finalization adds the finalized arenas to the list at
   399      * the *cursor position. backgroundFinalizeState controls the interaction
   400      * between the GC lock and the access to the list from the allocation
   401      * thread.
   402      *
   403      * BFS_DONE indicates that the finalizations is not running or cannot
   404      * affect this arena list. The allocation thread can access the list
   405      * outside the GC lock.
   406      *
   407      * In BFS_RUN and BFS_JUST_FINISHED the allocation thread must take the
   408      * lock. The former indicates that the finalization still runs. The latter
   409      * signals that finalization just added to the list finalized arenas. In
   410      * that case the lock effectively serves as a read barrier to ensure that
   411      * the allocation thread see all the writes done during finalization.
   412      */
   413     enum BackgroundFinalizeState {
   414         BFS_DONE,
   415         BFS_RUN,
   416         BFS_JUST_FINISHED
   417     };
   419     volatile uintptr_t backgroundFinalizeState[FINALIZE_LIMIT];
   421   public:
   422     /* For each arena kind, a list of arenas remaining to be swept. */
   423     ArenaHeader *arenaListsToSweep[FINALIZE_LIMIT];
   425     /* Shape arenas to be swept in the foreground. */
   426     ArenaHeader *gcShapeArenasToSweep;
   428   public:
   429     ArenaLists() {
   430         for (size_t i = 0; i != FINALIZE_LIMIT; ++i)
   431             freeLists[i].initAsEmpty();
   432         for (size_t i = 0; i != FINALIZE_LIMIT; ++i)
   433             backgroundFinalizeState[i] = BFS_DONE;
   434         for (size_t i = 0; i != FINALIZE_LIMIT; ++i)
   435             arenaListsToSweep[i] = nullptr;
   436         gcShapeArenasToSweep = nullptr;
   437     }
   439     ~ArenaLists() {
   440         for (size_t i = 0; i != FINALIZE_LIMIT; ++i) {
   441             /*
   442              * We can only call this during the shutdown after the last GC when
   443              * the background finalization is disabled.
   444              */
   445             JS_ASSERT(backgroundFinalizeState[i] == BFS_DONE);
   446             ArenaHeader **headp = &arenaLists[i].head;
   447             while (ArenaHeader *aheader = *headp) {
   448                 *headp = aheader->next;
   449                 aheader->chunk()->releaseArena(aheader);
   450             }
   451         }
   452     }
   454     static uintptr_t getFreeListOffset(AllocKind thingKind) {
   455         uintptr_t offset = offsetof(ArenaLists, freeLists);
   456         return offset + thingKind * sizeof(FreeSpan);
   457     }
   459     const FreeSpan *getFreeList(AllocKind thingKind) const {
   460         return &freeLists[thingKind];
   461     }
   463     ArenaHeader *getFirstArena(AllocKind thingKind) const {
   464         return arenaLists[thingKind].head;
   465     }
   467     ArenaHeader *getFirstArenaToSweep(AllocKind thingKind) const {
   468         return arenaListsToSweep[thingKind];
   469     }
   471     bool arenaListsAreEmpty() const {
   472         for (size_t i = 0; i != FINALIZE_LIMIT; ++i) {
   473             /*
   474              * The arena cannot be empty if the background finalization is not yet
   475              * done.
   476              */
   477             if (backgroundFinalizeState[i] != BFS_DONE)
   478                 return false;
   479             if (arenaLists[i].head)
   480                 return false;
   481         }
   482         return true;
   483     }
   485     bool arenasAreFull(AllocKind thingKind) const {
   486         return !*arenaLists[thingKind].cursor;
   487     }
   489     void unmarkAll() {
   490         for (size_t i = 0; i != FINALIZE_LIMIT; ++i) {
   491             /* The background finalization must have stopped at this point. */
   492             JS_ASSERT(backgroundFinalizeState[i] == BFS_DONE ||
   493                       backgroundFinalizeState[i] == BFS_JUST_FINISHED);
   494             for (ArenaHeader *aheader = arenaLists[i].head; aheader; aheader = aheader->next) {
   495                 uintptr_t *word = aheader->chunk()->bitmap.arenaBits(aheader);
   496                 memset(word, 0, ArenaBitmapWords * sizeof(uintptr_t));
   497             }
   498         }
   499     }
   501     bool doneBackgroundFinalize(AllocKind kind) const {
   502         return backgroundFinalizeState[kind] == BFS_DONE ||
   503                backgroundFinalizeState[kind] == BFS_JUST_FINISHED;
   504     }
   506     bool needBackgroundFinalizeWait(AllocKind kind) const {
   507         return backgroundFinalizeState[kind] != BFS_DONE;
   508     }
   510     /*
   511      * Return the free list back to the arena so the GC finalization will not
   512      * run the finalizers over unitialized bytes from free things.
   513      */
   514     void purge() {
   515         for (size_t i = 0; i != FINALIZE_LIMIT; ++i)
   516             purge(AllocKind(i));
   517     }
   519     void purge(AllocKind i) {
   520         FreeSpan *headSpan = &freeLists[i];
   521         if (!headSpan->isEmpty()) {
   522             ArenaHeader *aheader = headSpan->arenaHeader();
   523             aheader->setFirstFreeSpan(headSpan);
   524             headSpan->initAsEmpty();
   525         }
   526     }
   528     inline void prepareForIncrementalGC(JSRuntime *rt);
   530     /*
   531      * Temporarily copy the free list heads to the arenas so the code can see
   532      * the proper value in ArenaHeader::freeList when accessing the latter
   533      * outside the GC.
   534      */
   535     void copyFreeListsToArenas() {
   536         for (size_t i = 0; i != FINALIZE_LIMIT; ++i)
   537             copyFreeListToArena(AllocKind(i));
   538     }
   540     void copyFreeListToArena(AllocKind thingKind) {
   541         FreeSpan *headSpan = &freeLists[thingKind];
   542         if (!headSpan->isEmpty()) {
   543             ArenaHeader *aheader = headSpan->arenaHeader();
   544             JS_ASSERT(!aheader->hasFreeThings());
   545             aheader->setFirstFreeSpan(headSpan);
   546         }
   547     }
   549     /*
   550      * Clear the free lists in arenas that were temporarily set there using
   551      * copyToArenas.
   552      */
   553     void clearFreeListsInArenas() {
   554         for (size_t i = 0; i != FINALIZE_LIMIT; ++i)
   555             clearFreeListInArena(AllocKind(i));
   556     }
   559     void clearFreeListInArena(AllocKind kind) {
   560         FreeSpan *headSpan = &freeLists[kind];
   561         if (!headSpan->isEmpty()) {
   562             ArenaHeader *aheader = headSpan->arenaHeader();
   563             JS_ASSERT(aheader->getFirstFreeSpan().isSameNonEmptySpan(headSpan));
   564             aheader->setAsFullyUsed();
   565         }
   566     }
   568     /*
   569      * Check that the free list is either empty or were synchronized with the
   570      * arena using copyToArena().
   571      */
   572     bool isSynchronizedFreeList(AllocKind kind) {
   573         FreeSpan *headSpan = &freeLists[kind];
   574         if (headSpan->isEmpty())
   575             return true;
   576         ArenaHeader *aheader = headSpan->arenaHeader();
   577         if (aheader->hasFreeThings()) {
   578             /*
   579              * If the arena has a free list, it must be the same as one in
   580              * lists.
   581              */
   582             JS_ASSERT(aheader->getFirstFreeSpan().isSameNonEmptySpan(headSpan));
   583             return true;
   584         }
   585         return false;
   586     }
   588     MOZ_ALWAYS_INLINE void *allocateFromFreeList(AllocKind thingKind, size_t thingSize) {
   589         return freeLists[thingKind].allocate(thingSize);
   590     }
   592     template <AllowGC allowGC>
   593     static void *refillFreeList(ThreadSafeContext *cx, AllocKind thingKind);
   595     /*
   596      * Moves all arenas from |fromArenaLists| into |this|.  In
   597      * parallel blocks, we temporarily create one ArenaLists per
   598      * parallel thread.  When the parallel block ends, we move
   599      * whatever allocations may have been performed back into the
   600      * compartment's main arena list using this function.
   601      */
   602     void adoptArenas(JSRuntime *runtime, ArenaLists *fromArenaLists);
   604     /* True if the ArenaHeader in question is found in this ArenaLists */
   605     bool containsArena(JSRuntime *runtime, ArenaHeader *arenaHeader);
   607     void checkEmptyFreeLists() {
   608 #ifdef DEBUG
   609         for (size_t i = 0; i < mozilla::ArrayLength(freeLists); ++i)
   610             JS_ASSERT(freeLists[i].isEmpty());
   611 #endif
   612     }
   614     void checkEmptyFreeList(AllocKind kind) {
   615         JS_ASSERT(freeLists[kind].isEmpty());
   616     }
   618     void queueObjectsForSweep(FreeOp *fop);
   619     void queueStringsForSweep(FreeOp *fop);
   620     void queueShapesForSweep(FreeOp *fop);
   621     void queueScriptsForSweep(FreeOp *fop);
   622     void queueJitCodeForSweep(FreeOp *fop);
   624     bool foregroundFinalize(FreeOp *fop, AllocKind thingKind, SliceBudget &sliceBudget);
   625     static void backgroundFinalize(FreeOp *fop, ArenaHeader *listHead, bool onBackgroundThread);
   627     void wipeDuringParallelExecution(JSRuntime *rt);
   629   private:
   630     inline void finalizeNow(FreeOp *fop, AllocKind thingKind);
   631     inline void forceFinalizeNow(FreeOp *fop, AllocKind thingKind);
   632     inline void queueForForegroundSweep(FreeOp *fop, AllocKind thingKind);
   633     inline void queueForBackgroundSweep(FreeOp *fop, AllocKind thingKind);
   635     void *allocateFromArena(JS::Zone *zone, AllocKind thingKind);
   636     inline void *allocateFromArenaInline(JS::Zone *zone, AllocKind thingKind);
   638     inline void normalizeBackgroundFinalizeState(AllocKind thingKind);
   640     friend class js::Nursery;
   641 };
   643 /*
   644  * Initial allocation size for data structures holding chunks is set to hold
   645  * chunks with total capacity of 16MB to avoid buffer resizes during browser
   646  * startup.
   647  */
   648 const size_t INITIAL_CHUNK_CAPACITY = 16 * 1024 * 1024 / ChunkSize;
   650 /* The number of GC cycles an empty chunk can survive before been released. */
   651 const size_t MAX_EMPTY_CHUNK_AGE = 4;
   653 } /* namespace gc */
   655 typedef enum JSGCRootType {
   656     JS_GC_ROOT_VALUE_PTR,
   657     JS_GC_ROOT_STRING_PTR,
   658     JS_GC_ROOT_OBJECT_PTR,
   659     JS_GC_ROOT_SCRIPT_PTR
   660 } JSGCRootType;
   662 struct RootInfo {
   663     RootInfo() {}
   664     RootInfo(const char *name, JSGCRootType type) : name(name), type(type) {}
   665     const char *name;
   666     JSGCRootType type;
   667 };
   669 typedef js::HashMap<void *,
   670                     RootInfo,
   671                     js::DefaultHasher<void *>,
   672                     js::SystemAllocPolicy> RootedValueMap;
   674 extern bool
   675 AddValueRoot(JSContext *cx, js::Value *vp, const char *name);
   677 extern bool
   678 AddValueRootRT(JSRuntime *rt, js::Value *vp, const char *name);
   680 extern bool
   681 AddStringRoot(JSContext *cx, JSString **rp, const char *name);
   683 extern bool
   684 AddObjectRoot(JSContext *cx, JSObject **rp, const char *name);
   686 extern bool
   687 AddObjectRoot(JSRuntime *rt, JSObject **rp, const char *name);
   689 extern bool
   690 AddScriptRoot(JSContext *cx, JSScript **rp, const char *name);
   692 extern void
   693 RemoveRoot(JSRuntime *rt, void *rp);
   695 } /* namespace js */
   697 extern bool
   698 js_InitGC(JSRuntime *rt, uint32_t maxbytes);
   700 extern void
   701 js_FinishGC(JSRuntime *rt);
   703 namespace js {
   705 class InterpreterFrame;
   707 extern void
   708 MarkCompartmentActive(js::InterpreterFrame *fp);
   710 extern void
   711 TraceRuntime(JSTracer *trc);
   713 /* Must be called with GC lock taken. */
   714 extern bool
   715 TriggerGC(JSRuntime *rt, JS::gcreason::Reason reason);
   717 /* Must be called with GC lock taken. */
   718 extern bool
   719 TriggerZoneGC(Zone *zone, JS::gcreason::Reason reason);
   721 extern void
   722 MaybeGC(JSContext *cx);
   724 extern void
   725 ReleaseAllJITCode(FreeOp *op);
   727 /*
   728  * Kinds of js_GC invocation.
   729  */
   730 typedef enum JSGCInvocationKind {
   731     /* Normal invocation. */
   732     GC_NORMAL           = 0,
   734     /* Minimize GC triggers and release empty GC chunks right away. */
   735     GC_SHRINK             = 1
   736 } JSGCInvocationKind;
   738 extern void
   739 GC(JSRuntime *rt, JSGCInvocationKind gckind, JS::gcreason::Reason reason);
   741 extern void
   742 GCSlice(JSRuntime *rt, JSGCInvocationKind gckind, JS::gcreason::Reason reason, int64_t millis = 0);
   744 extern void
   745 GCFinalSlice(JSRuntime *rt, JSGCInvocationKind gckind, JS::gcreason::Reason reason);
   747 extern void
   748 GCDebugSlice(JSRuntime *rt, bool limit, int64_t objCount);
   750 extern void
   751 PrepareForDebugGC(JSRuntime *rt);
   753 extern void
   754 MinorGC(JSRuntime *rt, JS::gcreason::Reason reason);
   756 extern void
   757 MinorGC(JSContext *cx, JS::gcreason::Reason reason);
   759 #ifdef JS_GC_ZEAL
   760 extern void
   761 SetGCZeal(JSRuntime *rt, uint8_t zeal, uint32_t frequency);
   762 #endif
   764 /* Functions for managing cross compartment gray pointers. */
   766 extern void
   767 DelayCrossCompartmentGrayMarking(JSObject *src);
   769 extern void
   770 NotifyGCNukeWrapper(JSObject *o);
   772 extern unsigned
   773 NotifyGCPreSwap(JSObject *a, JSObject *b);
   775 extern void
   776 NotifyGCPostSwap(JSObject *a, JSObject *b, unsigned preResult);
   778 /*
   779  * Helper that implements sweeping and allocation for kinds that can be swept
   780  * and allocated off the main thread.
   781  *
   782  * In non-threadsafe builds, all actual sweeping and allocation is performed
   783  * on the main thread, but GCHelperThread encapsulates this from clients as
   784  * much as possible.
   785  */
   786 class GCHelperThread {
   787     enum State {
   788         IDLE,
   789         SWEEPING,
   790         ALLOCATING,
   791         CANCEL_ALLOCATION,
   792         SHUTDOWN
   793     };
   795     /*
   796      * During the finalization we do not free immediately. Rather we add the
   797      * corresponding pointers to a buffer which we later release on a
   798      * separated thread.
   799      *
   800      * The buffer is implemented as a vector of 64K arrays of pointers, not as
   801      * a simple vector, to avoid realloc calls during the vector growth and to
   802      * not bloat the binary size of the inlined freeLater method. Any OOM
   803      * during buffer growth results in the pointer being freed immediately.
   804      */
   805     static const size_t FREE_ARRAY_SIZE = size_t(1) << 16;
   806     static const size_t FREE_ARRAY_LENGTH = FREE_ARRAY_SIZE / sizeof(void *);
   808     JSRuntime         *const rt;
   809     PRThread          *thread;
   810     PRCondVar         *wakeup;
   811     PRCondVar         *done;
   812     volatile State    state;
   814     void wait(PRCondVar *which);
   816     bool              sweepFlag;
   817     bool              shrinkFlag;
   819     Vector<void **, 16, js::SystemAllocPolicy> freeVector;
   820     void            **freeCursor;
   821     void            **freeCursorEnd;
   823     bool              backgroundAllocation;
   825     friend class js::gc::ArenaLists;
   827     void
   828     replenishAndFreeLater(void *ptr);
   830     static void freeElementsAndArray(void **array, void **end) {
   831         JS_ASSERT(array <= end);
   832         for (void **p = array; p != end; ++p)
   833             js_free(*p);
   834         js_free(array);
   835     }
   837     static void threadMain(void* arg);
   838     void threadLoop();
   840     /* Must be called with the GC lock taken. */
   841     void doSweep();
   843   public:
   844     GCHelperThread(JSRuntime *rt)
   845       : rt(rt),
   846         thread(nullptr),
   847         wakeup(nullptr),
   848         done(nullptr),
   849         state(IDLE),
   850         sweepFlag(false),
   851         shrinkFlag(false),
   852         freeCursor(nullptr),
   853         freeCursorEnd(nullptr),
   854         backgroundAllocation(true)
   855     { }
   857     bool init();
   858     void finish();
   860     /* Must be called with the GC lock taken. */
   861     void startBackgroundSweep(bool shouldShrink);
   863     /* Must be called with the GC lock taken. */
   864     void startBackgroundShrink();
   866     /* Must be called without the GC lock taken. */
   867     void waitBackgroundSweepEnd();
   869     /* Must be called without the GC lock taken. */
   870     void waitBackgroundSweepOrAllocEnd();
   872     /* Must be called with the GC lock taken. */
   873     inline void startBackgroundAllocationIfIdle();
   875     bool canBackgroundAllocate() const {
   876         return backgroundAllocation;
   877     }
   879     void disableBackgroundAllocation() {
   880         backgroundAllocation = false;
   881     }
   883     PRThread *getThread() const {
   884         return thread;
   885     }
   887     bool onBackgroundThread();
   889     /*
   890      * Outside the GC lock may give true answer when in fact the sweeping has
   891      * been done.
   892      */
   893     bool sweeping() const {
   894         return state == SWEEPING;
   895     }
   897     bool shouldShrink() const {
   898         JS_ASSERT(sweeping());
   899         return shrinkFlag;
   900     }
   902     void freeLater(void *ptr) {
   903         JS_ASSERT(!sweeping());
   904         if (freeCursor != freeCursorEnd)
   905             *freeCursor++ = ptr;
   906         else
   907             replenishAndFreeLater(ptr);
   908     }
   909 };
   911 struct GCChunkHasher {
   912     typedef gc::Chunk *Lookup;
   914     /*
   915      * Strip zeros for better distribution after multiplying by the golden
   916      * ratio.
   917      */
   918     static HashNumber hash(gc::Chunk *chunk) {
   919         JS_ASSERT(!(uintptr_t(chunk) & gc::ChunkMask));
   920         return HashNumber(uintptr_t(chunk) >> gc::ChunkShift);
   921     }
   923     static bool match(gc::Chunk *k, gc::Chunk *l) {
   924         JS_ASSERT(!(uintptr_t(k) & gc::ChunkMask));
   925         JS_ASSERT(!(uintptr_t(l) & gc::ChunkMask));
   926         return k == l;
   927     }
   928 };
   930 typedef HashSet<js::gc::Chunk *, GCChunkHasher, SystemAllocPolicy> GCChunkSet;
   932 struct GrayRoot {
   933     void *thing;
   934     JSGCTraceKind kind;
   935 #ifdef DEBUG
   936     JSTraceNamePrinter debugPrinter;
   937     const void *debugPrintArg;
   938     size_t debugPrintIndex;
   939 #endif
   941     GrayRoot(void *thing, JSGCTraceKind kind)
   942         : thing(thing), kind(kind) {}
   943 };
   945 void
   946 MarkStackRangeConservatively(JSTracer *trc, Value *begin, Value *end);
   948 typedef void (*IterateChunkCallback)(JSRuntime *rt, void *data, gc::Chunk *chunk);
   949 typedef void (*IterateZoneCallback)(JSRuntime *rt, void *data, JS::Zone *zone);
   950 typedef void (*IterateArenaCallback)(JSRuntime *rt, void *data, gc::Arena *arena,
   951                                      JSGCTraceKind traceKind, size_t thingSize);
   952 typedef void (*IterateCellCallback)(JSRuntime *rt, void *data, void *thing,
   953                                     JSGCTraceKind traceKind, size_t thingSize);
   955 /*
   956  * This function calls |zoneCallback| on every zone, |compartmentCallback| on
   957  * every compartment, |arenaCallback| on every in-use arena, and |cellCallback|
   958  * on every in-use cell in the GC heap.
   959  */
   960 extern void
   961 IterateZonesCompartmentsArenasCells(JSRuntime *rt, void *data,
   962                                     IterateZoneCallback zoneCallback,
   963                                     JSIterateCompartmentCallback compartmentCallback,
   964                                     IterateArenaCallback arenaCallback,
   965                                     IterateCellCallback cellCallback);
   967 /*
   968  * This function is like IterateZonesCompartmentsArenasCells, but does it for a
   969  * single zone.
   970  */
   971 extern void
   972 IterateZoneCompartmentsArenasCells(JSRuntime *rt, Zone *zone, void *data,
   973                                    IterateZoneCallback zoneCallback,
   974                                    JSIterateCompartmentCallback compartmentCallback,
   975                                    IterateArenaCallback arenaCallback,
   976                                    IterateCellCallback cellCallback);
   978 /*
   979  * Invoke chunkCallback on every in-use chunk.
   980  */
   981 extern void
   982 IterateChunks(JSRuntime *rt, void *data, IterateChunkCallback chunkCallback);
   984 typedef void (*IterateScriptCallback)(JSRuntime *rt, void *data, JSScript *script);
   986 /*
   987  * Invoke scriptCallback on every in-use script for
   988  * the given compartment or for all compartments if it is null.
   989  */
   990 extern void
   991 IterateScripts(JSRuntime *rt, JSCompartment *compartment,
   992                void *data, IterateScriptCallback scriptCallback);
   994 } /* namespace js */
   996 extern void
   997 js_FinalizeStringRT(JSRuntime *rt, JSString *str);
   999 namespace js {
  1001 JSCompartment *
  1002 NewCompartment(JSContext *cx, JS::Zone *zone, JSPrincipals *principals,
  1003                const JS::CompartmentOptions &options);
  1005 namespace gc {
  1007 extern void
  1008 GCIfNeeded(JSContext *cx);
  1010 /* Tries to run a GC no matter what (used for GC zeal). */
  1011 void
  1012 RunDebugGC(JSContext *cx);
  1014 void
  1015 SetDeterministicGC(JSContext *cx, bool enabled);
  1017 void
  1018 SetValidateGC(JSContext *cx, bool enabled);
  1020 void
  1021 SetFullCompartmentChecks(JSContext *cx, bool enabled);
  1023 /* Wait for the background thread to finish sweeping if it is running. */
  1024 void
  1025 FinishBackgroundFinalize(JSRuntime *rt);
  1027 /*
  1028  * Merge all contents of source into target. This can only be used if source is
  1029  * the only compartment in its zone.
  1030  */
  1031 void
  1032 MergeCompartments(JSCompartment *source, JSCompartment *target);
  1034 const int ZealPokeValue = 1;
  1035 const int ZealAllocValue = 2;
  1036 const int ZealFrameGCValue = 3;
  1037 const int ZealVerifierPreValue = 4;
  1038 const int ZealFrameVerifierPreValue = 5;
  1039 const int ZealStackRootingValue = 6;
  1040 const int ZealGenerationalGCValue = 7;
  1041 const int ZealIncrementalRootsThenFinish = 8;
  1042 const int ZealIncrementalMarkAllThenFinish = 9;
  1043 const int ZealIncrementalMultipleSlices = 10;
  1044 const int ZealVerifierPostValue = 11;
  1045 const int ZealFrameVerifierPostValue = 12;
  1046 const int ZealCheckHashTablesOnMinorGC = 13;
  1047 const int ZealLimit = 13;
  1049 enum VerifierType {
  1050     PreBarrierVerifier,
  1051     PostBarrierVerifier
  1052 };
  1054 #ifdef JS_GC_ZEAL
  1056 /* Check that write barriers have been used correctly. See jsgc.cpp. */
  1057 void
  1058 VerifyBarriers(JSRuntime *rt, VerifierType type);
  1060 void
  1061 MaybeVerifyBarriers(JSContext *cx, bool always = false);
  1063 #else
  1065 static inline void
  1066 VerifyBarriers(JSRuntime *rt, VerifierType type)
  1070 static inline void
  1071 MaybeVerifyBarriers(JSContext *cx, bool always = false)
  1075 #endif
  1077 /*
  1078  * Instances of this class set the |JSRuntime::suppressGC| flag for the duration
  1079  * that they are live. Use of this class is highly discouraged. Please carefully
  1080  * read the comment in jscntxt.h above |suppressGC| and take all appropriate
  1081  * precautions before instantiating this class.
  1082  */
  1083 class AutoSuppressGC
  1085     int32_t &suppressGC_;
  1087   public:
  1088     AutoSuppressGC(ExclusiveContext *cx);
  1089     AutoSuppressGC(JSCompartment *comp);
  1090     AutoSuppressGC(JSRuntime *rt);
  1092     ~AutoSuppressGC()
  1094         suppressGC_--;
  1096 };
  1098 #ifdef DEBUG
  1099 /* Disable OOM testing in sections which are not OOM safe. */
  1100 class AutoEnterOOMUnsafeRegion
  1102     uint32_t saved_;
  1104   public:
  1105     AutoEnterOOMUnsafeRegion() : saved_(OOM_maxAllocations) {
  1106         OOM_maxAllocations = UINT32_MAX;
  1108     ~AutoEnterOOMUnsafeRegion() {
  1109         OOM_maxAllocations = saved_;
  1111 };
  1112 #else
  1113 class AutoEnterOOMUnsafeRegion {};
  1114 #endif /* DEBUG */
  1116 } /* namespace gc */
  1118 #ifdef DEBUG
  1119 /* Use this to avoid assertions when manipulating the wrapper map. */
  1120 class AutoDisableProxyCheck
  1122     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER;
  1123     uintptr_t &count;
  1125   public:
  1126     AutoDisableProxyCheck(JSRuntime *rt
  1127                           MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
  1129     ~AutoDisableProxyCheck() {
  1130         count--;
  1132 };
  1133 #else
  1134 struct AutoDisableProxyCheck
  1136     AutoDisableProxyCheck(JSRuntime *rt) {}
  1137 };
  1138 #endif
  1140 void
  1141 PurgeJITCaches(JS::Zone *zone);
  1143 // This is the same as IsInsideNursery, but not inlined.
  1144 bool
  1145 UninlinedIsInsideNursery(JSRuntime *rt, const void *thing);
  1147 } /* namespace js */
  1149 #endif /* jsgc_h */

mercurial