bug 684527 - making sure that we never try to allocate from the GC. r=wmccloskey
authorIgor Bukanov <igor@mir2.org>
Mon, 12 Sep 2011 11:44:27 +0200
changeset 78538 a84273cf364468ced0be05f8ea860c70a95c53c5
parent 78537 3c429287dfbe623a21a9ca2382873a10a63ed781
child 78539 f98f144896ad67aa57e737b8b74eaa95d42ad711
push id78
push userclegnitto@mozilla.com
push dateFri, 16 Dec 2011 17:32:24 +0000
treeherdermozilla-release@79d24e644fdd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerswmccloskey
bugs684527
milestone9.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 684527 - making sure that we never try to allocate from the GC. r=wmccloskey
js/src/jsgc.cpp
js/src/jsgc.h
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -445,19 +445,18 @@ Chunk::removeFromAvailableList()
         JS_ASSERT(info.next->info.prevp == &info.next);
         info.next->info.prevp = info.prevp;
     }
     info.prevp = NULL;
     info.next = NULL;
 }
 
 ArenaHeader *
-Chunk::allocateArena(JSContext *cx, AllocKind thingKind)
+Chunk::allocateArena(JSCompartment *comp, AllocKind thingKind)
 {
-    JSCompartment *comp = cx->compartment;
     JS_ASSERT(hasAvailableArenas());
     ArenaHeader *aheader = info.emptyArenaListHead;
     info.emptyArenaListHead = aheader->next;
     aheader->init(comp, thingKind);
     --info.numFree;
 
     if (!hasAvailableArenas())
         removeFromAvailableList();
@@ -542,19 +541,18 @@ ReleaseGCChunk(JSRuntime *rt, Chunk *p)
 #ifdef MOZ_GCTIMER
     JS_ATOMIC_INCREMENT(&destroyChunkCount);
 #endif
     FreeGCChunk(p);
 }
 
 /* The caller must hold the GC lock. */
 static Chunk *
