Bug 949607, part 2 - Add and use runNextCollectorTimer. r=smaug
authorAndrew McCreight <continuation@gmail.com>
Fri, 25 Apr 2014 13:21:04 -0700
changeset 180332 6ebfa2a0fbc84b907600dfbba9955ebbecadc57f
parent 180331 f42f8c4bfd26cee4b9900a5a299479bae3d08ada
child 180333 ea4e380efa0ad3422e017a0e4c74cc61c9ed5187
push id26663
push userryanvm@gmail.com
push dateSun, 27 Apr 2014 01:52:51 +0000
treeherdermozilla-central@fcf19894d9f3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs949607
milestone31.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 949607, part 2 - Add and use runNextCollectorTimer. r=smaug
dom/base/nsDOMWindowUtils.cpp
dom/base/nsJSEnvironment.cpp
dom/base/nsJSEnvironment.h
dom/interfaces/base/nsIDOMWindowUtils.idl
testing/mochitest/tests/SimpleTest/iframe-between-tests.html
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -1482,16 +1482,28 @@ nsDOMWindowUtils::CycleCollect(nsICycleC
   }
 #endif
 
   nsJSContext::CycleCollectNow(aListener, aExtraForgetSkippableCalls);
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsDOMWindowUtils::RunNextCollectorTimer()
+{
+  if (!nsContentUtils::IsCallerChrome()) {
+    return NS_ERROR_DOM_SECURITY_ERR;
+  }
+
+  nsJSContext::RunNextCollectorTimer();
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDOMWindowUtils::SendSimpleGestureEvent(const nsAString& aType,
                                          float aX,
                                          float aY,
                                          uint32_t aDirection,
                                          double aDelta,
                                          int32_t aModifiers,
                                          uint32_t aClickCount)
 {
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -212,16 +212,22 @@ static int32_t sContextCount;
 static nsIScriptSecurityManager *sSecurityManager;
 
 // nsJSEnvironmentObserver observes the memory-pressure notifications
 // and forces a garbage collection and cycle collection when it happens, if
 // the appropriate pref is set.
 
 static bool sGCOnMemoryPressure;
 
+// In testing, we call RunNextCollectorTimer() to ensure that the collectors are run more
+// aggressively than they would be in regular browsing. sExpensiveCollectorPokes keeps
+// us from triggering expensive full collections too frequently.
+static int32_t sExpensiveCollectorPokes = 0;
+static const int32_t kPokesBetweenExpensiveCollectorTriggers = 5;
+
 static PRTime
 GetCollectionTimeDelta()
 {
   PRTime now = PR_Now();
   if (sFirstCollectionTime) {
     return now - sFirstCollectionTime;
   }
   sFirstCollectionTime = now;
@@ -2313,16 +2319,74 @@ nsJSContext::LoadEnd()
     return;
   }
 
   // Its probably a good idea to GC soon since we have finished loading.
   sLoadingInProgress = false;
   PokeGC(JS::gcreason::LOAD_END);
 }
 
+// Only trigger expensive timers when they have been checked a number of times.
+static bool
+ReadyToTriggerExpensiveCollectorTimer()
+{
+  bool ready = kPokesBetweenExpensiveCollectorTriggers < ++sExpensiveCollectorPokes;
+  if (ready) {
+    sExpensiveCollectorPokes = 0;
+  }
+  return ready;
+}
+
+
+// Check all of the various collector timers and see if they are waiting to fire.
+// For the synchronous collector timers, sGCTimer and sCCTimer, we only want to trigger
+// the collection occasionally, because they are expensive.  The incremental collector
+// timers, sInterSliceGCTimer and sICCTimer, are fast and need to be run many times, so
+// always run their corresponding timer.
+
+// This does not check sFullGCTimer, as that's an even more expensive collector we run
+// on a long timer.
+
+// static
+void
+nsJSContext::RunNextCollectorTimer()
+{
+  if (sShuttingDown) {
+    return;
+  }
+
+  if (sGCTimer) {
+    if (ReadyToTriggerExpensiveCollectorTimer()) {
+      GCTimerFired(nullptr, reinterpret_cast<void *>(JS::gcreason::DOM_WINDOW_UTILS));
+    }
+    return;
+  }
+
+  if (sInterSliceGCTimer) {
+    InterSliceGCTimerFired(nullptr, nullptr);
+    return;
+  }
+
+  // Check the CC timers after the GC timers, because the CC timers won't do
+  // anything if a GC is in progress.
+  MOZ_ASSERT(!sCCLockedOut, "Don't check the CC timers if the CC is locked out.");
+
+  if (sCCTimer) {
+    if (ReadyToTriggerExpensiveCollectorTimer()) {
+      CCTimerFired(nullptr, nullptr);
+    }
+    return;
+  }
+
+  if (sICCTimer) {
+    ICCTimerFired(nullptr, nullptr);
+    return;
+  }
+}
+
 // static
 void
 nsJSContext::PokeGC(JS::gcreason::Reason aReason, int aDelay)
 {
   if (sGCTimer || sInterSliceGCTimer || sShuttingDown) {
     // There's already a timer for GC'ing, just return
     return;
   }
@@ -2443,17 +2507,16 @@ nsJSContext::KillShrinkGCBuffersTimer()
   }
 }
 
 //static
 void
 nsJSContext::KillCCTimer()
 {
   sCCLockedOutTime = 0;
-
   if (sCCTimer) {
     sCCTimer->Cancel();
     NS_RELEASE(sCCTimer);
   }
 }
 
 //static
 void
