Bug 1034225 - Avoid purging the free lists every iteration in PJS by checking if the current arena is in use. r=terrence
authorEmanuel Hoogeveen <emanuel.hoogeveen@gmail.com>
Fri, 04 Jul 2014 04:19:00 -0400
changeset 192677 f81fe2b04fefb76f88bbce9952ad80112968c4f6
parent 192676 4c59adf6c245e82aab94f83a9b25eaa6b2132fe6
child 192678 b2fd5a214d2ae521b60a1acb4ff7c3f648da2d52
push idunknown
push userunknown
push dateunknown
reviewersterrence
bugs1034225
milestone33.0a1
Bug 1034225 - Avoid purging the free lists every iteration in PJS by checking if the current arena is in use. r=terrence
js/src/gc/ForkJoinNursery.cpp
js/src/jsgc.h
--- a/js/src/gc/ForkJoinNursery.cpp
+++ b/js/src/gc/ForkJoinNursery.cpp
@@ -414,42 +414,42 @@ ForkJoinNursery::forwardFromStack(ForkJo
 {
     MarkForkJoinStack(trc);
 }
 
 void
 ForkJoinNursery::forwardFromTenured(ForkJoinNurseryCollectionTracer *trc)
 {
     JSObject *objs[ArenaCellCount];
+    ArenaLists &lists = tenured_->arenas;
     for (size_t k=0; k < FINALIZE_LIMIT; k++) {
         AllocKind kind = (AllocKind)k;
         if (!IsFJNurseryAllocable(kind))
             continue;
 
         // When non-JSObject types become nursery-allocable the assumptions in the
         // loops below will no longer hold; other types than JSObject must be
         // handled.
         JS_ASSERT(kind <= FINALIZE_OBJECT_LAST);
 
+        // Clear the free list that we're currently allocating out of.
+        lists.purge(kind);
+
+        // Since we only purge once, there must not currently be any partially
+        // full arenas left to allocate out of, or we would break out early.
+        JS_ASSERT(!lists.getArenaAfterCursor(kind));
+
         ArenaIter ai;
         ai.init(const_cast<Allocator *>(tenured_), kind);
         for (; !ai.done(); ai.next()) {
-            // Do the walk in two steps to avoid problems resulting from allocating
-            // into the arena that's being walked: ArenaCellIter is not safe for that.
-            // It can happen during evacuation.
-            //
-            // ArenaCellIterUnderFinalize requires any free list to be flushed into
-            // its arena, and since we may allocate within traceObject() we must
-            // purge before each arena scan.  This is probably not very expensive,
-            // it's constant work, and inlined.
-            //
+            if (isEvacuating_ && lists.arenaIsInUse(ai.get(), kind))
+                break;
             // Use ArenaCellIterUnderFinalize, not ...UnderGC, because that side-steps
             // some assertions in the latter that are wrong for PJS collection.
             size_t numObjs = 0;
-            tenured_->arenas.purge(kind);
             for (ArenaCellIterUnderFinalize i(ai.get()); !i.done(); i.next())
                 objs[numObjs++] = i.get<JSObject>();
             for (size_t i=0; i < numObjs; i++)
                 traceObject(trc, objs[i]);
         }
     }
 }
 
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -662,16 +662,20 @@ class ArenaLists
     ArenaHeader *getFirstArena(AllocKind thingKind) const {
         return arenaLists[thingKind].head();
     }
 
     ArenaHeader *getFirstArenaToSweep(AllocKind thingKind) const {
         return arenaListsToSweep[thingKind];
     }
 
+    ArenaHeader *getArenaAfterCursor(AllocKind thingKind) const {
+        return arenaLists[thingKind].arenaAfterCursor();
+    }
+
     bool arenaListsAreEmpty() const {
         for (size_t i = 0; i != FINALIZE_LIMIT; ++i) {
             /*
              * The arena cannot be empty if the background finalization is not yet
              * done.
              */
             if (backgroundFinalizeState[i] != BFS_DONE)
                 return false;
@@ -775,16 +779,25 @@ class ArenaLists
              * lists.
              */
             JS_ASSERT(freeList->isSameNonEmptySpan(aheader->getFirstFreeSpan()));
             return true;
         }
         return false;
     }
 
+    /* Check if |aheader|'s arena is in use. */
+    bool arenaIsInUse(ArenaHeader *aheader, AllocKind kind) const {
+        JS_ASSERT(aheader);
+        const FreeList &freeList = freeLists[kind];
+        if (freeList.isEmpty())
+            return false;
+        return aheader == freeList.arenaHeader();
+    }
+
     MOZ_ALWAYS_INLINE void *allocateFromFreeList(AllocKind thingKind, size_t thingSize) {
         return freeLists[thingKind].allocate(thingSize);
     }
 
     template <AllowGC allowGC>
     static void *refillFreeList(ThreadSafeContext *cx, AllocKind thingKind);
 
     /*