Bug 1386660 - Part 3: Provide code to reset SM/GC parameters. r=jonco
authorPaul Bone <pbone@mozilla.com>
Thu, 17 Aug 2017 11:18:10 +1000
changeset 648503 6f05eb69802f47a9e9e8124a77e8a96d2fb12f0a
parent 648502 32e58ff6a1eeced1de880b614f0c5f828eeae7a5
child 648504 a1c53785fd4b6c6ddaf4fc7ea84a8605b1551ff7
push id74775
push userbmo:ted@mielczarek.org
push dateThu, 17 Aug 2017 21:15:41 +0000
reviewersjonco
bugs1386660
milestone57.0a1
Bug 1386660 - Part 3: Provide code to reset SM/GC parameters. r=jonco
js/src/gc/GCRuntime.h
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsgc.cpp
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -211,35 +211,17 @@ class GCSchedulingTunables
 
     /*
      * Controls the number of empty chunks reserved for future allocation.
      */
     UnprotectedData<uint32_t> minEmptyChunkCount_;
     UnprotectedData<uint32_t> maxEmptyChunkCount_;
 
   public:
-    GCSchedulingTunables()
-      : gcMaxBytes_(0),
-        gcMaxNurseryBytes_(0),
-        gcZoneAllocThresholdBase_(30 * 1024 * 1024),
-        zoneAllocThresholdFactor_(0.9f),
-        zoneAllocThresholdFactorAvoidInterrupt_(0.9f),
-        zoneAllocDelayBytes_(1024 * 1024),
-        dynamicHeapGrowthEnabled_(false),
-        highFrequencyThresholdUsec_(1000 * 1000),
-        highFrequencyLowLimitBytes_(100 * 1024 * 1024),
-        highFrequencyHighLimitBytes_(500 * 1024 * 1024),
-        highFrequencyHeapGrowthMax_(3.0),
-        highFrequencyHeapGrowthMin_(1.5),
-        lowFrequencyHeapGrowth_(1.5),
-        dynamicMarkSliceEnabled_(false),
-        refreshFrameSlicesEnabled_(true),
-        minEmptyChunkCount_(1),
-        maxEmptyChunkCount_(30)
-    {}
+    GCSchedulingTunables();
 
     size_t gcMaxBytes() const { return gcMaxBytes_; }
     size_t gcMaxNurseryBytes() const { return gcMaxNurseryBytes_; }
     size_t gcZoneAllocThresholdBase() const { return gcZoneAllocThresholdBase_; }
     float zoneAllocThresholdFactor() const { return zoneAllocThresholdFactor_; }
     float zoneAllocThresholdFactorAvoidInterrupt() const { return zoneAllocThresholdFactorAvoidInterrupt_; }
     size_t zoneAllocDelayBytes() const { return zoneAllocDelayBytes_; }
     bool isDynamicHeapGrowthEnabled() const { return dynamicHeapGrowthEnabled_; }
@@ -250,16 +232,23 @@ class GCSchedulingTunables
     double highFrequencyHeapGrowthMin() const { return highFrequencyHeapGrowthMin_; }
     double lowFrequencyHeapGrowth() const { return lowFrequencyHeapGrowth_; }
     bool isDynamicMarkSliceEnabled() const { return dynamicMarkSliceEnabled_; }
     bool areRefreshFrameSlicesEnabled() const { return refreshFrameSlicesEnabled_; }
     unsigned minEmptyChunkCount(const AutoLockGC&) const { return minEmptyChunkCount_; }
     unsigned maxEmptyChunkCount() const { return maxEmptyChunkCount_; }
 
     MOZ_MUST_USE bool setParameter(JSGCParamKey key, uint32_t value, const AutoLockGC& lock);