-PickChunk(JSContext *cx)
+PickChunk(JSCompartment *comp)
 {
-    JSCompartment *comp = cx->compartment;
     JSRuntime *rt = comp->rt;
     Chunk **listHeadp = GetAvailableChunkList(comp);
     Chunk *chunk = *listHeadp;
     if (chunk)
         return chunk;
 
     /*
      * We do not have available chunks, either get one from the empty set or
@@ -1140,58 +1138,58 @@ JSCompartment::reduceGCTriggerBytes(uint
         return;
     gcTriggerBytes -= amount;
 }
 
 namespace js {
 namespace gc {
 
 inline void *
-ArenaLists::allocateFromArena(JSContext *cx, AllocKind thingKind)
+ArenaLists::allocateFromArena(JSCompartment *comp, AllocKind thingKind)
 {
     Chunk *chunk = NULL;
 
     ArenaList *al = &arenaLists[thingKind];
     AutoLockGC maybeLock;
 
 #ifdef JS_THREADSAFE
     volatile uintptr_t *bfs = &backgroundFinalizeState[thingKind];
     if (*bfs != BFS_DONE) {
         /*
          * We cannot search the arena list for free things while the
          * background finalization runs and can modify head or cursor at any
          * moment. So we always allocate a new arena in that case.
          */
-        maybeLock.lock(cx->runtime);
+        maybeLock.lock(comp->rt);
         for (;;) {
             if (*bfs == BFS_DONE)
                 break;
 
             if (*bfs == BFS_JUST_FINISHED) {
                 /*
                  * Before we took the GC lock or while waiting for the
                  * background finalization to finish the latter added new
                  * arenas to the list.
                  */
                 *bfs = BFS_DONE;
                 break;
             }
 
             JS_ASSERT(!*al->cursor);
-            chunk = PickChunk(cx);
+            chunk = PickChunk(comp);
             if (chunk)
                 break;
 
             /*
              * If the background finalization still runs, wait for it to
              * finish and retry to check if it populated the arena list or
              * added new empty arenas.
              */
             JS_ASSERT(*bfs == BFS_RUN);
-            cx->runtime->gcHelperThread.waitBackgroundSweepEnd(cx->runtime, false);
+            comp->rt->gcHelperThread.waitBackgroundSweepEnd(comp->rt, false);
             JS_ASSERT(*bfs == BFS_JUST_FINISHED || *bfs == BFS_DONE);
         }
     }
 #endif /* JS_THREADSAFE */
 
     if (!chunk) {
         if (ArenaHeader *aheader = *al->cursor) {
             JS_ASSERT(aheader->hasFreeThings());
@@ -1209,33 +1207,33 @@ ArenaLists::allocateFromArena(JSContext 
              */
             freeLists[thingKind] = aheader->getFirstFreeSpan();
             aheader->setAsFullyUsed();
             return freeLists[thingKind].infallibleAllocate(Arena::thingSize(thingKind));
         }
 
         /* Make sure we hold the GC lock before we call PickChunk. */
         if (!maybeLock.locked())
-            maybeLock.lock(cx->runtime);
-        chunk = PickChunk(cx);
+            maybeLock.lock(comp->rt);
+        chunk = PickChunk(comp);
         if (!chunk)
             return NULL;
     }
 
     /*
      * While we still hold the GC lock get an arena from some chunk, mark it
      * as full as its single free span is moved to the free lits, and insert
      * it to the list as a fully allocated arena.
      *
      * We add the arena before the the head, not after the tail pointed by the
      * cursor, so after the GC the most recently added arena will be used first
      * for allocations improving cache locality.
      */
     JS_ASSERT(!*al->cursor);
-    ArenaHeader *aheader = chunk->allocateArena(cx, thingKind);
+    ArenaHeader *aheader = chunk->allocateArena(comp, thingKind);
     aheader->next = al->head;
     if (!al->head) {
         JS_ASSERT(al->cursor == &al->head);
         al->cursor = &aheader->next;
     }
     al->head = aheader;
 
     /* See comments before allocateFromNewArena about this assert. */
@@ -1428,44 +1426,39 @@ IsGCAllowed(JSContext *cx)
     return !JS_ON_TRACE(cx) && !JS_THREAD_DATA(cx)->waiveGCQuota;
 }
 
 /* static */ void *
 ArenaLists::refillFreeList(JSContext *cx, AllocKind thingKind)
 {
     JS_ASSERT(cx->compartment->arenas.freeLists[thingKind].isEmpty());
 
-    /*
-     * For compatibility with older code we tolerate calling the allocator
-     * during the GC in optimized builds.
-     */
-    JSRuntime *rt = cx->runtime;
+    JSCompartment *comp = cx->compartment;
+    JSRuntime *rt = comp->rt;
     JS_ASSERT(!rt->gcRunning);
-    if (rt->gcRunning)
-        return NULL;
 
     bool runGC = !!rt->gcIsNeeded;
     for (;;) {
         if (JS_UNLIKELY(runGC) && IsGCAllowed(cx)) {
             RunLastDitchGC(cx);
 
             /* Report OOM of the GC failed to free enough memory. */
             if (rt->gcBytes > rt->gcMaxBytes)
                 break;
 
             /*
              * The JSGC_END callback can legitimately allocate new GC
              * things and populate the free list. If that happens, just
              * return that list head.
              */
             size_t thingSize = Arena::thingSize(thingKind);
-            if (void *thing = cx->compartment->arenas.allocateFromFreeList(thingKind, thingSize))
+            if (void *thing = comp->arenas.allocateFromFreeList(thingKind, thingSize))
                 return thing;
         }
-        void *thing = cx->compartment->arenas.allocateFromArena(cx, thingKind);
+        void *thing = comp->arenas.allocateFromArena(comp, thingKind);
         if (JS_LIKELY(!!thing))
             return thing;
 
         /*
          * We failed to allocate. Run the GC if we can unless we have done it
          * already. Otherwise report OOM but first schedule a new GC soon.
          */
         if (runGC || !IsGCAllowed(cx)) {
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -649,17 +649,17 @@ struct Chunk {
 
     bool hasAvailableArenas() const {
         return info.numFree > 0;
     }
 
     inline void addToAvailableList(JSCompartment *compartment);
     inline void removeFromAvailableList();
 
-    ArenaHeader *allocateArena(JSContext *cx, AllocKind kind);
+    ArenaHeader *allocateArena(JSCompartment *comp, AllocKind kind);
 
     void releaseArena(ArenaHeader *aheader);
 };
 
 JS_STATIC_ASSERT(sizeof(Chunk) <= GC_CHUNK_SIZE);
 JS_STATIC_ASSERT(sizeof(Chunk) + BytesPerArena > GC_CHUNK_SIZE);
 
 inline uintptr_t
@@ -1097,17 +1097,17 @@ struct ArenaLists {
 #ifdef JS_THREADSAFE
     static void backgroundFinalize(JSContext *cx, ArenaHeader *listHead);
 #endif
 
   private:
     inline void finalizeNow(JSContext *cx, AllocKind thingKind);
     inline void finalizeLater(JSContext *cx, AllocKind thingKind);
 
-    inline void *allocateFromArena(JSContext *cx, AllocKind thingKind);
+    inline void *allocateFromArena(JSCompartment *comp, AllocKind thingKind);
 };
 
 /*
  * Initial allocation size for data structures holding chunks is set to hold
  * chunks with total capacity of 16MB to avoid buffer resizes during browser
  * startup.
  */
 const size_t INITIAL_CHUNK_CAPACITY = 16 * 1024 * 1024 / GC_CHUNK_SIZE;