Backed out changeset 04a28fb0458b
authorDoug Turner <dougt@meer.net>
Wed, 03 Sep 2008 23:19:34 -0700
changeset 18767 1288ff8be58a39b0305606af968e6331f9f85327
parent 18758 04a28fb0458b4eb952492631625c5dffe5967b16
child 18768 a0d8c801c709edb7851acc07cf1a9d89ebbe1b6b
push id1710
push userdougt@mozilla.com
push dateThu, 04 Sep 2008 06:20:50 +0000
treeherdermozilla-central@a0d8c801c709 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone1.9.1b1pre
backs out04a28fb0458b4eb952492631625c5dffe5967b16
Backed out changeset 04a28fb0458b
xpcom/base/nsMemoryImpl.cpp
xpcom/base/nsMemoryImpl.h
xpcom/build/nsXPComInit.cpp
--- a/xpcom/base/nsMemoryImpl.cpp
+++ b/xpcom/base/nsMemoryImpl.cpp
@@ -35,38 +35,127 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsXPCOM.h"
 #include "nsMemoryImpl.h"
 #include "nsThreadUtils.h"
 
 #include "nsIObserverService.h"
-#include "nsIObserver.h"
 #include "nsIServiceManager.h"
 #include "nsISupportsArray.h"
 
 #include "prmem.h"
 #include "prcvar.h"
 #include "pratom.h"
 
 #include "nsAlgorithm.h"
 #include "nsAutoLock.h"
 #include "nsCOMPtr.h"
 #include "nsString.h"
 
 #if defined(XP_WIN)
 #include <windows.h>
+#define NS_MEMORY_FLUSHER
 #elif defined (NS_OSSO)
 #include <osso-mem.h>
-#include <fcntl.h>
-#include <unistd.h>
-const char* kHighMark = "/sys/kernel/high_watermark";
+#else
+// Need to implement the nsIMemory::IsLowMemory() predicate
+#undef NS_MEMORY_FLUSHER
+#endif
+
+#ifdef NS_MEMORY_FLUSHER
+#include "nsITimer.h"
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Define NS_OUT_OF_MEMORY_TESTER if you want to force memory failures
+
+#ifdef DEBUG_xwarren
+#define NS_OUT_OF_MEMORY_TESTER
 #endif
 