+    void resetParameter(JSGCParamKey key, const AutoLockGC& lock);
+
+private:
+    void setHighFrequencyLowLimit(uint64_t value);
+    void setHighFrequencyHighLimit(uint64_t value);
+    void setMinEmptyChunkCount(uint32_t value);
+    void setMaxEmptyChunkCount(uint32_t value);
 };
 
 /*
  * GC Scheduling Overview
  * ======================
  *
  * Scheduling GC's in SpiderMonkey/Firefox is tremendously complicated because
  * of the large number of subtle, cross-cutting, and widely dispersed factors
@@ -695,16 +684,17 @@ class GCRuntime
     inline bool upcomingZealousGC();
     inline bool needZealousGC();
 
     MOZ_MUST_USE bool addRoot(Value* vp, const char* name);
     void removeRoot(Value* vp);
     void setMarkStackLimit(size_t limit, AutoLockGC& lock);
 
     MOZ_MUST_USE bool setParameter(JSGCParamKey key, uint32_t value, AutoLockGC& lock);
+    void resetParameter(JSGCParamKey key, AutoLockGC& lock);
     uint32_t getParameter(JSGCParamKey key, const AutoLockGC& lock);
 
     MOZ_MUST_USE bool triggerGC(JS::gcreason::Reason reason);
     void maybeAllocTriggerZoneGC(Zone* zone, const AutoLockGC& lock);
     // The return value indicates if we were able to do the GC.
     bool triggerZoneGC(Zone* zone, JS::gcreason::Reason reason,
                        size_t usedBytes, size_t thresholdBytes);
     void maybeGC(Zone* zone);
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1472,16 +1472,24 @@ JS_UpdateWeakPointerAfterGCUnbarriered(J
 JS_PUBLIC_API(void)
 JS_SetGCParameter(JSContext* cx, JSGCParamKey key, uint32_t value)
 {
     cx->runtime()->gc.waitBackgroundSweepEnd();
     AutoLockGC lock(cx->runtime());
     MOZ_ALWAYS_TRUE(cx->runtime()->gc.setParameter(key, value, lock));
 }
 
+JS_PUBLIC_API(void)
+JS_ResetGCParameter(JSContext* cx, JSGCParamKey key)
+{
+    cx->runtime()->gc.waitBackgroundSweepEnd();
+    AutoLockGC lock(cx->runtime());
+    cx->runtime()->gc.resetParameter(key, lock);
+}
+
 JS_PUBLIC_API(uint32_t)
 JS_GetGCParameter(JSContext* cx, JSGCParamKey key)
 {
     AutoLockGC lock(cx->runtime());
     return cx->runtime()->gc.getParameter(key, lock);
 }
 
 static const size_t NumGCConfigs = 14;
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1870,16 +1870,19 @@ typedef enum JSGCParamKey {
 
     /** If true, painting can trigger IGC slices. */
     JSGC_REFRESH_FRAME_SLICES_ENABLED = 24,
 } JSGCParamKey;
 
 extern JS_PUBLIC_API(void)
 JS_SetGCParameter(JSContext* cx, JSGCParamKey key, uint32_t value);
 