@@ -2644,16 +2707,17 @@ mozilla::dom::StartupJSEnvironment()
   sRuntimeService = nullptr;
   sRuntime = nullptr;
   sIsInitialized = false;
   sDidShutdown = false;
   sShuttingDown = false;
   sContextCount = 0;
   sSecurityManager = nullptr;
   gCCStats.Clear();
+  sExpensiveCollectorPokes = 0;
 }
 
 static void
 ReportAllJSExceptionsPrefChangedCallback(const char* aPrefName, void* aClosure)
 {
   bool reportAll = Preferences::GetBool(aPrefName, false);
   nsContentUtils::XPConnect()->SetReportAllJSExceptions(reportAll);
 }
--- a/dom/base/nsJSEnvironment.h
+++ b/dom/base/nsJSEnvironment.h
@@ -99,16 +99,18 @@ public:
                               int32_t aExtraForgetSkippableCalls = 0);
 
   // Run a cycle collector slice, using a heuristic to decide how long to run it.
   static void RunCycleCollectorSlice();
 
   static void BeginCycleCollectionCallback();
   static void EndCycleCollectionCallback(mozilla::CycleCollectorResults &aResults);
 
+  static void RunNextCollectorTimer();
+
   static void PokeGC(JS::gcreason::Reason aReason, int aDelay = 0);
   static void KillGCTimer();
 
   static void PokeShrinkGCBuffers();
   static void KillShrinkGCBuffersTimer();
 
   static void MaybePokeCC();
   static void KillCCTimer();
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -38,17 +38,17 @@ interface nsIDOMFile;
 interface nsIFile;
 interface nsIDOMClientRect;
 interface nsIURI;
 interface nsIDOMEventTarget;
 interface nsIRunnable;
 interface nsICompositionStringSynthesizer;
 interface nsITranslationNodeList;
 
-[scriptable, uuid(3d977df2-1c0e-4b61-bc21-c6ee757a9191)]
+[scriptable, uuid(926c7450-ab88-11e3-a5e2-0800200c9a66)]
 interface nsIDOMWindowUtils : nsISupports {
 
   /**
    * Image animation mode of the window. When this attribute's value
    * is changed, the implementation should set all images in the window
    * to the given value. That is, when set to kDontAnimMode, all images
    * will stop animating. The attribute's value must be one of the
    * animationMode values from imgIContainer.
@@ -738,16 +738,22 @@ interface nsIDOMWindowUtils : nsISupport
    *                                   be called before running cycle collection.
    *                                   -1 prevents the default
    *                                   nsCycleCollector_forgetSkippable call
    *                                   which happens after garbage collection.
    */
   void cycleCollect([optional] in nsICycleCollectorListener aListener,
                     [optional] in long aExtraForgetSkippableCalls);
 
+  /**
+   * Trigger whichever GC or CC timer is currently active and waiting to fire.
+   * Don't do this too much for initiating heavy actions, like the start of a IGC.
+   */
+  void runNextCollectorTimer();
+
   /** Synthesize a simple gesture event for a window. The event types
    *  supported are: MozSwipeGestureStart, MozSwipeGestureUpdate,
    *  MozSwipeGestureEnd, MozSwipeGesture, MozMagnifyGestureStart,
    *  MozMagnifyGestureUpdate, MozMagnifyGesture, MozRotateGestureStart,
    *  MozRotateGestureUpdate, MozRotateGesture, MozPressTapGesture,
    *  MozTapGesture, and MozEdgeUIGesture.
    *
    * Cannot be accessed from unprivileged context (not
--- a/testing/mochitest/tests/SimpleTest/iframe-between-tests.html
+++ b/testing/mochitest/tests/SimpleTest/iframe-between-tests.html
@@ -6,17 +6,12 @@
   load the next.
 -->
 <script>
 window.addEventListener("load", function() {
   var runner = (parent.TestRunner || parent.wrappedJSObject.TestRunner);
   runner.testUnloaded();
 
   if (SpecialPowers) {
-    if (!runner.garbageCollectCount) {
-      runner.garbageCollectCount = 0;
-    }
-    if (runner.garbageCollectCount++ % 10 == 0) {
-      SpecialPowers.DOMWindowUtils.garbageCollect();
-    }
+    SpecialPowers.DOMWindowUtils.runNextCollectorTimer();
   }
 });
 </script>