Bug 1575938 Convert dom/JSEnvironment GC timing constants to StaticPref r=edgar
authorAndrew Creskey <acreskey@mozilla.com>
Mon, 26 Aug 2019 15:51:01 +0000
changeset 490418 c8b9ae708d8e1805bd68f3deb3c0fe5cd9f9e1c5
parent 490417 b11b42a2c23909b96a12e5de13ea00913b7285de
child 490419 3853d8979a553067596ab19f07ed5a416ee4b358
child 490551 c1dfd9ec253d65976eb7751dfb4c2fbdcd687bf3
push id36503
push userccoroiu@mozilla.com
push dateWed, 28 Aug 2019 21:44:52 +0000
treeherdermozilla-central@c8b9ae708d8e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersedgar
bugs1575938
milestone70.0a1
first release with
nightly linux32
c8b9ae708d8e / 70.0a1 / 20190828214452 / files
nightly linux64
c8b9ae708d8e / 70.0a1 / 20190828214452 / files
nightly mac
c8b9ae708d8e / 70.0a1 / 20190828214452 / files
nightly win32
c8b9ae708d8e / 70.0a1 / 20190828214452 / files
nightly win64
c8b9ae708d8e / 70.0a1 / 20190828214452 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1575938 Convert dom/JSEnvironment GC timing constants to StaticPref r=edgar Converted the following to StaticPrefs so that we can easily test variations: NS_GC_DELAY NS_SHRINK_GC_BUFFERS_DELAY NS_FIRST_GC_DELAY NS_FULL_GC_DELAY NS_INTERSLICE_GC_DELAY NS_USER_INTERACTION_INTERVAL Differential Revision: https://phabricator.services.mozilla.com/D43112
dom/base/nsJSEnvironment.cpp
dom/base/nsJSEnvironment.h
dom/events/EventStateManager.cpp
dom/events/EventStateManager.h
layout/base/nsDocumentViewer.cpp
modules/libpref/init/StaticPrefList.yaml
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -98,27 +98,16 @@ using namespace mozilla::dom;
 
 const size_t gStackSize = 8192;
 
 // Thank you Microsoft!
 #ifdef CompareString
 #  undef CompareString
 #endif
 
-#define NS_SHRINK_GC_BUFFERS_DELAY 4000  // ms
-
-// The amount of time we wait from the first request to GC to actually
-// doing the first GC.
-#define NS_FIRST_GC_DELAY 10000  // ms
-
-#define NS_FULL_GC_DELAY 60000  // ms
-
-// Maximum amount of time that should elapse between incremental GC slices
-#define NS_INTERSLICE_GC_DELAY 100  // ms
-
 // The amount of time we wait between a request to CC (after GC ran)
 // and doing the actual CC.
 #define NS_CC_DELAY 6000  // ms
 
 #define NS_CC_SKIPPABLE_DELAY 250  // ms
 
 // In case the cycle collector isn't run at all, we don't want
 // forget skippables to run too often. So limit the forget skippable cycle to
@@ -1555,19 +1544,16 @@ void nsJSContext::BeginCycleCollectionCa
   // manually triggering an incremental collection, and we want to be sure to
   // finish it.
   sICCRunner = IdleTaskRunner::Create(
       ICCRunnerFired, "BeginCycleCollectionCallback::ICCRunnerFired",
       kICCIntersliceDelay, kIdleICCSliceBudget, true,
       [] { return sShuttingDown; }, TaskCategory::GarbageCollection);
 }
 
-static_assert(NS_GC_DELAY > kMaxICCDuration,
-              "A max duration ICC shouldn't reduce GC delay to 0");
-
 // static
 void nsJSContext::EndCycleCollectionCallback(CycleCollectorResults& aResults) {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsJSContext::KillICCRunner();
 
   // Update timing information for the current slice before we log it, if
   // we previously called PrepareForCycleCollectionSlice(). During shutdown
@@ -1576,18 +1562,22 @@ void nsJSContext::EndCycleCollectionCall
 
   sCCollectedWaitingForGC += aResults.mFreedGCed;
   sCCollectedZonesWaitingForGC += aResults.mFreedJSZones;
 
   TimeStamp endCCTimeStamp = TimeStamp::Now();
   uint32_t ccNowDuration = TimeBetween(gCCStats.mBeginTime, endCCTimeStamp);
 
   if (NeedsGCAfterCC()) {
+    MOZ_ASSERT(StaticPrefs::javascript_options_gc_delay() > kMaxICCDuration,
+               "A max duration ICC shouldn't reduce GC delay to 0");
+
     PokeGC(JS::GCReason::CC_WAITING, nullptr,
-           NS_GC_DELAY - std::min(ccNowDuration, kMaxICCDuration));
+           StaticPrefs::javascript_options_gc_delay() -
+               std::min(ccNowDuration, kMaxICCDuration));
   }
 
   // Log information about the CC via telemetry, JSON and the console.
   Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_FINISH_IGC,
                         gCCStats.mAnyLockedOut);
   Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_SYNC_SKIPPABLE,
                         gCCStats.mRanSyncForgetSkippable);
   Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_FULL, ccNowDuration);
