Bug 863526 - Attempt to GC before the StoreBuffer overflows; r=billm
authorTerrence Cole <terrence@mozilla.com>
Thu, 18 Apr 2013 17:03:29 -0700
changeset 130391 8696520f1e90
parent 130390 e0b317249f19
child 130392 c0789f4c3113
push id24614
push userryanvm@gmail.com
push date2013-05-01 01:49 +0000
treeherdermozilla-central@9f86b0aa679e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbillm
bugs863526
milestone23.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 863526 - Attempt to GC before the StoreBuffer overflows; r=billm
js/src/gc/StoreBuffer.cpp
js/src/gc/StoreBuffer.h
js/src/jscntxt.cpp
--- a/js/src/gc/StoreBuffer.cpp
+++ b/js/src/gc/StoreBuffer.cpp
@@ -53,31 +53,38 @@ StoreBuffer::SlotEdge::inRememberedSet(N
 JS_ALWAYS_INLINE bool
 StoreBuffer::SlotEdge::isNullEdge() const
 {
     return !deref();
 }
 
 /*** MonoTypeBuffer ***/
 
+
+/* How full we allow a store buffer to become before we request a MinorGC. */
+const static double HighwaterRatio = 7.0 / 8.0;
+
 template <typename T>
 bool
 StoreBuffer::MonoTypeBuffer<T>::enable(uint8_t *region, size_t len)
 {
     JS_ASSERT(len % sizeof(T) == 0);
     base = pos = reinterpret_cast<T *>(region);
     top = reinterpret_cast<T *>(region + len);
+    highwater = reinterpret_cast<T *>(region + size_t(double(len) * HighwaterRatio));
+    JS_ASSERT(highwater > base);
+    JS_ASSERT(highwater < top);
     return true;
 }
 
 template <typename T>
 void
 StoreBuffer::MonoTypeBuffer<T>::disable()
 {
-    base = pos = top = NULL;
+    base = pos = top = highwater = NULL;
 }
 
 template <typename T>
 void
 StoreBuffer::MonoTypeBuffer<T>::clear()
 {
     pos = base;
 }
@@ -104,39 +111,16 @@ StoreBuffer::MonoTypeBuffer<T>::compact(
         compactNotInSet(&owner->runtime->gcVerifierNursery);
     else
 #endif
         compactNotInSet(&owner->runtime->gcNursery);
 }
 
 template <typename T>
 void
-StoreBuffer::MonoTypeBuffer<T>::put(const T &v)
-{
-    /* Check if we have been enabled. */
-    if (!pos)
-        return;
-
-    /*
-     * Note: it is sometimes valid for a put to happen in the middle of a GC,
-     * e.g. a rekey of a Relocatable may end up here. In general, we do not
-     * care about these new entries or any overflows they cause.
-     */
-    *pos++ = v;
-    if (isFull()) {
-        compact();
-        if (isFull()) {
-            owner->setOverflowed();
-            pos = base;
-        }
-    }
-}
-
-template <typename T>
-void
 StoreBuffer::MonoTypeBuffer<T>::mark(JSTracer *trc)
 {
     compact();
     T *cursor = base;
     while (cursor != pos) {
         T edge = *cursor++;
 
         if (edge.isNullEdge())
@@ -196,23 +180,16 @@ StoreBuffer::RelocatableMonoTypeBuffer<T
 template <typename T>
 void
 StoreBuffer::RelocatableMonoTypeBuffer<T>::compact()
 {
     compactMoved();
     StoreBuffer::MonoTypeBuffer<T>::compact();
 }
 
-template <typename T>
-void
-StoreBuffer::RelocatableMonoTypeBuffer<T>::unput(const T &v)
-{
-    MonoTypeBuffer<T>::put(v.tagged());
-}
-
 /*** GenericBuffer ***/
 
 bool
 StoreBuffer::GenericBuffer::enable(uint8_t *region, size_t len)
 {
     base = pos = region;
     top = region + len;
     return true;
@@ -331,16 +308,18 @@ StoreBuffer::enable()
 }
 
 void
 StoreBuffer::disable()
 {
     if (!enabled)
         return;
 
+    aboutToOverflow = false;
+
     bufferVal.disable();
     bufferCell.disable();
     bufferSlot.disable();
     bufferRelocVal.disable();
     bufferRelocCell.disable();
     bufferGeneric.disable();
 
     js_free(buffer);
@@ -349,16 +328,18 @@ StoreBuffer::disable()
 }
 
 bool
 StoreBuffer::clear()
 {
     if (!enabled)
         return true;
 
+    aboutToOverflow = false;
+
     bufferVal.clear();
     bufferCell.clear();
     bufferSlot.clear();
     bufferRelocVal.clear();
     bufferRelocCell.clear();
     bufferGeneric.clear();
 
     return true;
@@ -374,16 +355,23 @@ StoreBuffer::mark(JSTracer *trc)
     bufferCell.mark(trc);
     bufferSlot.mark(trc);
     bufferRelocVal.mark(trc);
     bufferRelocCell.mark(trc);
     bufferGeneric.mark(trc);
 }
 
 void
+StoreBuffer::setAboutToOverflow()
+{
+    aboutToOverflow = true;
+    runtime->triggerOperationCallback();
+}
+
+void
 StoreBuffer::setOverflowed()
 {
     JS_ASSERT(enabled);
     overflowed = true;
 }
 
 bool
 StoreBuffer::coalesceForVerification()
--- a/js/src/gc/StoreBuffer.h
+++ b/js/src/gc/StoreBuffer.h
@@ -132,41 +132,69 @@ class StoreBuffer
         friend class StoreBuffer;
 
         StoreBuffer *owner;
 
         T *base;      /* Pointer to the start of the buffer. */
         T *pos;       /* Pointer to the current insertion position. */
         T *top;       /* Pointer to one element after the end. */
 
+        /*
+         * If the buffer's insertion position goes over the high-water-mark,
+         * we trigger a minor GC at the next operation callback.
+         */
+        T *highwater;
+
         MonoTypeBuffer(StoreBuffer *owner)
           : owner(owner), base(NULL), pos(NULL), top(NULL)
         {}
 
         MonoTypeBuffer &operator=(const MonoTypeBuffer& other) MOZ_DELETE;
 
         bool enable(uint8_t *region, size_t len);
         void disable();
         void clear();
 
         bool isEmpty() const { return pos == base; }
         bool isFull() const { JS_ASSERT(pos <= top); return pos == top; }
+        bool isAboutToOverflow() const { return pos >= highwater; }
 
         /* Compaction algorithms. */
         template <typename NurseryType>
         void compactNotInSet(NurseryType *nursery);
 
         /*
          * Attempts to reduce the usage of the buffer by removing unnecessary
          * entries.
          */
         virtual void compact();
 
         /* Add one item to the buffer. */
-        void put(const T &v);
+        void put(const T &v) {
+            /* Check if we have been enabled. */
+            if (!pos)
+                return;
+
+            /*
+             * Note: it is sometimes valid for a put to happen in the middle of a GC,
+             * e.g. a rekey of a Relocatable may end up here. In general, we do not
+             * care about these new entries or any overflows they cause.
+             */
+            *pos++ = v;
+            if (isAboutToOverflow()) {
+                owner->setAboutToOverflow();
+                if (isFull()) {
+                    compact();
+                    if (isFull()) {
+                        owner->setOverflowed();
+                        clear();
+                    }
+                }
+            }
+        }
 
         /* Mark the source of all edges in the store buffer. */
         void mark(JSTracer *trc);
 
         /* For verification. */
         bool accumulateEdges(EdgeSet &edges);
     };
 
@@ -183,17 +211,19 @@ class StoreBuffer
           : MonoTypeBuffer<T>(owner)
         {}
 
         /* Override compaction to filter out removed items. */
         void compactMoved();
         virtual void compact();
 
         /* Record a removal from the buffer. */
-        void unput(const T &v);
+        void unput(const T &v) {
+            MonoTypeBuffer<T>::put(v.tagged());
+        }
     };
 
     class GenericBuffer
     {
         friend class StoreBuffer;
 
         StoreBuffer *owner;
 
@@ -340,16 +370,17 @@ class StoreBuffer
     RelocatableMonoTypeBuffer<ValueEdge> bufferRelocVal;
     RelocatableMonoTypeBuffer<CellPtrEdge> bufferRelocCell;
     GenericBuffer bufferGeneric;
 
     JSRuntime *runtime;
 
     void *buffer;
 
+    bool aboutToOverflow;
     bool overflowed;
     bool enabled;
 
     /* For the verifier. */
     EdgeSet edgeSet;
 
 #ifdef JS_GC_ZEAL
     /* For verification, we approximate an infinitly large buffer. */
@@ -368,32 +399,35 @@ class StoreBuffer
     static const size_t RelocCellBufferSize = 1 * 1024 * sizeof(CellPtrEdge);
     static const size_t GenericBufferSize = 1 * 1024 * sizeof(int);
 #endif
     static const size_t TotalSize = ValueBufferSize + CellBufferSize +
                                     SlotBufferSize + RelocValueBufferSize + RelocCellBufferSize +
                                     GenericBufferSize;
 
     /* For use by our owned buffers. */
+    void setAboutToOverflow();
     void setOverflowed();
 
   public:
     explicit StoreBuffer(JSRuntime *rt)
       : bufferVal(this), bufferCell(this), bufferSlot(this),
         bufferRelocVal(this), bufferRelocCell(this), bufferGeneric(this),
-        runtime(rt), buffer(NULL), overflowed(false), enabled(false)
+        runtime(rt), buffer(NULL), aboutToOverflow(false), overflowed(false),
+        enabled(false)
     {}
 
     bool enable();
     void disable();
     bool isEnabled() { return enabled; }
 
     bool clear();
 
     /* Get the overflowed status. */
+    bool isAboutToOverflow() const { return aboutToOverflow; }
     bool hasOverflowed() const { return overflowed; }
 
     /* Insert a single edge into the buffer/remembered set. */
     void putValue(Value *v) {
         bufferVal.put(v);
     }
     void putCell(Cell **o) {
         bufferCell.put(o);
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -1103,16 +1103,21 @@ js_InvokeOperationCallback(JSContext *cx
     JS_ATOMIC_SET(&rt->interrupt, 0);
 
     /* IonMonkey sets its stack limit to NULL to trigger operaton callbacks. */
     rt->resetIonStackLimit();
 
     if (rt->gcIsNeeded)
         GCSlice(rt, GC_NORMAL, rt->gcTriggerReason);
 
+#ifdef JSGC_GENERATIONAL
+    if (rt->gcStoreBuffer.isAboutToOverflow())
+        MinorGC(rt, JS::gcreason::FULL_STORE_BUFFER);
+#endif
+
 #ifdef JS_ION
     /*
      * A worker thread may have set the callback after finishing an Ion
      * compilation.
      */
     ion::AttachFinishedCompilations(cx);
 #endif