Bug 1146696 - Don't assume there are no arenas available after last ditch GC. r=terrence, a=sledru
authorJon Coppeard <jcoppeard@mozilla.com>
Mon, 30 Mar 2015 11:03:35 +0100
changeset 265363 1131368d1567cbf40f29913022574ea27640eaa2
parent 265362 9ed6bab112593d2d4df18be050bee7da2dc821cb
child 265364 9ededdba9f926dd220e6a629789c696021a76660
push id4718
push userraliiev@mozilla.com
push dateMon, 11 May 2015 18:39:53 +0000
treeherdermozilla-beta@c20c4ef55f08 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersterrence, sledru
bugs1146696
milestone39.0a2
Bug 1146696 - Don't assume there are no arenas available after last ditch GC. r=terrence, a=sledru
js/src/gc/Allocator.cpp
js/src/gc/GCRuntime.h
js/src/jit-test/tests/gc/bug-1146696.js
--- a/js/src/gc/Allocator.cpp
+++ b/js/src/gc/Allocator.cpp
@@ -227,38 +227,38 @@ FOR_ALL_NON_OBJECT_GC_LAYOUTS(DECL_ALLOC
 #undef DECL_ALLOCATOR_INSTANCES
 
 template <typename T, AllowGC allowGC>
 /* static */ T*
 GCRuntime::tryNewTenuredThing(ExclusiveContext* cx, AllocKind kind, size_t thingSize)
 {
     T* t = reinterpret_cast<T*>(cx->arenas()->allocateFromFreeList(kind, thingSize));
     if (!t)
-        t = reinterpret_cast<T*>(refillFreeListFromAnyThread<allowGC>(cx, kind));
+        t = reinterpret_cast<T*>(refillFreeListFromAnyThread<allowGC>(cx, kind, thingSize));
 
     checkIncrementalZoneState(cx, t);
     TraceTenuredAlloc(t, kind);
     return t;
 }
 
 template <AllowGC allowGC>
 /* static */ void*
-GCRuntime::refillFreeListFromAnyThread(ExclusiveContext* cx, AllocKind thingKind)
+GCRuntime::refillFreeListFromAnyThread(ExclusiveContext* cx, AllocKind thingKind, size_t thingSize)
 {
     MOZ_ASSERT(cx->arenas()->freeLists[thingKind].isEmpty());
 
     if (cx->isJSContext())
-        return refillFreeListFromMainThread<allowGC>(cx->asJSContext(), thingKind);
+        return refillFreeListFromMainThread<allowGC>(cx->asJSContext(), thingKind, thingSize);
 
     return refillFreeListOffMainThread(cx, thingKind);
 }
 
 template <AllowGC allowGC>
 /* static */ void*
-GCRuntime::refillFreeListFromMainThread(JSContext* cx, AllocKind thingKind)
+GCRuntime::refillFreeListFromMainThread(JSContext* cx, AllocKind thingKind, size_t thingSize)
 {
     JSRuntime* rt = cx->runtime();
     MOZ_ASSERT(!rt->isHeapBusy(), "allocating while under GC");
     MOZ_ASSERT_IF(allowGC, !rt->currentThreadHasExclusiveAccess());
 
     // Try to allocate; synchronize with background GC threads if necessary.
     void* thing = tryRefillFreeListFromMainThread(cx, thingKind);
     if (MOZ_LIKELY(thing))
@@ -272,17 +272,21 @@ GCRuntime::refillFreeListFromMainThread(
             return nullptr;
 
         JS::PrepareForFullGC(rt);
         AutoKeepAtoms keepAtoms(cx->perThreadData);
         rt->gc.gc(GC_SHRINK, JS::gcreason::LAST_DITCH);
     }
 
     // Retry the allocation after the last-ditch GC.
-    thing = tryRefillFreeListFromMainThread(cx, thingKind);
+    // Note that due to GC callbacks we might already have allocated an arena
+    // for this thing kind!
+    thing = cx->arenas()->allocateFromFreeList(thingKind, thingSize);
+    if (!thing)
+        thing = tryRefillFreeListFromMainThread(cx, thingKind);
     if (thing)
         return thing;
 
     // We are really just totally out of memory.
     MOZ_ASSERT(allowGC, "A fallible allocation must not report OOM on failure.");
     ReportOutOfMemory(cx);
     return nullptr;
 }
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -874,19 +874,21 @@ class GCRuntime
     ArenaHeader* allocateArena(Chunk* chunk, Zone* zone, AllocKind kind, const AutoLockGC& lock);
     void arenaAllocatedDuringGC(JS::Zone* zone, ArenaHeader* arena);
 
     // Allocator internals
     bool gcIfNeededPerAllocation(JSContext* cx);
     template <typename T>
     static void checkIncrementalZoneState(ExclusiveContext* cx, T* t);
     template <AllowGC allowGC>
-    static void* refillFreeListFromAnyThread(ExclusiveContext* cx, AllocKind thingKind);
+    static void* refillFreeListFromAnyThread(ExclusiveContext* cx, AllocKind thingKind,
+                                             size_t thingSize);
     template <AllowGC allowGC>
-    static void* refillFreeListFromMainThread(JSContext* cx, AllocKind thingKind);
+    static void* refillFreeListFromMainThread(JSContext* cx, AllocKind thingKind,
+                                              size_t thingSize);
     static void* tryRefillFreeListFromMainThread(JSContext* cx, AllocKind thingKind);
     static void* refillFreeListOffMainThread(ExclusiveContext* cx, AllocKind thingKind);
 
     /*
      * Return the list of chunks that can be released outside the GC lock.
      * Must be called either during the GC or with the GC lock taken.
      */
     ChunkPool expireEmptyChunkPool(bool shrinkBuffers, const AutoLockGC& lock);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1146696.js
@@ -0,0 +1,21 @@
+// |jit-test| --no-ggc; allow-oom
+gc();
+dbg1 = new Debugger();
+root2 = newGlobal();
+dbg1.memory.onGarbageCollection = function(){}
+dbg1.addDebuggee(root2);
+for (var j = 0; j < 9999; ++j) {
+    try {
+        a
+    } catch (e) {}
+}
+gcparam("maxBytes", gcparam("gcBytes") + 8000);
+function g(i) {
+    if (i == 0)
+        return;
+    var x = "";
+    function f() {}
+    eval('');
+    g(i - 1);
+}
+g(100);