Bug 1146696 - Don't assume there are no arenas available after last ditch GC r=terrence
authorJon Coppeard <jcoppeard@mozilla.com>
Fri, 27 Mar 2015 10:20:53 +0000
changeset 266414 eaf2526ffd900b4bbe0572f55d590962f5896e39
parent 266413 0e0e159457ec700aae5bb661a392c6ee2a915c05
child 266415 b1f9fa1917187b8303f7cef6a27ab6aa1496372f
push id830
push userraliiev@mozilla.com
push dateFri, 19 Jun 2015 19:24:37 +0000
treeherdermozilla-release@932614382a68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersterrence
bugs1146696
milestone39.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 1146696 - Don't assume there are no arenas available after last ditch GC r=terrence
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,18 @@
+// |jit-test| error: out of memory
+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") + 1);
+g();
+function g() {
+    var x = "";
+    function f() {}
+    eval('');
+    g();
+}