@@ -1781,17 +1771,18 @@ void GCTimerFired(nsITimer* aTimer, void
     return;
   }
 
   // Now start the actual GC after initial timer has fired.
   sInterSliceGCRunner = IdleTaskRunner::Create(
       [aClosure](TimeStamp aDeadline) {
         return InterSliceGCRunnerFired(aDeadline, aClosure);
       },
-      "GCTimerFired::InterSliceGCRunnerFired", NS_INTERSLICE_GC_DELAY,
+      "GCTimerFired::InterSliceGCRunnerFired",
+      StaticPrefs::javascript_options_gc_delay_interslice(),
       sActiveIntersliceGCBudget, true, [] { return sShuttingDown; },
       TaskCategory::GarbageCollection);
 }
 
 // static
 void ShrinkingGCTimerFired(nsITimer* aTimer, void* aClosure) {
   nsJSContext::KillShrinkingGCTimer();
   sIsCompactingOnUserInactive = true;
@@ -1979,28 +1970,30 @@ void nsJSContext::MaybeRunNextCollectorS
 
   // GetLastUserEventTime returns microseconds.
   uint32_t lastEventTime = 0;
   vm->GetLastUserEventTime(lastEventTime);
   uint32_t currentTime = PR_IntervalToMicroseconds(PR_IntervalNow());
   // Only try to trigger collectors more often if user hasn't interacted with
   // the page for awhile.
   if ((currentTime - lastEventTime) >
-      (NS_USER_INTERACTION_INTERVAL * PR_USEC_PER_MSEC)) {
+      (StaticPrefs::dom_events_user_interaction_interval() *
+       PR_USEC_PER_MSEC)) {
     Maybe<TimeStamp> next = nsRefreshDriver::GetNextTickHint();
     // Try to not delay the next RefreshDriver tick, so give a reasonable
     // deadline for collectors.
     if (next.isSome()) {
       nsJSContext::RunNextCollectorTimer(aReason, next.value());
     }
   }
 }
 
 // static
