Bug 937766, part 5 - Implement PrepareForGarbageCollection. r=smaug
authorAndrew McCreight <continuation@gmail.com>
Fri, 06 Dec 2013 10:17:20 -0800
changeset 174954 edb01fe9d0002e2b25f472a8e4b62da954bb4cf1
parent 174953 abc785d60d75cd82d880d025603afbde102147e2
child 174955 60e84998a0a2aebe31cae479c8f079547cc72c61
push id445
push userffxbld
push dateMon, 10 Mar 2014 22:05:19 +0000
treeherdermozilla-release@dc38b741b04e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs937766
milestone28.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 937766, part 5 - Implement PrepareForGarbageCollection. r=smaug Running the garbage collector can cause objects in the CC graph to die, so just finish off an incremental cycle collection when we start a GC.
xpcom/base/CycleCollectedJSRuntime.cpp
xpcom/base/nsCycleCollector.cpp
xpcom/base/nsCycleCollector.h
--- a/xpcom/base/CycleCollectedJSRuntime.cpp
+++ b/xpcom/base/CycleCollectedJSRuntime.cpp
@@ -1104,16 +1104,17 @@ CycleCollectedJSRuntime::FinalizeDeferre
   }
 }
 
 void
 CycleCollectedJSRuntime::OnGC(JSGCStatus aStatus)
 {
   switch (aStatus) {
     case JSGC_BEGIN:
+      nsCycleCollector_prepareForGarbageCollection();
       break;
     case JSGC_END:
     {
       /*
        * If the previous GC created a runnable to finalize objects
        * incrementally, and if it hasn't finished yet, finish it now. We
        * don't want these to build up. We also don't want to allow any
        * existing incremental finalize runnables to run after a
--- a/xpcom/base/nsCycleCollector.cpp
+++ b/xpcom/base/nsCycleCollector.cpp
@@ -1126,16 +1126,18 @@ public:
                  nsCycleCollectingAutoRefCnt *aRefCnt);
     uint32_t SuspectedCount();
     void ForgetSkippable(bool aRemoveChildlessNodes, bool aAsyncSnowWhiteFreeing);
     bool FreeSnowWhite(bool aUntilNoSWInPurpleBuffer);
 
     // This method assumes its argument is already canonicalized.
     void RemoveObjectFromGraph(void *aPtr);
 
+    void PrepareForGarbageCollection();
+
     bool Collect(ccType aCCType,
                  SliceBudget &aBudget,
                  nsICycleCollectorListener *aManualListener);
     void Shutdown();
 
     NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
                               nsISupports* aData);
 
@@ -2889,16 +2891,34 @@ nsCycleCollector::Collect(ccType aCCType
         }
     }
 
     MOZ_ASSERT_IF(aCCType != ScheduledCC, mIncrementalPhase == IdlePhase);
 
     return collectedAny;
 }
 
+// Any JS objects we have in the graph could die when we GC, but we
+// don't want to abandon the current CC, because the graph contains
+// information about purple roots. So we synchronously finish off
+// the current CC.
+void nsCycleCollector::PrepareForGarbageCollection()
+{
+    if (mIncrementalPhase == IdlePhase) {
+        MOZ_ASSERT(mGraph.IsEmpty(), "Non-empty graph when idle");
+        MOZ_ASSERT(!mBuilder, "Non-null builder when idle");
+        return;
+    }
+
+    SliceBudget unlimitedBudget;
+    PrintPhase("PrepareForGarbageCollection");
+    Collect(ScheduledCC, unlimitedBudget, nullptr);
+    MOZ_ASSERT(mIncrementalPhase == IdlePhase);
+}
+
 // Don't merge too many times in a row, and do at least a minimum
 // number of unmerged CCs in a row.
 static const uint32_t kMinConsecutiveUnmerged = 3;
 static const uint32_t kMaxConsecutiveMerged = 3;
 
 bool
 nsCycleCollector::ShouldMergeZones(ccType aCCType)
 {
@@ -3384,16 +3404,30 @@ nsCycleCollector_scheduledCollect()
     MOZ_ASSERT(data->mCollector);
 
     PROFILER_LABEL("CC", "nsCycleCollector_scheduledCollect");
     SliceBudget unlimitedBudget;
     data->mCollector->Collect(ScheduledCC, unlimitedBudget, nullptr);
 }
 
 void
+nsCycleCollector_prepareForGarbageCollection()
+{
+    CollectorData *data = sCollectorData.get();
+
+    MOZ_ASSERT(data);
+
+    if (!data->mCollector) {
+        return;
+    }
+
+    data->mCollector->PrepareForGarbageCollection();
+}
+
+void
 nsCycleCollector_shutdown()
 {
     CollectorData *data = sCollectorData.get();
 
     if (data) {
         MOZ_ASSERT(data->mCollector);
         PROFILER_LABEL("CC", "nsCycleCollector_shutdown");
         data->mCollector->Shutdown();
--- a/xpcom/base/nsCycleCollector.h
+++ b/xpcom/base/nsCycleCollector.h
@@ -30,16 +30,18 @@ typedef void (*CC_BeforeUnlinkCallback)(
 void nsCycleCollector_setBeforeUnlinkCallback(CC_BeforeUnlinkCallback aCB);
 
 typedef void (*CC_ForgetSkippableCallback)(void);
 void nsCycleCollector_setForgetSkippableCallback(CC_ForgetSkippableCallback aCB);
 
 void nsCycleCollector_forgetSkippable(bool aRemoveChildlessNodes = false,
                                       bool aAsyncSnowWhiteFreeing = false);
 
+void nsCycleCollector_prepareForGarbageCollection();
+
 void nsCycleCollector_dispatchDeferredDeletion(bool aContinuation = false);
 bool nsCycleCollector_doDeferredDeletion();
 
 void nsCycleCollector_collect(nsICycleCollectorListener *aManualListener);
 void nsCycleCollector_scheduledCollect();
 
 uint32_t nsCycleCollector_suspectedCount();
 void nsCycleCollector_shutdown();