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 291055 dbbe7c7e305daafc0bb6533a4e39a64a3bc6b38f
parent 291054 cbf86e5e944ff3d4c274013d1557400d808ea6e2
child 291056 457e7d5b11363cf859bb4c476701cf4803f452c4
push id19656
push usergwagner@mozilla.com
push dateMon, 04 Apr 2016 13:43:23 +0000
treeherderb2g-inbound@e99061fde28a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersterrence
bugs1259042
milestone48.0a1
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())