Bug 988326 - Start GC arenas in the decommitted state; r=jonco
☠☠ backed out by 11b3e19f4eb8 ☠ ☠
authorTerrence Cole <terrence@mozilla.com>
Wed, 26 Mar 2014 13:12:45 -0400
changeset 175619 71568f10952ac39b3f2f691712931a27143cd155
parent 175604 1f781e1fe79dfa77f1219cb3cde7a0da953ec27e
child 175620 3f28447213aa3dd8234c27c12034cd3d1ce42156
push id26496
push userkwierso@gmail.com
push dateFri, 28 Mar 2014 02:28:34 +0000
treeherdermozilla-central@3c09159e01da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjonco
bugs988326
milestone31.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 988326 - Start GC arenas in the decommitted state; r=jonco
js/src/gc/Heap.h
js/src/jsgc.cpp
--- a/js/src/gc/Heap.h
+++ b/js/src/gc/Heap.h
@@ -13,16 +13,17 @@
 #include <stddef.h>
 #include <stdint.h>
 
 #include "jspubtd.h"
 #include "jstypes.h"
 #include "jsutil.h"
 
 #include "ds/BitArray.h"
+#include "gc/Memory.h"
 #include "js/HeapAPI.h"
 
 struct JSCompartment;
 
 struct JSRuntime;
 
 namespace JS {
 namespace shadow {
@@ -803,16 +804,26 @@ struct Chunk
 
     ArenaHeader *allocateArena(JS::Zone *zone, AllocKind kind);
 
     void releaseArena(ArenaHeader *aheader);
     void recycleArena(ArenaHeader *aheader, ArenaList &dest, AllocKind thingKind);
 
     static Chunk *allocate(JSRuntime *rt);
 
+    void decommitAllArenas(JSRuntime *rt) {
+        decommittedArenas.clear(true);
+        MarkPagesUnused(rt, &arenas[0], ArenasPerChunk * ArenaSize);
+
+        info.freeArenasHead = nullptr;
+        info.lastDecommittedArenaOffset = 0;
+        info.numArenasFree = ArenasPerChunk;
+        info.numArenasFreeCommitted = 0;
+    }
+
     /* Must be called with the GC lock taken. */
     static inline void release(JSRuntime *rt, Chunk *chunk);
     static inline void releaseList(JSRuntime *rt, Chunk *chunkListHead);
 
     /* Must be called with the GC lock taken. */
     inline void prepareToBeFreed(JSRuntime *rt);
 
     /*
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -662,18 +662,17 @@ ChunkPool::get(JSRuntime *rt)
         JS_ASSERT(emptyCount);
         emptyChunkListHead = chunk->info.next;
         --emptyCount;
     } else {
         JS_ASSERT(!emptyCount);
         chunk = Chunk::allocate(rt);
         if (!chunk)
             return nullptr;
-        JS_ASSERT(chunk->info.numArenasFreeCommitted == ArenasPerChunk);
-        rt->gcNumArenasFreeCommitted += ArenasPerChunk;
+        JS_ASSERT(chunk->info.numArenasFreeCommitted == 0);
     }
     JS_ASSERT(chunk->unused());
     JS_ASSERT(!rt->gcChunkSet.has(chunk));
 
     if (wantBackgroundAllocation(rt))
         rt->gcHelperThread.startBackgroundAllocationIfIdle();
 
     return chunk;
@@ -800,35 +799,26 @@ Chunk::init(JSRuntime *rt)
     JS_POISON(this, JS_FREE_PATTERN, ChunkSize);
 
     /*
      * We clear the bitmap to guard against xpc_IsGrayGCThing being called on
      * uninitialized data, which would happen before the first GC cycle.
      */
     bitmap.clear();
 
-    /* Initialize the arena tracking bitmap. */
-    decommittedArenas.clear(false);
+    /*
+     * Decommit the arenas. We do this after poisoning so that if the OS does
+     * not have to recycle the pages, we still get the benefit of poisoning.
+     */
+    decommitAllArenas(rt);
 
     /* Initialize the chunk info. */
-    info.freeArenasHead = &arenas[0].aheader;
-    info.lastDecommittedArenaOffset = 0;
-    info.numArenasFree = ArenasPerChunk;
-    info.numArenasFreeCommitted = ArenasPerChunk;
     info.age = 0;
     info.trailer.runtime = rt;
 
-    /* Initialize the arena header state. */
-    for (unsigned i = 0; i < ArenasPerChunk; i++) {
-        arenas[i].aheader.setAsNotAllocated();
-        arenas[i].aheader.next = (i + 1 < ArenasPerChunk)
-                                 ? &arenas[i + 1].aheader
-                                 : nullptr;
-    }
-
     /* The rest of info fields are initialized in PickChunk. */
 }
 
 static inline Chunk **
 GetAvailableChunkList(Zone *zone)
 {
     JSRuntime *rt = zone->runtimeFromAnyThread();
     return zone->isSystem
@@ -994,16 +984,18 @@ Chunk::releaseArena(ArenaHeader *aheader
         JS_ASSERT(!info.prevp);
         JS_ASSERT(!info.next);
         addToAvailableList(zone);
     } else if (!unused()) {
         JS_ASSERT(info.prevp);
     } else {
         rt->gcChunkSet.remove(this);
         removeFromAvailableList();
+        JS_ASSERT(info.numArenasFree == ArenasPerChunk);
+        decommitAllArenas(rt);
         rt->gcChunkPool.put(this);
     }
 }
 
 /* The caller must hold the GC lock. */
 static Chunk *
 PickChunk(Zone *zone)
 {
@@ -2617,22 +2609,21 @@ GCHelperThread::threadLoop()
                 {
                     AutoUnlockGC unlock(rt);
                     chunk = Chunk::allocate(rt);
                 }
 
                 /* OOM stops the background allocation. */
                 if (!chunk) {
 #if JS_TRACE_LOGGING
-            logger->log(TraceLogging::GC_ALLOCATING_STOP);
+                    logger->log(TraceLogging::GC_ALLOCATING_STOP);
 #endif
                     break;
                 }
-                JS_ASSERT(chunk->info.numArenasFreeCommitted == ArenasPerChunk);
-                rt->gcNumArenasFreeCommitted += ArenasPerChunk;
+                JS_ASSERT(chunk->info.numArenasFreeCommitted == 0);
                 rt->gcChunkPool.put(chunk);
             } while (state == ALLOCATING && rt->gcChunkPool.wantBackgroundAllocation(rt));
             if (state == ALLOCATING)
                 state = IDLE;
 #if JS_TRACE_LOGGING
             logger->log(TraceLogging::GC_ALLOCATING_STOP);
 #endif
             break;