Bug 1459761 - Baldr: relax WebAssembly.Memory GC heuristic (r=lth,jonco)
authorLuke Wagner <luke@mozilla.com>
Mon, 14 May 2018 09:21:46 -0500
changeset 418182 5c0629a808fe23cc67a1e98a75f57647cb3087db
parent 418141 06f48abf78dcbf37c786d500dd840bbdab334c89
child 418183 436f28e55ce42f53ac4b9e91b6635426d1f320b4
push id33995
push usernbeleuzu@mozilla.com
push dateMon, 14 May 2018 21:37:08 +0000
treeherdermozilla-central@a382f8feaba4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslth, jonco
bugs1459761
milestone62.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 1459761 - Baldr: relax WebAssembly.Memory GC heuristic (r=lth,jonco)
js/public/GCAPI.h
js/src/gc/GC.cpp
js/src/vm/ArrayBufferObject.cpp
--- a/js/public/GCAPI.h
+++ b/js/public/GCAPI.h
@@ -351,28 +351,28 @@ namespace JS {
     D(INCREMENTAL_TOO_SLOW)                     \
     D(ABORT_GC)                                 \
     D(FULL_WHOLE_CELL_BUFFER)                   \
     D(FULL_GENERIC_BUFFER)                      \
     D(FULL_VALUE_BUFFER)                        \
     D(FULL_CELL_PTR_BUFFER)                     \
     D(FULL_SLOT_BUFFER)                         \
     D(FULL_SHAPE_BUFFER)                        \
+    D(TOO_MUCH_WASM_MEMORY)                     \
                                                 \
     /* These are reserved for future use. */    \
     D(RESERVED0)                                \
     D(RESERVED1)                                \
     D(RESERVED2)                                \
     D(RESERVED3)                                \
     D(RESERVED4)                                \
     D(RESERVED5)                                \
     D(RESERVED6)                                \
     D(RESERVED7)                                \
     D(RESERVED8)                                \
-    D(RESERVED9)                                \
                                                 \
     /* Reasons from Firefox */                  \
     D(DOM_WINDOW_UTILS)                         \
     D(COMPONENT_UTILS)                          \
     D(MEM_PRESSURE)                             \
     D(CC_WAITING)                               \
     D(CC_FORCED)                                \
     D(LOAD_END)                                 \
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -7491,16 +7491,17 @@ GCRuntime::gcCycle(bool nonincrementalBy
 static bool
 IsDeterministicGCReason(JS::gcreason::Reason reason)
 {
     switch (reason) {
       case JS::gcreason::API:
       case JS::gcreason::DESTROY_RUNTIME:
       case JS::gcreason::LAST_DITCH:
       case JS::gcreason::TOO_MUCH_MALLOC:
+      case JS::gcreason::TOO_MUCH_WASM_MEMORY:
       case JS::gcreason::ALLOC_TRIGGER:
       case JS::gcreason::DEBUG_GC:
       case JS::gcreason::CC_FORCED:
       case JS::gcreason::SHUTDOWN_CC:
       case JS::gcreason::ABORT_GC:
         return true;
 
       default:
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -49,20 +49,22 @@
 #include "gc/Marking-inl.h"
 #include "gc/Nursery-inl.h"
 #include "vm/JSAtom-inl.h"
 #include "vm/NativeObject-inl.h"
 #include "vm/Shape-inl.h"
 
 using JS::ToInt32;
 
+using mozilla::Atomic;
 using mozilla::CheckedInt;
 using mozilla::Some;
 using mozilla::Maybe;
 using mozilla::Nothing;
+using mozilla::Unused;
 
 using namespace js;
 using namespace js::gc;
 
 /*
  * Convert |v| to an array index for an array of length |length| per
  * the Typed Array Specification section 7.0, |subarray|. If successful,
  * the output value is in the range [0, length].
@@ -86,19 +88,29 @@ js::ToClampedIndex(JSContext* cx, Handle
 
 // If there are too many 4GB buffers live we run up against system resource
 // exhaustion (address space or number of memory map descriptors), see
 // bug 1068684, bug 1073934 for details.  The limiting case seems to be
 // Windows Vista Home 64-bit, where the per-process address space is limited
 // to 8TB.  Thus we track the number of live objects, and set a limit of
 // 1000 live objects per process; we run synchronous GC if necessary; and
 // we throw an OOM error if the per-process limit is exceeded.
-static mozilla::Atomic<int32_t, mozilla::ReleaseAcquire> liveBufferCount(0);
+//
+// Since the MaximumLiveMappedBuffers limit is not generally accounted for by
+// any existing GC-trigger heuristics, we need an extra heuristic for triggering
+// GCs when the caller is allocating memories rapidly without other garbage.
+// Thus, once the live buffer count crosses a certain threshold, we start
+// triggering GCs every N allocations.
 
 static const int32_t MaximumLiveMappedBuffers = 1000;
+static const int32_t StartTriggeringAtLiveBufferCount = 200;
+static const int32_t AllocatedBuffersPerTrigger = 100;
+
+static Atomic<int32_t, mozilla::ReleaseAcquire> liveBufferCount(0);
+static Atomic<int32_t, mozilla::ReleaseAcquire> allocatedSinceLastTrigger(0);
 
 int32_t
 js::LiveMappedBufferCount()
 {
     return liveBufferCount;
 }
 
 void*
@@ -667,20 +679,16 @@ class js::WasmArrayRawBuffer
     size_t mappedSize() const {
         return mappedSize_;
     }
 
     Maybe<uint32_t> maxSize() const {
         return maxSize_;
     }
 
-    size_t allocatedBytes() const {
-        return mappedSize_ + gc::SystemPageSize();
-    }
-
 #ifndef WASM_HUGE_MEMORY
     uint32_t boundsCheckLimit() const {
         MOZ_ASSERT(mappedSize_ <= UINT32_MAX);
         MOZ_ASSERT(mappedSize_ >= wasm::GuardSize);
         MOZ_ASSERT(wasm::IsValidBoundsCheckImmediate(mappedSize_ - wasm::GuardSize));
         return mappedSize_ - wasm::GuardSize;
     }
 #endif
@@ -824,16 +832,28 @@ CreateBuffer(JSContext* cx, uint32_t ini
 
     // ObjT::createFromNewRawBuffer assumes ownership of |buffer| even in case
     // of failure.
     ObjT* object = ObjT::createFromNewRawBuffer(cx, buffer, initialSize);
     if (!object)
         return false;
 
     maybeSharedObject.set(object);
+
+    // See StartTriggeringAtLiveBufferCount comment above.
+    if (liveBufferCount > StartTriggeringAtLiveBufferCount) {
+        allocatedSinceLastTrigger++;
+        if (allocatedSinceLastTrigger > AllocatedBuffersPerTrigger) {
+            Unused << cx->runtime()->gc.triggerGC(JS::gcreason::TOO_MUCH_WASM_MEMORY);
+            allocatedSinceLastTrigger = 0;
+        }
+    } else {
+        allocatedSinceLastTrigger = 0;
+    }
+
     return true;
 }
 
 bool
 js::CreateWasmBuffer(JSContext* cx, const wasm::Limits& memory,
                      MutableHandleArrayBufferObjectMaybeShared buffer)
 {
     MOZ_ASSERT(memory.initial % wasm::PageSize == 0);
@@ -1193,18 +1213,16 @@ ArrayBufferObject::create(JSContext* cx,
                            "RefcountInfo must fit in inline slots");
                 nslots += refcountInfoSlots;
             } else {
                 // The ABO is taking ownership, so account the bytes against
                 // the zone.
                 size_t nAllocated = nbytes;
                 if (contents.kind() == MAPPED)
                     nAllocated = JS_ROUNDUP(nbytes, js::gc::SystemPageSize());
-                else if (contents.kind() == WASM)
-                    nAllocated = contents.wasmBuffer()->allocatedBytes();
                 cx->updateMallocCounter(nAllocated);
             }
         }
     } else {
         MOZ_ASSERT(ownsState == OwnsData);
         size_t usableSlots = NativeObject::MAX_FIXED_SLOTS - reservedSlots;
         if (nbytes <= usableSlots * sizeof(Value)) {
             int newSlots = JS_HOWMANY(nbytes, sizeof(Value));
@@ -1282,17 +1300,17 @@ ArrayBufferObject::createFromNewRawBuffe
 
     obj->setByteLength(initialSize);
     obj->setFlags(0);
     obj->setFirstView(nullptr);
 
     auto contents = BufferContents::create<WASM>(buffer->dataPointer());
     obj->setDataPointer(contents, OwnsData);
 
-    cx->updateMallocCounter(buffer->mappedSize());
+    cx->updateMallocCounter(initialSize);
 
     return obj;
 }
 
 /* static */ ArrayBufferObject::BufferContents
 ArrayBufferObject::externalizeContents(JSContext* cx, Handle<ArrayBufferObject*> buffer,
                                        bool hasStealableContents)
 {