Bug 1451002 - Send ongoing memory pressure notifications when a low-memory condition persists for a long time; r=njn
authorGabriele Svelto <gsvelto@mozilla.com>
Wed, 18 Apr 2018 17:07:39 +0200
changeset 414952 9dcde577687cd5519b2156728f033c322e142a51
parent 414951 0f113bac293a677b74c6fc2ce9ecc2a7af09e0e2
child 414953 dbc1cffb4a11cabf48862d5206f139dd190c94ce
child 414975 5a5ca0495cda64fde9c27ca31aced494a8739e30
push id33881
push usernerli@mozilla.com
push dateSat, 21 Apr 2018 21:44:58 +0000
treeherdermozilla-central@9dcde577687c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnjn
bugs1451002
milestone61.0a1
first release with
nightly linux32
9dcde577687c / 61.0a1 / 20180421220102 / files
nightly linux64
9dcde577687c / 61.0a1 / 20180421220102 / files
nightly mac
9dcde577687c / 61.0a1 / 20180421220102 / files
nightly win32
9dcde577687c / 61.0a1 / 20180421220102 / files
nightly win64
9dcde577687c / 61.0a1 / 20180421220102 / 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 1451002 - Send ongoing memory pressure notifications when a low-memory condition persists for a long time; r=njn
xpcom/base/AvailableMemoryTracker.cpp
--- a/xpcom/base/AvailableMemoryTracker.cpp
+++ b/xpcom/base/AvailableMemoryTracker.cpp
@@ -32,120 +32,135 @@
 #endif  // MOZ_MEMORY
 
 using namespace mozilla;
 
 namespace {
 
 #if defined(_M_IX86) && defined(XP_WIN)
 
-// Fire a low-memory notification if we have less than this many MiB of virtual
-// address space available.
-static const uint32_t kLowVirtualMemoryThresholdMiB = 256;
+// Fire a low-memory notification if we have less than this many bytes of
+// virtual address space available.
+static const size_t kLowVirtualMemoryThreshold = 256 * 1024 * 1024;
 
-// Fire a low-memory notification if we have less than this many MiB of commit
+// Fire a low-memory notification if we have less than this many bytes of commit
 // space (physical memory plus page file) left.
-static const uint32_t kLowCommitSpaceThresholdMiB = 256;
+static const size_t kLowCommitSpaceThreshold = 256 * 1024 * 1024;
 
-// Fire a low-memory notification if we have less than this many MiB of
+// Fire a low-memory notification if we have less than this many bytes of
 // physical memory available on the whole machine.
-static const uint32_t kLowPhysicalMemoryThresholdMiB = 0;
+static const size_t kLowPhysicalMemoryThreshold = 0;
 
 // Don't fire a low-memory notification because of low available physical
 // memory or low commit space more often than this interval.
 static const uint32_t kLowMemoryNotificationIntervalMS = 10000;
 
-Atomic<uint32_t> sNumLowVirtualMemEvents;
-Atomic<uint32_t> sNumLowCommitSpaceEvents;
-Atomic<uint32_t> sNumLowPhysicalMemEvents;
+Atomic<uint32_t, MemoryOrdering::Relaxed> sNumLowVirtualMemEvents;
+Atomic<uint32_t, MemoryOrdering::Relaxed> sNumLowCommitSpaceEvents;
+Atomic<uint32_t, MemoryOrdering::Relaxed> sNumLowPhysicalMemEvents;
 
 WindowsDllInterceptor sKernel32Intercept;
 WindowsDllInterceptor sGdi32Intercept;
 
 // Has Init() been called?
 bool sInitialized = false;
 
 // Has Activate() been called?  The hooks don't do anything until this happens.
 bool sHooksActive = false;
 
 // Alas, we'd like to use mozilla::TimeStamp, but we can't, because it acquires
 // a lock!
-volatile bool sHasScheduledOneLowMemoryNotification = false;
+volatile bool sUnderMemoryPressure = false;
 volatile PRIntervalTime sLastLowMemoryNotificationTime;
 
 // These are function pointers to the functions we wrap in Init().
 
 void* (WINAPI* sVirtualAllocOrig)(LPVOID aAddress, SIZE_T aSize,
                                   DWORD aAllocationType, DWORD aProtect);
 
 void* (WINAPI* sMapViewOfFileOrig)(HANDLE aFileMappingObject,
                                    DWORD aDesiredAccess, DWORD aFileOffsetHigh,
                                    DWORD aFileOffsetLow, SIZE_T aNumBytesToMap);
 
 HBITMAP(WINAPI* sCreateDIBSectionOrig)(HDC aDC, const BITMAPINFO* aBitmapInfo,
                                        UINT aUsage, VOID** aBits,
                                        HANDLE aSection, DWORD aOffset);
 
 /**
- * Fire a memory pressure event if it's been long enough since the last one we
+ * Fire a memory pressure event if we were not under memory pressure yet, or
+ * fire an ongoing one if it's been long enough since the last one we
  * fired.
  */
 bool
 MaybeScheduleMemoryPressureEvent()
 {
+  MemoryPressureState state = MemPressure_New;
+  PRIntervalTime now = PR_IntervalNow();
+
   // If this interval rolls over, we may fire an extra memory pressure
   // event, but that's not a big deal.
-  PRIntervalTime interval = PR_IntervalNow() - sLastLowMemoryNotificationTime;
-  if (sHasScheduledOneLowMemoryNotification &&
-      PR_IntervalToMilliseconds(interval) < kLowMemoryNotificationIntervalMS) {
+  PRIntervalTime interval = now - sLastLowMemoryNotificationTime;
+  if (sUnderMemoryPressure) {
+    if (PR_IntervalToMilliseconds(interval) <
+        kLowMemoryNotificationIntervalMS) {
+      return false;
+    }
 
-    return false;
+    state = MemPressure_Ongoing;
   }
 
   // There's a bit of a race condition here, since an interval may be a
   // 64-bit number, and 64-bit writes aren't atomic on x86-32.  But let's
   // not worry about it -- the races only happen when we're already
   // experiencing memory pressure and firing notifications, so the worst
   // thing that can happen is that we fire two notifications when we
   // should have fired only one.
-  sHasScheduledOneLowMemoryNotification = true;
-  sLastLowMemoryNotificationTime = PR_IntervalNow();
+  sUnderMemoryPressure = true;
+  sLastLowMemoryNotificationTime = now;
+
+  NS_DispatchEventualMemoryPressure(state);
+  return true;
+}
 
-  NS_DispatchEventualMemoryPressure(MemPressure_New);
-  return true;
+static bool
+CheckLowMemory(DWORDLONG available, size_t threshold,
+               Atomic<uint32_t, MemoryOrdering::Relaxed>& counter)
+{
+  if (available < threshold) {
+    if (MaybeScheduleMemoryPressureEvent()) {
+      counter++;
+    }
+
+    return true;
+  }
+
+  return false;
 }
 
 void
 CheckMemAvailable()
 {
   if (!sHooksActive) {
     return;
   }
 
   MEMORYSTATUSEX stat;
   stat.dwLength = sizeof(stat);
   bool success = GlobalMemoryStatusEx(&stat);
 
   if (success) {
-    // kLowVirtualMemoryThresholdMiB is in MiB, but ullAvailVirtual is in bytes.
-    if (stat.ullAvailVirtual < kLowVirtualMemoryThresholdMiB * 1024 * 1024) {
-      // If we're running low on virtual memory, unconditionally schedule the
-      // notification.  We'll probably crash if we run out of virtual memory,
-      // so don't worry about firing this notification too often.
-      ++sNumLowVirtualMemEvents;
-      NS_DispatchEventualMemoryPressure(MemPressure_New);
-    } else if (stat.ullAvailPageFile < kLowCommitSpaceThresholdMiB * 1024 * 1024) {
-      if (MaybeScheduleMemoryPressureEvent()) {
-        ++sNumLowCommitSpaceEvents;
-      }
-    } else if (stat.ullAvailPhys < kLowPhysicalMemoryThresholdMiB * 1024 * 1024) {
-      if (MaybeScheduleMemoryPressureEvent()) {
-        ++sNumLowPhysicalMemEvents;
-      }
-    }
+    bool lowMemory = CheckLowMemory(stat.ullAvailVirtual,
+                                    kLowVirtualMemoryThreshold,
+                                    sNumLowVirtualMemEvents);
+    lowMemory |= CheckLowMemory(stat.ullAvailPageFile, kLowCommitSpaceThreshold,
+                                sNumLowCommitSpaceEvents);
+    lowMemory |= CheckLowMemory(stat.ullAvailPhys, kLowPhysicalMemoryThreshold,
+                                sNumLowPhysicalMemEvents);
+
+    sUnderMemoryPressure = lowMemory;
   }
 }
 
 LPVOID WINAPI
 VirtualAllocHook(LPVOID aAddress, SIZE_T aSize,
                  DWORD aAllocationType,
                  DWORD aProtect)
 {
@@ -161,18 +176,19 @@ VirtualAllocHook(LPVOID aAddress, SIZE_T
   // afterwards how much free virtual address space we have.  If we're running
   // low, we schedule a low-memory notification to run as soon as possible.
 
   LPVOID result = sVirtualAllocOrig(aAddress, aSize, aAllocationType, aProtect);
 
   // Don't call CheckMemAvailable for MEM_RESERVE if we're not tracking low
   // virtual memory.  Similarly, don't call CheckMemAvailable for MEM_COMMIT if
   // we're not tracking low physical memory.
-  if ((kLowVirtualMemoryThresholdMiB != 0 && aAllocationType & MEM_RESERVE) ||
-      (kLowPhysicalMemoryThresholdMiB != 0 && aAllocationType & MEM_COMMIT)) {
+  if ((kLowVirtualMemoryThreshold != 0 && aAllocationType & MEM_RESERVE) ||
+      ((kLowCommitSpaceThreshold != 0 || kLowPhysicalMemoryThreshold != 0) &&
+       aAllocationType & MEM_COMMIT)) {
     CheckMemAvailable();
   }
 
   return result;
 }
 
 LPVOID WINAPI
 MapViewOfFileHook(HANDLE aFileMappingObject,