Bug 1259042 - Re-introduce a version of ZoneCellIter for use under GC r=terrence
authorJon Coppeard <jcoppeard@mozilla.com>
Wed, 30 Mar 2016 12:33:55 +0100
changeset 290996 dbbe7c7e305daafc0bb6533a4e39a64a3bc6b38f
parent 290995 cbf86e5e944ff3d4c274013d1557400d808ea6e2
child 290997 457e7d5b11363cf859bb4c476701cf4803f452c4
push id30128
push userkwierso@gmail.com
push dateThu, 31 Mar 2016 20:04:34 +0000
treeherdermozilla-central@bccb11375f2a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersterrence
bugs1259042
milestone48.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 1259042 - Re-introduce a version of ZoneCellIter for use under GC r=terrence
js/src/gc/Iteration.cpp
js/src/gc/Zone.cpp
js/src/jit/Ion.cpp
js/src/jsgc.cpp
js/src/jsgcinlines.h
--- a/js/src/gc/Iteration.cpp
+++ b/js/src/gc/Iteration.cpp
@@ -96,37 +96,37 @@ js::IterateScripts(JSRuntime* rt, JSComp
 {
     MOZ_ASSERT(!rt->mainThread.suppressGC);
     rt->gc.evictNursery();
     MOZ_ASSERT(rt->gc.nursery.isEmpty());
 
     AutoPrepareForTracing prep(rt, SkipAtoms);
 
     if (compartment) {
-        for (ZoneCellIter i(compartment->zone(), gc::AllocKind::SCRIPT); !i.done(); i.next()) {
+        for (ZoneCellIterUnderGC i(compartment->zone(), gc::AllocKind::SCRIPT); !i.done(); i.next()) {
             JSScript* script = i.get<JSScript>();
             if (script->compartment() == compartment)
                 scriptCallback(rt, data, script);
         }
     } else {
         for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
-            for (ZoneCellIter i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next())
+            for (ZoneCellIterUnderGC i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next())
                 scriptCallback(rt, data, i.get<JSScript>());
         }
     }
 }
 
 void
 js::IterateGrayObjects(Zone* zone, GCThingCallback cellCallback, void* data)
 {
     zone->runtimeFromMainThread()->gc.evictNursery();
     AutoPrepareForTracing prep(zone->runtimeFromMainThread(), SkipAtoms);
 
     for (auto thingKind : ObjectAllocKinds()) {
-        for (ZoneCellIter i(zone, thingKind); !i.done(); i.next()) {
+        for (ZoneCellIterUnderGC i(zone, thingKind); !i.done(); i.next()) {
             JSObject* obj = i.get<JSObject>();
             if (obj->asTenured().isMarked(GRAY))
                 cellCallback(data, JS::GCCellPtr(obj));
         }
     }
 }
 
 JS_PUBLIC_API(void)
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -141,17 +141,17 @@ Zone::sweepBreakpoints(FreeOp* fop)
         return;
 
     /*
      * Sweep all compartments in a zone at the same time, since there is no way
      * to iterate over the scripts belonging to a single compartment in a zone.
      */
 
     MOZ_ASSERT(isGCSweepingOrCompacting());
-    for (ZoneCellIter i(this, AllocKind::SCRIPT); !i.done(); i.next()) {
+    for (ZoneCellIterUnderGC i(this, AllocKind::SCRIPT); !i.done(); i.next()) {
         JSScript* script = i.get<JSScript>();
         if (!script->hasAnyBreakpointsOrStepMode())
             continue;
 
         bool scriptGone = IsAboutToBeFinalizedUnbarriered(&script);
         MOZ_ASSERT(script == i.get<JSScript>());
         for (unsigned i = 0; i < script->length(); i++) {
             BreakpointSite* site = script->getBreakpointSite(script->offsetToPC(i));
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -584,17 +584,17 @@ jit::LazyLinkTopActivation(JSContext* cx
     return calleeScript->baselineOrIonRawPointer();
 }
 
 /* static */ void
 JitRuntime::Mark(JSTracer* trc)
 {
     MOZ_ASSERT(!trc->runtime()->isHeapMinorCollecting());
     Zone* zone = trc->runtime()->atomsCompartment()->zone();
-    for (gc::ZoneCellIter i(zone, gc::AllocKind::JITCODE); !i.done(); i.next()) {
+    for (gc::ZoneCellIterUnderGC i(zone, gc::AllocKind::JITCODE); !i.done(); i.next()) {
         JitCode* code = i.get<JitCode>();
         TraceRoot(trc, &code, "wrapper");
     }
 }
 
 /* static */ void
 JitRuntime::MarkJitcodeGlobalTableUnconditionally(JSTracer* trc)
 {
@@ -1318,17 +1318,17 @@ IonScript::unlinkFromRuntime(FreeOp* fop
 
 void
 jit::ToggleBarriers(JS::Zone* zone, bool needs)
 {
     JSRuntime* rt = zone->runtimeFromMainThread();
     if (!rt->hasJitRuntime())
         return;
 
-    for (gc::ZoneCellIter i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next()) {
+    for (gc::ZoneCellIterUnderGC i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next()) {
         JSScript* script = i.get<JSScript>();
         if (script->hasIonScript())
             script->ionScript()->toggleBarriers(needs);
         if (script->hasBaselineScript())
             script->baselineScript()->toggleBarriers(needs);
     }
 
     for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) {
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -2408,22 +2408,22 @@ Zone::prepareForCompacting()
 void
 GCRuntime::sweepTypesAfterCompacting(Zone* zone)
 {
     FreeOp* fop = rt->defaultFreeOp();
     zone->beginSweepTypes(fop, rt->gc.releaseObservedTypes && !zone->isPreservingCode());
 
     AutoClearTypeInferenceStateOnOOM oom(zone);
 
-    for (ZoneCellIter i(zone, AllocKind::SCRIPT); !i.done(); i.next()) {
+    for (ZoneCellIterUnderGC i(zone, AllocKind::SCRIPT); !i.done(); i.next()) {
         JSScript* script = i.get<JSScript>();
         script->maybeSweepTypes(&oom);
     }
 
-    for (ZoneCellIter i(zone, AllocKind::OBJECT_GROUP); !i.done(); i.next()) {
+    for (ZoneCellIterUnderGC i(zone, AllocKind::OBJECT_GROUP); !i.done(); i.next()) {
         ObjectGroup* group = i.get<ObjectGroup>();
         group->maybeSweep(&oom);
     }
 
     zone->types.endSweep(rt);
 }
 
 void
@@ -3929,25 +3929,21 @@ CompartmentCheckTracer::onChild(const JS
 }
 
 void
 GCRuntime::checkForCompartmentMismatches()
 {
     if (disableStrictProxyCheckingCount)
         return;
 
-    // ZoneCellIter could GC were this not called under GC.
-    MOZ_ASSERT(rt->isHeapBusy());
-    JS::AutoSuppressGCAnalysis noAnalysis;
-
     CompartmentCheckTracer trc(rt);
     for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
         trc.zone = zone;
         for (auto thingKind : AllAllocKinds()) {
-            for (ZoneCellIter i(zone, thingKind); !i.done(); i.next()) {
+            for (ZoneCellIterUnderGC i(zone, thingKind); !i.done(); i.next()) {
                 trc.src = i.getCell();
                 trc.srcKind = MapAllocToTraceKind(thingKind);
                 trc.compartment = DispatchTraceKindTyped(MaybeCompartmentFunctor(),
                                                          trc.src, trc.srcKind);
                 js::TraceChildren(&trc, trc.src, trc.srcKind);
             }
         }
     }
@@ -3957,17 +3953,17 @@ GCRuntime::checkForCompartmentMismatches
 static void
 RelazifyFunctions(Zone* zone, AllocKind kind)
 {
     MOZ_ASSERT(kind == AllocKind::FUNCTION ||
                kind == AllocKind::FUNCTION_EXTENDED);
 
     JSRuntime* rt = zone->runtimeFromMainThread();
 
-    for (ZoneCellIter i(zone, kind); !i.done(); i.next()) {
+    for (ZoneCellIterUnderGC i(zone, kind); !i.done(); i.next()) {
         JSFunction* fun = &i.get<JSObject>()->as<JSFunction>();
         if (fun->hasScript())
             fun->maybeRelazify(rt);
     }
 }
 
 bool
 GCRuntime::beginMarkPhase(JS::gcreason::Reason reason)
@@ -7357,21 +7353,17 @@ js::gc::CheckHashTablesAfterMovingGC(JSR
 {
     /*
      * Check that internal hash tables no longer have any pointers to things
      * that have been moved.
      */
     for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
         zone->checkUniqueIdTableAfterMovingGC();
 
-        // ZoneCellIter could GC were this not called under GC.
-        MOZ_ASSERT(rt->isHeapBusy());
-        JS::AutoSuppressGCAnalysis noAnalysis;
-
-        for (ZoneCellIter i(zone, AllocKind::BASE_SHAPE); !i.done(); i.next()) {
+        for (ZoneCellIterUnderGC i(zone, AllocKind::BASE_SHAPE); !i.done(); i.next()) {
             BaseShape* baseShape = i.get<BaseShape>();
             if (baseShape->hasTable())
                 baseShape->table().checkAfterMovingGC();
         }
     }
     for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
         c->objectGroups.checkTablesAfterMovingGC();
         c->dtoaCache.checkCacheAfterMovingGC();
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jsgcinlines_h
 #define jsgcinlines_h
 
 #include "jsgc.h"
 
 #include "mozilla/DebugOnly.h"
+#include "mozilla/Maybe.h"
 
 #include "gc/GCTrace.h"
 #include "gc/Zone.h"
 
 namespace js {
 namespace gc {
 
 static inline AllocKind
@@ -180,47 +181,25 @@ class ArenaCellIterUnderGC : public Aren
 };
 
 class ArenaCellIterUnderFinalize : public ArenaCellIterImpl
 {
   public:
     explicit ArenaCellIterUnderFinalize(Arena* arena) : ArenaCellIterImpl(arena) {}
 };
 
-class ZoneCellIter
+class ZoneCellIterImpl
 {
     ArenaIter arenaIter;
     ArenaCellIterImpl cellIter;
-    JS::AutoAssertNoAlloc noAlloc;
 
   public:
-    ZoneCellIter(JS::Zone* zone, AllocKind kind) {
+    ZoneCellIterImpl(JS::Zone* zone, AllocKind kind) {
         MOZ_ASSERT(zone);
-
-        // If called from outside a GC, ensure that the heap is in a state
-        // allows us to iterate.
-        JSRuntime* rt = zone->runtimeFromMainThread();
-        MOZ_ASSERT_IF(rt->isHeapBusy(), rt->gc.nursery.isEmpty());
-        if (!rt->isHeapBusy()) {
-            // We have a single-threaded runtime, so there's no need to protect
-            // against other threads iterating or allocating. However, we do
-            // have background finalization; we have to wait for this to finish
-            // if it's currently active.
-            if (IsBackgroundFinalized(kind) &&
-                zone->arenas.needBackgroundFinalizeWait(kind))
-            {
-                rt->gc.waitBackgroundSweepEnd();
-            }
-
-            // Evict the nursery before iterating so we can see all things.
-            rt->gc.evictNursery();
-
-            // Assert that no GCs can occur while a ZoneCellIter is live.
-            noAlloc.disallowAlloc(rt);
-        }
+        MOZ_ASSERT(zone->runtimeFromAnyThread()->gc.nursery.isEmpty());
 
         arenaIter.init(zone, kind);
         if (!arenaIter.done())
             cellIter.init(arenaIter.get());
     }
 
     bool done() const {
         return arenaIter.done();
@@ -243,16 +222,61 @@ class ZoneCellIter
             MOZ_ASSERT(!arenaIter.done());
             arenaIter.next();
             if (!arenaIter.done())
                 cellIter.reset(arenaIter.get());
         }
     }
 };
 
+class ZoneCellIterUnderGC : public ZoneCellIterImpl
+{
+  public:
+    ZoneCellIterUnderGC(JS::Zone* zone, AllocKind kind)
+      : ZoneCellIterImpl(zone, kind)
+    {
+        MOZ_ASSERT(zone->runtimeFromAnyThread()->isHeapBusy());
+    }
+};
+
+class ZoneCellIter
+{
+    mozilla::Maybe<ZoneCellIterImpl> impl;
+    JS::AutoAssertNoAlloc noAlloc;
+
+  public:
+    ZoneCellIter(JS::Zone* zone, AllocKind kind) {
+        // If called from outside a GC, ensure that the heap is in a state
+        // that allows us to iterate.
+        JSRuntime* rt = zone->runtimeFromMainThread();
+        if (!rt->isHeapBusy()) {
+            // We have a single-threaded runtime, so there's no need to protect
+            // against other threads iterating or allocating. However, we do
+            // have background finalization; we have to wait for this to finish
+            // if it's currently active.
+            if (IsBackgroundFinalized(kind) && zone->arenas.needBackgroundFinalizeWait(kind))
+                rt->gc.waitBackgroundSweepEnd();
+
+            // Evict the nursery before iterating so we can see all things.
+            rt->gc.evictNursery();
+
+            // Assert that no GCs can occur while a ZoneCellIter is live.
+            noAlloc.disallowAlloc(rt);
+        }
+
+        impl.emplace(zone, kind);
+    }
+
+    bool done() const { return impl->done(); }
+    template<typename T>
+    T* get() const { return impl->get<T>(); }
+    Cell* getCell() const { return impl->getCell(); }
+    void next() { impl->next(); }
+};
+
 class GCZonesIter
 {
   private:
     ZonesIter zone;
 
   public:
     explicit GCZonesIter(JSRuntime* rt, ZoneSelector selector = WithAtoms) : zone(rt, selector) {
         if (!zone->isCollecting())