+#ifdef NS_OUT_OF_MEMORY_TESTER
+
+// flush memory one in this number of times:
+#define NS_FLUSH_FREQUENCY        100000
+
+// fail allocation one in this number of flushes:
+#define NS_FAIL_FREQUENCY         10
+
+PRUint32 gFlushFreq = 0;
+PRUint32 gFailFreq = 0;
+
+static void*
+mallocator(PRSize size, PRUint32& counter, PRUint32 max)
+{
+    if (counter++ >= max) {
+        counter = 0;
+        NS_ASSERTION(0, "about to fail allocation... watch out");
+        return nsnull;
+    }
+    return PR_Malloc(size);
+}
+
+static void*
+reallocator(void* ptr, PRSize size, PRUint32& counter, PRUint32 max)
+{
+    if (counter++ >= max) {
+        counter = 0;
+        NS_ASSERTION(0, "about to fail reallocation... watch out");
+        return nsnull;
+    }
+    return PR_Realloc(ptr, size);
+}
+
+#define MALLOC1(s)       mallocator(s, gFlushFreq, NS_FLUSH_FREQUENCY)
+#define REALLOC1(p, s)   reallocator(p, s, gFlushFreq, NS_FLUSH_FREQUENCY)
+
+#else
+
+#define MALLOC1(s)       PR_Malloc(s)
+#define REALLOC1(p, s)   PR_Realloc(p, s)
+
+#endif // NS_OUT_OF_MEMORY_TESTER
+
+#if defined(XDEBUG_waterson)
+#define NS_TEST_MEMORY_FLUSHER
+#endif
+
+#ifdef NS_MEMORY_FLUSHER
+/**
+ * A class that is used to periodically check the status of the system,
+ * determine if too much memory is in use, and if so, trigger a "memory flush".
+ */
+class MemoryFlusher : public nsITimerCallback
+{
+public:
+    // We don't use the generic macros because we are a special static object
+    NS_IMETHOD QueryInterface(REFNSIID aIID, void** aResult);
+    NS_IMETHOD_(nsrefcnt) AddRef(void) { return 2; }
+    NS_IMETHOD_(nsrefcnt) Release(void) { return 1; }
+
+    NS_DECL_NSITIMERCALLBACK
+
+    nsresult Init();
+    void StopAndJoin();
+
+private:
+    static PRIntervalTime sTimeout;
+    static PRLock*        sLock;
+    static PRCondVar*     sCVar;
+    
+    enum {
+        kTimeout = 60000 // milliseconds
+    };
+};
+
+static MemoryFlusher sGlobalMemoryFlusher;
+
+#endif // NS_MEMORY_FLUSHER
 
 static nsMemoryImpl sGlobalMemory;
 
 NS_IMPL_QUERY_INTERFACE1(nsMemoryImpl, nsIMemory)
 
 NS_IMETHODIMP_(void*)
 nsMemoryImpl::Alloc(PRSize size)
 {
@@ -98,32 +187,42 @@ nsMemoryImpl::IsLowMemory(PRBool *result
     MEMORYSTATUS stat;
     GlobalMemoryStatus(&stat);
     *result = ((float)stat.dwAvailPhys / stat.dwTotalPhys) < 0.1;
 #elif defined(XP_WIN)
     MEMORYSTATUS stat;
     GlobalMemoryStatus(&stat);
     *result = ((float)stat.dwAvailPageFile / stat.dwTotalPageFile) < 0.1;
 #elif defined(NS_OSSO)
-
-    int fd = open (kHighMark, O_RDONLY);
-    if (fd == -1) {
-        *result = PR_FALSE;
-        return NS_OK;
-    }
-    int c = 0;
-    read (fd, &c, 1);
-    close(fd);
-    *result = (c == '1');
+    osso_mem_usage_t usage;
+    osso_mem_get_usage(&usage);
+    
+    // According to the docs, low memory limit isn't set if it is
+    // zero, or if it is greater than 100%.
+    if (usage.low == 0 || usage.low > usage.total)
+      *result = PR_FALSE;
+    else
+      *result = (PRBool) usage.low <= usage.used;
 #else
     *result = PR_FALSE;
 #endif
     return NS_OK;
 }
 
+
+/*static*/ nsresult 
+nsMemoryImpl::InitFlusher()
+{
+#ifdef NS_MEMORY_FLUSHER
+    return sGlobalMemoryFlusher.Init();
+#else
+    return NS_OK;
+#endif
+}
+
 /*static*/ nsresult
 nsMemoryImpl::Create(nsISupports* outer, const nsIID& aIID, void **aResult)
 {
     NS_ENSURE_NO_AGGREGATION(outer);
     return sGlobalMemory.QueryInterface(aIID, aResult);
 }
 
 nsresult
@@ -153,52 +252,22 @@ nsMemoryImpl::FlushMemory(const PRUnicha
     else {
         sFlushEvent.mReason = aReason;
         rv = NS_DispatchToMainThread(&sFlushEvent, NS_DISPATCH_NORMAL);
     }
 
     return rv;
 }
 
-#ifdef GET_OOM_STATS
-static long getUsedMemory()
-{
-#ifdef NS_OSSO
-    osso_mem_usage_t usage;
-    osso_mem_get_usage(&usage);
-    return usage.used;
-#else
-    return 0;
-#endif
-}
-#endif
-
 nsresult
 nsMemoryImpl::RunFlushers(const PRUnichar* aReason)
 {
     nsCOMPtr<nsIObserverService> os = do_GetService("@mozilla.org/observer-service;1");
     if (os) {
-
-        nsCOMPtr<nsISimpleEnumerator> theEnum;
-        os->EnumerateObservers("memory-pressure", getter_AddRefs(theEnum));
-        
-        nsCOMPtr<nsIObserver> elem;
-        PRBool loop;
-        while (NS_SUCCEEDED(theEnum->HasMoreElements(&loop)) && loop) {
-            theEnum->GetNext(getter_AddRefs(elem));
-
-#ifdef GET_OOM_STATS
-            long before = getUsedMemory();
-#endif
-            elem->Observe(this, "memory-pressure", aReason);
-
-#ifdef GET_OOM_STATS
-            printf("Memory Purged: %ld\n", before - getUsedMemory());
-#endif
-        }
+        os->NotifyObservers(this, "memory-pressure", aReason);
     }
 
     sIsFlushing = 0;
     return NS_OK;
 }
 
 // XXX need NS_IMPL_STATIC_ADDREF/RELEASE
 NS_IMETHODIMP_(nsrefcnt) nsMemoryImpl::FlushEvent::AddRef() { return 2; }
@@ -216,38 +285,75 @@ PRInt32
 nsMemoryImpl::sIsFlushing = 0;
 
 nsMemoryImpl::FlushEvent
 nsMemoryImpl::sFlushEvent;
 
 XPCOM_API(void*)
 NS_Alloc(PRSize size)
 {
-    void* result = PR_Malloc(size);
+    void* result = MALLOC1(size);
     if (! result) {
         // Request an asynchronous flush
         sGlobalMemory.FlushMemory(NS_LITERAL_STRING("alloc-failure").get(), PR_FALSE);
     }
     return result;
 }
 
 XPCOM_API(void*)
 NS_Realloc(void* ptr, PRSize size)
 {
-    void* result = PR_Realloc(ptr, size);
+    void* result = REALLOC1(ptr, size);
     if (! result && size != 0) {
         // Request an asynchronous flush
         sGlobalMemory.FlushMemory(NS_LITERAL_STRING("alloc-failure").get(), PR_FALSE);
     }
     return result;
 }
 
 XPCOM_API(void)
 NS_Free(void* ptr)
 {
     PR_Free(ptr);
 }
 
+#ifdef NS_MEMORY_FLUSHER
+
+NS_IMPL_QUERY_INTERFACE1(MemoryFlusher, nsITimerCallback)
+
+NS_IMETHODIMP
+MemoryFlusher::Notify(nsITimer *timer)
+{
+    PRBool isLowMemory;
+    sGlobalMemory.IsLowMemory(&isLowMemory);
+
+#ifdef NS_TEST_MEMORY_FLUSHER
+    // Fire the flusher *every* time
+    isLowMemory = PR_TRUE;
+#endif
+
+    if (isLowMemory)
+        sGlobalMemory.FlushMemory(NS_LITERAL_STRING("low-memory").get(),
+                                  PR_FALSE);
+    return NS_OK;
+}
+
+nsresult
+MemoryFlusher::Init()
+{
+    nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
+    NS_ENSURE_STATE(timer);
+
+    // There is no need to keep a reference to the timer object because we
+    // don't need to kill the timer.  It will be killed automatically when
+    // XPCOM is shutdown.
+
+    return timer->InitWithCallback(this, kTimeout,
+                                   nsITimer::TYPE_REPEATING_SLACK);
+}
+
+#endif // NS_MEMORY_FLUSHER
+
 nsresult
 NS_GetMemoryManager(nsIMemory* *result)
 {
     return sGlobalMemory.QueryInterface(NS_GET_IID(nsIMemory), (void**) result);
 }
--- a/xpcom/base/nsMemoryImpl.h
+++ b/xpcom/base/nsMemoryImpl.h
@@ -50,16 +50,17 @@ class nsMemoryImpl : public nsIMemory
 public:
     // We don't use the generic macros because we are a special static object
     NS_IMETHOD QueryInterface(REFNSIID aIID, void** aResult);
     NS_IMETHOD_(nsrefcnt) AddRef(void) { return 1; }
     NS_IMETHOD_(nsrefcnt) Release(void) { return 1; }
 
     NS_DECL_NSIMEMORY
 
+    static NS_HIDDEN_(nsresult) InitFlusher();
     static NS_METHOD Create(nsISupports* outer,
                             const nsIID& aIID, void **aResult);
 
     NS_HIDDEN_(nsresult) FlushMemory(const PRUnichar* aReason, PRBool aImmediate);
     NS_HIDDEN_(nsresult) RunFlushers(const PRUnichar* aReason);
 
 protected:
     struct FlushEvent : public nsIRunnable {
--- a/xpcom/build/nsXPComInit.cpp
+++ b/xpcom/build/nsXPComInit.cpp
@@ -655,16 +655,19 @@ NS_InitXPCOM3(nsIServiceManager* *result
         nsComponentManagerImpl::gComponentManager->AutoRegister(nsnull);
     }
 
     // After autoreg, but before we actually instantiate any components,
     // add any services listed in the "xpcom-directory-providers" category
     // to the directory service.
     nsDirectoryService::gService->RegisterCategoryProviders();
 
+    // Initialize memory flusher
+    nsMemoryImpl::InitFlusher();
+
     // Notify observers of xpcom autoregistration start
     NS_CreateServicesFromCategory(NS_XPCOM_STARTUP_CATEGORY, 
                                   nsnull,
                                   NS_XPCOM_STARTUP_OBSERVER_ID);
     
     return NS_OK;
 }