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 1491734 9dcde577687cd5519b2156728f033c322e142a51
parent 1491733 0f113bac293a677b74c6fc2ce9ecc2a7af09e0e2
child 1491735 a39def43cc5153c4debecffc305510d084029f73
child 1491772 dbc1cffb4a11cabf48862d5206f139dd190c94ce
child 1491796 dcab0ade21d77139ad465ebb1bbce70785165a4e
child 1491813 1c631198c804b7d219fc4f35b7cbc338d701ba0b
child 1491872 5a5ca0495cda64fde9c27ca31aced494a8739e30
child 1492707 7e0d0321e71eb0af9591ead76dc163996fbaf819
child 1496255 577a7c281e9977d1415cc181b26995d4b4137f0d
push id266529
push useropettay@mozilla.com
push dateSat, 21 Apr 2018 20:56:30 +0000
treeherdertry@514ff6e1dc4e [default view] [failures only]
reviewersnjn
bugs1451002
milestone61.0a1
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,