+extern JS_PUBLIC_API(void)
+JS_ResetGCParameter(JSContext* cx, JSGCParamKey key);
+
 extern JS_PUBLIC_API(uint32_t)
 JS_GetGCParameter(JSContext* cx, JSGCParamKey key);
 
 extern JS_PUBLIC_API(void)
 JS_SetGCParametersBasedOnAvailableMemory(JSContext* cx, uint32_t availMem);
 
 /**
  * Create a new JSString whose chars member refers to external memory, i.e.,
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1229,30 +1229,24 @@ GCSchedulingTunables::setParameter(JSGCP
         break;
       case JSGC_HIGH_FREQUENCY_TIME_LIMIT:
         highFrequencyThresholdUsec_ = value * PRMJ_USEC_PER_MSEC;
         break;
       case JSGC_HIGH_FREQUENCY_LOW_LIMIT: {
         uint64_t newLimit = (uint64_t)value * 1024 * 1024;
         if (newLimit == UINT64_MAX)
             return false;
-        highFrequencyLowLimitBytes_ = newLimit;
-        if (highFrequencyLowLimitBytes_ >= highFrequencyHighLimitBytes_)
-            highFrequencyHighLimitBytes_ = highFrequencyLowLimitBytes_ + 1;
-        MOZ_ASSERT(highFrequencyHighLimitBytes_ > highFrequencyLowLimitBytes_);
+        setHighFrequencyLowLimit(newLimit);
         break;
       }
       case JSGC_HIGH_FREQUENCY_HIGH_LIMIT: {
         uint64_t newLimit = (uint64_t)value * 1024 * 1024;
         if (newLimit == 0)
             return false;
-        highFrequencyHighLimitBytes_ = newLimit;
-        if (highFrequencyHighLimitBytes_ <= highFrequencyLowLimitBytes_)
-            highFrequencyLowLimitBytes_ = highFrequencyHighLimitBytes_ - 1;
-        MOZ_ASSERT(highFrequencyHighLimitBytes_ > highFrequencyLowLimitBytes_);
+        setHighFrequencyHighLimit(newLimit);
         break;
       }
       case JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX: {
         double newGrowth = value / 100.0;
         if (newGrowth <= 0.85 || newGrowth > MaxHeapGrowthFactor)
             return false;
         highFrequencyHeapGrowthMax_ = newGrowth;
         MOZ_ASSERT(highFrequencyHeapGrowthMax_ / 0.85 > 1.0);
@@ -1279,37 +1273,189 @@ GCSchedulingTunables::setParameter(JSGCP
         break;
       case JSGC_DYNAMIC_MARK_SLICE:
         dynamicMarkSliceEnabled_ = value != 0;
         break;
       case JSGC_ALLOCATION_THRESHOLD:
         gcZoneAllocThresholdBase_ = value * 1024 * 1024;
         break;
       case JSGC_MIN_EMPTY_CHUNK_COUNT:
-        minEmptyChunkCount_ = value;
-        if (minEmptyChunkCount_ > maxEmptyChunkCount_)
-            maxEmptyChunkCount_ = minEmptyChunkCount_;
-        MOZ_ASSERT(maxEmptyChunkCount_ >= minEmptyChunkCount_);
+        setMinEmptyChunkCount(value);
         break;
       case JSGC_MAX_EMPTY_CHUNK_COUNT:
-        maxEmptyChunkCount_ = value;
-        if (minEmptyChunkCount_ > maxEmptyChunkCount_)
-            minEmptyChunkCount_ = maxEmptyChunkCount_;
-        MOZ_ASSERT(maxEmptyChunkCount_ >= minEmptyChunkCount_);
+        setMaxEmptyChunkCount(value);
         break;
       case JSGC_REFRESH_FRAME_SLICES_ENABLED:
         refreshFrameSlicesEnabled_ = value != 0;
         break;
       default:
         MOZ_CRASH("Unknown GC parameter.");
     }
 
     return true;
 }
 
+void
+GCSchedulingTunables::setHighFrequencyLowLimit(uint64_t newLimit)
+{
+    highFrequencyLowLimitBytes_ = newLimit;
+    if (highFrequencyLowLimitBytes_ >= highFrequencyHighLimitBytes_)
+        highFrequencyHighLimitBytes_ = highFrequencyLowLimitBytes_ + 1;
+    MOZ_ASSERT(highFrequencyHighLimitBytes_ > highFrequencyLowLimitBytes_);
+}
+
+void
+GCSchedulingTunables::setHighFrequencyHighLimit(uint64_t newLimit)
+{
+    highFrequencyHighLimitBytes_ = newLimit;
+    if (highFrequencyHighLimitBytes_ <= highFrequencyLowLimitBytes_)
+        highFrequencyLowLimitBytes_ = highFrequencyHighLimitBytes_ - 1;
+    MOZ_ASSERT(highFrequencyHighLimitBytes_ > highFrequencyLowLimitBytes_);
+}
+
+void
+GCSchedulingTunables::setMinEmptyChunkCount(uint32_t value)
+{
+    minEmptyChunkCount_ = value;
+    if (minEmptyChunkCount_ > maxEmptyChunkCount_)
+        maxEmptyChunkCount_ = minEmptyChunkCount_;
+    MOZ_ASSERT(maxEmptyChunkCount_ >= minEmptyChunkCount_);
+}
+
+void
+GCSchedulingTunables::setMaxEmptyChunkCount(uint32_t value)
+{
+    maxEmptyChunkCount_ = value;
+    if (minEmptyChunkCount_ > maxEmptyChunkCount_)
+        minEmptyChunkCount_ = maxEmptyChunkCount_;
+    MOZ_ASSERT(maxEmptyChunkCount_ >= minEmptyChunkCount_);
+}
+
+static const size_t GCZoneAllocThresholdBaseDefault = 30 * 1024 * 1024;
+static const float ZoneAllocThresholdFactorDefault = 0.9f;
+static const float ZoneAllocThresholdFactorAvoidInterruptDefault = 0.9f;
+static const size_t ZoneAllocDelayBytesDefault = 1024 * 1024;
+static const bool DynamicHeapGrowthEnabledDefault = false;
+static const uint64_t HighFrequencyThresholdUsecDefault = 1000000;
+static const uint64_t HighFrequencyLowLimitBytesDefault = 100 * 1024 * 1024;
+static const uint64_t HighFrequencyHighLimitBytesDefault = 500 * 1024 * 1024;
+static const double HighFrequencyHeapGrowthMaxDefault = 3.0;
+static const double HighFrequencyHeapGrowthMinDefault = 1.5;
+static const double LowFrequencyHeapGrowthDefault = 1.5;
+static const bool DynamicMarkSliceEnabledDefault = false;
+static const bool RefreshFrameSlicesEnabledDefault = true;
+static const uint32_t MinEmptyChunkCountDefault = 1;
+static const uint32_t MaxEmptyChunkCountDefault = 30;
+
+GCSchedulingTunables::GCSchedulingTunables()
+  : gcMaxBytes_(0),
+    gcMaxNurseryBytes_(0),
+    gcZoneAllocThresholdBase_(GCZoneAllocThresholdBaseDefault),
+    zoneAllocThresholdFactor_(ZoneAllocThresholdFactorDefault),
+    zoneAllocThresholdFactorAvoidInterrupt_(
+        ZoneAllocThresholdFactorAvoidInterruptDefault),
+    zoneAllocDelayBytes_(ZoneAllocDelayBytesDefault),
+    dynamicHeapGrowthEnabled_(DynamicHeapGrowthEnabledDefault),
+    highFrequencyThresholdUsec_(HighFrequencyThresholdUsecDefault),
+    highFrequencyLowLimitBytes_(HighFrequencyLowLimitBytesDefault),
+    highFrequencyHighLimitBytes_(HighFrequencyHighLimitBytesDefault),
+    highFrequencyHeapGrowthMax_(HighFrequencyHeapGrowthMaxDefault),
+    highFrequencyHeapGrowthMin_(HighFrequencyHeapGrowthMinDefault),
+    lowFrequencyHeapGrowth_(LowFrequencyHeapGrowthDefault),
+    dynamicMarkSliceEnabled_(DynamicMarkSliceEnabledDefault),
+    refreshFrameSlicesEnabled_(RefreshFrameSlicesEnabledDefault),
+    minEmptyChunkCount_(MinEmptyChunkCountDefault),
+    maxEmptyChunkCount_(MaxEmptyChunkCountDefault)
+{}
+
+void
+GCRuntime::resetParameter(JSGCParamKey key, AutoLockGC& lock)
+{
+    switch (key) {
+      case JSGC_MAX_MALLOC_BYTES:
+        setMaxMallocBytes(0xffffffff);
+        for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next())
+            zone->setGCMaxMallocBytes(maxMallocBytesAllocated() * 0.9);
+        break;
+      case JSGC_SLICE_TIME_BUDGET:
+        defaultTimeBudget_ =
+            static_cast<int64_t>(SliceBudget::UnlimitedTimeBudget);
+        break;
+      case JSGC_MARK_STACK_LIMIT:
+        setMarkStackLimit(size_t(-1), lock);
+        break;
+      case JSGC_MODE:
+        mode = JSGC_MODE_INCREMENTAL;
+        break;
+      case JSGC_COMPACTING_ENABLED:
+        compactingEnabled = true;
+        break;
+      default:
+        tunables.resetParameter(key, lock);
+        for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
+            zone->threshold.updateAfterGC(zone->usage.gcBytes(), GC_NORMAL,
+                tunables, schedulingState, lock);
+        }
+    }
+}
+
+void
+GCSchedulingTunables::resetParameter(JSGCParamKey key, const AutoLockGC& lock)
+{
+    switch(key) {
+      case JSGC_MAX_BYTES:
+        gcMaxBytes_ = 0xffffffff;
+        break;
+      case JSGC_MAX_NURSERY_BYTES:
+        gcMaxNurseryBytes_ = JS::DefaultNurseryBytes;
+        break;
+      case JSGC_HIGH_FREQUENCY_TIME_LIMIT:
+        highFrequencyThresholdUsec_ = HighFrequencyThresholdUsecDefault;
+        break;
+      case JSGC_HIGH_FREQUENCY_LOW_LIMIT:
+        setHighFrequencyLowLimit(HighFrequencyLowLimitBytesDefault);
+        break;
+      case JSGC_HIGH_FREQUENCY_HIGH_LIMIT:
+        setHighFrequencyHighLimit(HighFrequencyHighLimitBytesDefault);
+        break;
+      case JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX:
+        highFrequencyHeapGrowthMax_ = HighFrequencyHeapGrowthMaxDefault;
+        MOZ_ASSERT(highFrequencyHeapGrowthMax_ / 0.85 > 1.0);
+        break;
+      case JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN:
+        highFrequencyHeapGrowthMin_ = HighFrequencyHeapGrowthMinDefault;
+        MOZ_ASSERT(highFrequencyHeapGrowthMin_ / 0.85 > 1.0);
+        break;
+      case JSGC_LOW_FREQUENCY_HEAP_GROWTH:
+        lowFrequencyHeapGrowth_ = LowFrequencyHeapGrowthDefault;
+        MOZ_ASSERT(lowFrequencyHeapGrowth_ / 0.9 > 1.0);
+        break;
+      case JSGC_DYNAMIC_HEAP_GROWTH:
+        dynamicHeapGrowthEnabled_ = DynamicHeapGrowthEnabledDefault;
+        break;
+      case JSGC_DYNAMIC_MARK_SLICE:
+        dynamicMarkSliceEnabled_ = DynamicMarkSliceEnabledDefault;
+        break;
+      case JSGC_ALLOCATION_THRESHOLD:
+        gcZoneAllocThresholdBase_ = GCZoneAllocThresholdBaseDefault;
+        break;
+      case JSGC_MIN_EMPTY_CHUNK_COUNT:
+        setMinEmptyChunkCount(MinEmptyChunkCountDefault);
+        break;
+      case JSGC_MAX_EMPTY_CHUNK_COUNT:
+        setMaxEmptyChunkCount(MaxEmptyChunkCountDefault);
+        break;
+      case JSGC_REFRESH_FRAME_SLICES_ENABLED:
+        refreshFrameSlicesEnabled_ = RefreshFrameSlicesEnabledDefault;
+        break;
+      default:
+        MOZ_CRASH("Unknown GC parameter.");
+    }
+}
+
 uint32_t
 GCRuntime::getParameter(JSGCParamKey key, const AutoLockGC& lock)
 {
     switch (key) {
       case JSGC_MAX_BYTES:
         return uint32_t(tunables.gcMaxBytes());
       case JSGC_MAX_MALLOC_BYTES:
         return mallocCounter.maxBytes();