Bug 1298018 - Part 3: Replace updateNumChunks(). r=jonco
authorPaul Bone <pbone@mozilla.com>
Mon, 30 Oct 2017 13:17:11 +1100
changeset 443521 bf065ffab5f7649ec7724ad226e97a853715497a
parent 443520 7434bbd4eee44b52e7bc43926c8444400391ee9c
child 443522 d8dbac587181077425996af8a29fb3df03765614
push id1618
push userCallek@gmail.com
push dateThu, 11 Jan 2018 17:45:48 +0000
treeherdermozilla-release@882ca853e05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjonco
bugs1298018
milestone58.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 1298018 - Part 3: Replace updateNumChunks(). r=jonco This method both shrinks and grows the nursery. This change replaces it with specialised code to either shrink or grow the nursery.
js/src/gc/Nursery.cpp
js/src/gc/Nursery.h
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -141,18 +141,17 @@ js::Nursery::init(uint32_t maxNurseryByt
 
     /* maxNurseryBytes parameter is rounded down to a multiple of chunk size. */
     maxNurseryChunks_ = maxNurseryBytes >> ChunkShift;
 
     /* If no chunks are specified then the nursery is permanently disabled. */
     if (maxNurseryChunks_ == 0)
         return true;
 
-    updateNumChunksLocked(1, lock);
-    if (numChunks() == 0)
+    if (!allocateFirstChunk(lock))
         return false;
 
     setCurrentChunk(0);
     setStartPosition();
 
     char* env = getenv("JS_GC_PROFILE_NURSERY");
     if (env) {
         if (0 == strcmp(env, "help")) {
@@ -190,19 +189,21 @@ js::Nursery::~Nursery()
 void
 js::Nursery::enable()
 {
     MOZ_ASSERT(isEmpty());
     MOZ_ASSERT(!runtime()->gc.isVerifyPreBarriersEnabled());
     if (isEnabled() || !maxChunks())
         return;
 
-    updateNumChunks(1);
-    if (numChunks() == 0)
-        return;
+    {
+        AutoLockGCBgAlloc lock(runtime());
+        if (!allocateFirstChunk(lock))
+            return;
+    }
 
     setCurrentChunk(0);
     setStartPosition();
 #ifdef JS_GC_ZEAL
     if (runtime()->hasZealMode(ZealMode::GenerationalGC))
         enterZealMode();
 #endif
 
@@ -210,17 +211,18 @@ js::Nursery::enable()
 }
 
 void
 js::Nursery::disable()
 {
     MOZ_ASSERT(isEmpty());
     if (!isEnabled())
         return;
-    updateNumChunks(0);
+
+    freeChunksFrom(0);
     currentEnd_ = 0;
     runtime()->gc.storeBuffer().disable();
 }
 
 bool
 js::Nursery::isEmpty() const
 {
     if (!isEnabled())
@@ -232,17 +234,17 @@ js::Nursery::isEmpty() const
     }
     return position() == currentStartPosition_;
 }
 
 #ifdef JS_GC_ZEAL
 void
 js::Nursery::enterZealMode() {
     if (isEnabled())
-        updateNumChunks(maxNurseryChunks_);
+        growAllocableSpace(maxChunks());
 }
 
 void
 js::Nursery::leaveZealMode() {
     if (isEnabled()) {
         MOZ_ASSERT(isEmpty());
         setCurrentChunk(0);
         setStartPosition();
@@ -996,103 +998,129 @@ js::Nursery::maybeResizeNursery(JS::gcre
      */
     const float promotionRate =
         float(previousGC.tenuredBytes) / float(previousGC.nurseryCapacity);
 
     newMaxNurseryChunks = runtime()->gc.tunables.gcMaxNurseryBytes() >> ChunkShift;
     if (newMaxNurseryChunks != maxNurseryChunks_) {
         maxNurseryChunks_ = newMaxNurseryChunks;
         /* The configured maximum nursery size is changing */
-        const int extraChunks = numChunks() - newMaxNurseryChunks;
-        if (extraChunks > 0) {
+        if (numChunks() > newMaxNurseryChunks) {
             /* We need to shrink the nursery */
-            shrinkAllocableSpace(extraChunks);
+            shrinkAllocableSpace(newMaxNurseryChunks);
 
             previousPromotionRate_ = promotionRate;
             return;
         }
     }
 
-    if (promotionRate > GrowThreshold)
+    if (promotionRate > GrowThreshold) {
+        // The GC nursery is an optimization and so if we fail to allocate
+        // nursery chunks we do not report an error.
         growAllocableSpace();
-    else if (promotionRate < ShrinkThreshold && previousPromotionRate_ < ShrinkThreshold)
-        shrinkAllocableSpace(1);
+    } else if (promotionRate < ShrinkThreshold && previousPromotionRate_ < ShrinkThreshold) {
+        shrinkAllocableSpace(numChunks() - 1);
+    }
 
     previousPromotionRate_ = promotionRate;
 }
 
-void
+bool
 js::Nursery::growAllocableSpace()
 {
-    updateNumChunks(Min(numChunks() * 2, maxNurseryChunks_));
+    return growAllocableSpace(Min(numChunks() * 2, maxChunks()));
+}
+
+bool
+js::Nursery::growAllocableSpace(unsigned newCount)
+{
+    unsigned priorCount = numChunks();
+
+    if (newCount == priorCount)
+        return false;
+
+    MOZ_ASSERT(newCount > priorCount);
+    MOZ_ASSERT(newCount <= maxChunks());
+    MOZ_ASSERT(priorCount >= 1);
+
+    if (!chunks_.resize(newCount))
+        return false;
+
+    AutoLockGCBgAlloc lock(runtime());
+    for (unsigned i = priorCount; i < newCount; i++) {
+        auto newChunk = runtime()->gc.getOrAllocChunk(lock);
+        if (!newChunk) {
+            chunks_.shrinkTo(i);
+            return false;
+        }
+
+        chunks_[i] = NurseryChunk::fromChunk(newChunk);
+        chunk(i).poisonAndInit(runtime(), JS_FRESH_NURSERY_PATTERN);
+    }
+
+    return true;
 }
 
 void
-js::Nursery::shrinkAllocableSpace(unsigned removeNumChunks)
+js::Nursery::freeChunksFrom(unsigned firstFreeChunk)
 {
-#ifdef JS_GC_ZEAL
-    if (runtime()->hasZealMode(ZealMode::GenerationalGC))
-        return;
-#endif
-    updateNumChunks(Max(numChunks() - removeNumChunks, 1u));
+    MOZ_ASSERT(firstFreeChunk < chunks_.length());
+    {
+        AutoLockGC lock(runtime());
+        for (unsigned i = firstFreeChunk; i < chunks_.length(); i++)
+            runtime()->gc.recycleChunk(chunk(i).toChunk(runtime()), lock);
+    }
+    chunks_.shrinkTo(firstFreeChunk);
 }
 
 void
-js::Nursery::minimizeAllocableSpace()
+js::Nursery::shrinkAllocableSpace(unsigned newCount)
 {
 #ifdef JS_GC_ZEAL
     if (runtime()->hasZealMode(ZealMode::GenerationalGC))
         return;
 #endif
-    updateNumChunks(1);
-}
 
-void
-js::Nursery::updateNumChunks(unsigned newCount)
-{
-    if (numChunks() != newCount) {
-        AutoLockGCBgAlloc lock(runtime());
-        updateNumChunksLocked(newCount, lock);
-    }
+    // Don't shrink the nursery to zero (use Nursery::disable() instead) and
+    // don't attempt to shrink it to the same size.
+    if ((newCount == 0) || (newCount == numChunks()))
+        return;
+
+    MOZ_ASSERT(newCount < numChunks());
+
+    freeChunksFrom(newCount);
 }
 
 void
-js::Nursery::updateNumChunksLocked(unsigned newCount,
-                                   AutoLockGCBgAlloc& lock)
+js::Nursery::minimizeAllocableSpace()
 {
-    // The GC nursery is an optimization and so if we fail to allocate nursery
-    // chunks we do not report an error.
-
-    MOZ_ASSERT(newCount <= maxChunks());
+    shrinkAllocableSpace(1);
+}
 
-    unsigned priorCount = numChunks();
-    MOZ_ASSERT(priorCount != newCount);
+bool
+js::Nursery::allocateFirstChunk(AutoLockGCBgAlloc& lock)
+{
+    // This assertion isn't required for correctness, but we do assume this
+    // is only called to initialize or re-enable the nursery.
+    MOZ_ASSERT(numChunks() == 0);
 
-    if (newCount < priorCount) {
-        // Shrink the nursery and free unused chunks.
-        for (unsigned i = newCount; i < priorCount; i++)
-            runtime()->gc.recycleChunk(chunk(i).toChunk(runtime()), lock);
-        chunks_.shrinkTo(newCount);
-        return;
+    MOZ_ASSERT(maxChunks() > 0);
+
+    if (!chunks_.resize(1))
+        return false;
+
+    auto chunk = runtime()->gc.getOrAllocChunk(lock);
+    if (!chunk) {
+        chunks_.shrinkTo(0);
+        return false;
     }
 
-    // Grow the nursery and allocate new chunks.
-    if (!chunks_.resize(newCount))
-        return;
+    chunks_[0] = NurseryChunk::fromChunk(chunk);
 
-    for (unsigned i = priorCount; i < newCount; i++) {
-        auto newChunk = runtime()->gc.getOrAllocChunk(lock);
-        if (!newChunk) {
-            chunks_.shrinkTo(i);
-            return;
-        }
-
-        chunks_[i] = NurseryChunk::fromChunk(newChunk);
-        chunk(i).poisonAndInit(runtime(), JS_FRESH_NURSERY_PATTERN);
-    }
+    return true;
 }
 
 bool
 js::Nursery::queueDictionaryModeObjectToSweep(NativeObject* obj)
 {
     MOZ_ASSERT(IsInsideNursery(obj));
     return dictionaryModeObjects_.append(obj);
 }
--- a/js/src/gc/Nursery.h
+++ b/js/src/gc/Nursery.h
@@ -420,19 +420,21 @@ class Nursery
 
     NurseryChunk& chunk(unsigned index) const {
         return *chunks_[index];
     }
 
     void setCurrentChunk(unsigned chunkno);
     void setStartPosition();
 
-    void updateNumChunks(unsigned newCount);
-    void updateNumChunksLocked(unsigned newCount,
-                               AutoLockGCBgAlloc& lock);
+    /*
+     * Ensure that the first chunk has been allocated. Callers will probably
+     * want to call setCurrentChunk(0) next.
+     */
+    MOZ_MUST_USE bool allocateFirstChunk(AutoLockGCBgAlloc& lock);
 
     MOZ_ALWAYS_INLINE uintptr_t currentEnd() const;
 
     uintptr_t position() const { return position_; }
 
     JSRuntime* runtime() const { return runtime_; }
 
     /* Allocates a new GC thing from the tenured generation during minor GC. */
@@ -474,20 +476,25 @@ class Nursery
      * collection.
      */
     void clear();
 
     void sweepDictionaryModeObjects();
 
     /* Change the allocable space provided by the nursery. */
     void maybeResizeNursery(JS::gcreason::Reason reason);
-    void growAllocableSpace();
-    void shrinkAllocableSpace(unsigned removeNumChunks);
+    bool growAllocableSpace();
+    bool growAllocableSpace(unsigned newSize);
+    void shrinkAllocableSpace(unsigned newCount);
     void minimizeAllocableSpace();
 
+    // Free the chunks starting at firstFreeChunk until the end of the chunks
+    // vector. Shrinks the vector but does not update maxChunksAlloc().
+    void freeChunksFrom(unsigned firstFreeChunk);
+
     /* Profile recording and printing. */
     void maybeClearProfileDurations();
     void startProfile(ProfileKey key);
     void endProfile(ProfileKey key);
     static void printProfileDurations(const ProfileDurations& times);
 
     friend class TenuringTracer;
     friend class gc::MinorCollectionTracer;