-void nsJSContext::PokeGC(JS::GCReason aReason, JSObject* aObj, int aDelay) {
+void nsJSContext::PokeGC(JS::GCReason aReason, JSObject* aObj,
+                         uint32_t aDelay) {
   if (sShuttingDown) {
     return;
   }
 
   if (aObj) {
     JS::Zone* zone = JS::GetObjectZone(aObj);
     CycleCollectedJSRuntime::Get()->AddZoneWaitingForGC(zone);
   } else if (aReason != JS::GCReason::CC_WAITING) {
@@ -2026,17 +2019,19 @@ void nsJSContext::PokeGC(JS::GCReason aR
     sNeedsGCAfterCC = true;
     return;
   }
 
   static bool first = true;
 
   NS_NewTimerWithFuncCallback(
       &sGCTimer, GCTimerFired, reinterpret_cast<void*>(aReason),
-      aDelay ? aDelay : (first ? NS_FIRST_GC_DELAY : NS_GC_DELAY),
+      aDelay ? aDelay
+             : (first ? StaticPrefs::javascript_options_gc_delay_first()
+                      : StaticPrefs::javascript_options_gc_delay()),
       nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY, "GCTimerFired",
       SystemGroup::EventTargetFor(TaskCategory::GarbageCollection));
 
   first = false;
 }
 
 // static
 void nsJSContext::PokeShrinkingGC() {
@@ -2222,17 +2217,18 @@ static void DOMGCSliceCallback(JSContext
       sCleanupsSinceLastGC = 0;
       sNeedsFullCC = true;
       sHasRunGC = true;
       nsJSContext::MaybePokeCC();
 
       if (aDesc.isZone_) {
         if (!sFullGCTimer && !sShuttingDown) {
           NS_NewTimerWithFuncCallback(
-              &sFullGCTimer, FullGCTimerFired, nullptr, NS_FULL_GC_DELAY,
+              &sFullGCTimer, FullGCTimerFired, nullptr,
+              StaticPrefs::javascript_options_gc_delay_full(),
               nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY, "FullGCTimerFired",
               SystemGroup::EventTargetFor(TaskCategory::GarbageCollection));
         }
       } else {
         nsJSContext::KillFullGCTimer();
       }
 
       if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
@@ -2261,18 +2257,19 @@ static void DOMGCSliceCallback(JSContext
         // If incremental GC wasn't triggered by GCTimerFired, we may not
         // have a runner to ensure all the slices are handled. So, create
         // the runner here.
         sInterSliceGCRunner = IdleTaskRunner::Create(
             [](TimeStamp aDeadline) {
               return InterSliceGCRunnerFired(aDeadline, nullptr);
             },
             "DOMGCSliceCallback::InterSliceGCRunnerFired",
-            NS_INTERSLICE_GC_DELAY, sActiveIntersliceGCBudget, true,
-            [] { return sShuttingDown; }, TaskCategory::GarbageCollection);
+            StaticPrefs::javascript_options_gc_delay_interslice(),
+            sActiveIntersliceGCBudget, true, [] { return sShuttingDown; },
+            TaskCategory::GarbageCollection);
       }
 
       if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
         nsCycleCollector_dispatchDeferredDeletion();
       }
 
       if (StaticPrefs::javascript_options_mem_log()) {
         nsString gcstats;
--- a/dom/base/nsJSEnvironment.h
+++ b/dom/base/nsJSEnvironment.h
@@ -23,20 +23,16 @@ class nsICycleCollectorListener;
 class nsIDocShell;
 
 namespace mozilla {
 template <class>
 class Maybe;
 struct CycleCollectorResults;
 }  // namespace mozilla
 
-// The amount of time we wait between a request to GC (due to leaving
-// a page) and doing the actual GC.
-#define NS_GC_DELAY 4000  // ms
-
 #define NS_MAJOR_FORGET_SKIPPABLE_CALLS 5
 
 class nsJSContext : public nsIScriptContext {
  public:
   nsJSContext(bool aGCOnDestruction, nsIScriptGlobalObject* aGlobalObject);
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsJSContext,
@@ -97,17 +93,17 @@ class nsJSContext : public nsIScriptCont
       mozilla::TimeStamp aDeadline = mozilla::TimeStamp());
   // If user has been idle and aDocShell is for an iframe being loaded in an
   // already loaded top level docshell, this will run a CC or GC
   // timer/runner if there is such pending.
   static void MaybeRunNextCollectorSlice(nsIDocShell* aDocShell,
                                          JS::GCReason aReason);
 
   // The GC should probably run soon, in the zone of object aObj (if given).
-  static void PokeGC(JS::GCReason aReason, JSObject* aObj, int aDelay = 0);
+  static void PokeGC(JS::GCReason aReason, JSObject* aObj, uint32_t aDelay = 0);
   static void KillGCTimer();
 
   static void PokeShrinkingGC();
   static void KillShrinkingGCTimer();
 
   static void MaybePokeCC();
   static void KillCCRunner();
   static void KillICCRunner();
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -258,19 +258,20 @@ nsresult EventStateManager::UpdateUserAc
   if (!gUserInteractionTimerCallback) return NS_OK;
 
   if (!gUserInteractionTimer) {
     gUserInteractionTimer =
         NS_NewTimer(SystemGroup::EventTargetFor(TaskCategory::Other)).take();
   }
 
   if (gUserInteractionTimer) {
-    gUserInteractionTimer->InitWithCallback(gUserInteractionTimerCallback,
-                                            NS_USER_INTERACTION_INTERVAL,
-                                            nsITimer::TYPE_ONE_SHOT);
+    gUserInteractionTimer->InitWithCallback(
+        gUserInteractionTimerCallback,
+        StaticPrefs::dom_events_user_interaction_interval(),
+        nsITimer::TYPE_ONE_SHOT);
   }
   return NS_OK;
 }
 
 nsresult EventStateManager::Init() {
   nsCOMPtr<nsIObserverService> observerService =
       mozilla::services::GetObserverService();
   if (!observerService) return NS_ERROR_FAILURE;
--- a/dom/events/EventStateManager.h
+++ b/dom/events/EventStateManager.h
@@ -15,18 +15,16 @@
 #include "nsCOMArray.h"
 #include "nsCycleCollectionParticipant.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/layers/APZUtils.h"
 #include "nsIFrame.h"
 #include "Units.h"
 #include "WheelHandlingHelper.h"  // for WheelDeltaAdjustmentStrategy
 
-#define NS_USER_INTERACTION_INTERVAL 5000  // ms
-
 class nsFrameLoader;
 class nsIContent;
 class nsIDocShell;
 class nsIDocShellTreeItem;
 class imgIContainer;
 class nsIContentViewer;
 class nsIScrollableFrame;
 class nsITimer;
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -37,16 +37,17 @@
 #ifdef ACCESSIBILITY
 #  include "mozilla/a11y/DocAccessible.h"
 #endif
 #include "mozilla/BasicEvents.h"
 #include "mozilla/Encoding.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/WeakPtr.h"
+#include "mozilla/StaticPrefs_javascript.h"
 #include "mozilla/StyleSheet.h"
 #include "mozilla/StyleSheetInlines.h"
 
 #include "nsViewManager.h"
 #include "nsView.h"
 
 #include "nsPageSequenceFrame.h"
 #include "nsNetUtil.h"
@@ -1485,17 +1486,18 @@ nsDocumentViewer::PageHide(bool aIsUnloa
 
   if (!mDocument) {
     return NS_ERROR_NULL_POINTER;
   }
 
   if (aIsUnload) {
     // Poke the GC. The window might be collectable garbage now.
     nsJSContext::PokeGC(JS::GCReason::PAGE_HIDE,
-                        mDocument->GetWrapperPreserveColor(), NS_GC_DELAY * 2);
+                        mDocument->GetWrapperPreserveColor(),
+                        StaticPrefs::javascript_options_gc_delay() * 2);
   }
 
   mDocument->OnPageHide(!aIsUnload, nullptr);
 
   // inform the window so that the focus state is reset.
   NS_ENSURE_STATE(mDocument);
   nsPIDOMWindowOuter* window = mDocument->GetWindow();
   if (window) window->PageHidden();
@@ -2369,17 +2371,18 @@ nsDocumentViewer::RequestWindowClose(boo
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocumentViewer::ClearHistoryEntry() {
   if (mDocument) {
     nsJSContext::PokeGC(JS::GCReason::PAGE_HIDE,
-                        mDocument->GetWrapperPreserveColor(), NS_GC_DELAY * 2);
+                        mDocument->GetWrapperPreserveColor(),
+                        StaticPrefs::javascript_options_gc_delay() * 2);
   }
 
   mSHEntry = nullptr;
   return NS_OK;
 }
 
 //-------------------------------------------------------
 
--- a/modules/libpref/init/StaticPrefList.yaml
+++ b/modules/libpref/init/StaticPrefList.yaml
@@ -1393,16 +1393,22 @@
 # enabled. If the `protected` dataTransfer stae is disabled, then the
 # DataTransfer will be read-only whenever it should be protected, and will not
 # be disconnected after a drag event is completed.
 - name: dom.events.dataTransfer.protected.enabled
   type: bool
   value: false
   mirror: always
 
+# User interaction timer interval, in ms
+- name: dom.events.user_interaction_interval
+  type: uint32_t
+  value: 5000
+  mirror: always
+
 # Whether to expose test interfaces of various sorts
 - name: dom.expose_test_interfaces
   type: bool
   value: false
   mirror: always
 
 - name: dom.fetchObserver.enabled
   type: RelaxedAtomicBool
@@ -3694,16 +3700,40 @@
   value: true
   mirror: always
 
 - name: javascript.options.experimental.await_fix
   type: RelaxedAtomicBool
   value: true
   mirror: always
 
+# The amount of time we wait between a request to GC (due to leaving a page) and doing the actual GC, in ms.
+- name: javascript.options.gc_delay
+  type: uint32_t
+  value: 4000
+  mirror: always
+
+# The amount of time we wait from the first request to GC to actually doing the first GC, in ms.
+- name: javascript.options.gc_delay.first
+  type: uint32_t
+  value: 10000
+  mirror: always
+
+# Duration of the dom events full gc delay, in ms.
+- name: javascript.options.gc_delay.full
+  type: uint32_t
+  value: 60000
+  mirror: always
+
+# Maximum amount of time that should elapse between incremental GC slices, in ms.
+- name: javascript.options.gc_delay.interslice
+  type: uint32_t
+  value: 100
+  mirror: always
+
 # nsJSEnvironmentObserver observes the memory-pressure notifications and
 # forces a garbage collection and cycle collection when it happens, if the
 # appropriate pref is set.
 - name: javascript.options.gc_on_memory_pressure
   type: bool
 #ifdef ANDROID
   # Disable the JS engine's GC on memory pressure, since we do one in the
   # mobile browser (bug 669346).