michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsMemoryImpl.h" michael@0: #include "nsThreadUtils.h" michael@0: michael@0: #include "nsIObserver.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsISimpleEnumerator.h" michael@0: michael@0: #include "nsCOMPtr.h" michael@0: #include "mozilla/Services.h" michael@0: michael@0: #ifdef ANDROID michael@0: #include michael@0: michael@0: // Minimum memory threshold for a device to be considered michael@0: // a low memory platform. This value has be in sync with michael@0: // Java's equivalent threshold, defined in michael@0: // mobile/android/base/util/HardwareUtils.java michael@0: #define LOW_MEMORY_THRESHOLD_KB (384 * 1024) michael@0: #endif michael@0: michael@0: static nsMemoryImpl sGlobalMemory; michael@0: michael@0: NS_IMPL_QUERY_INTERFACE(nsMemoryImpl, nsIMemory) michael@0: michael@0: NS_IMETHODIMP_(void*) michael@0: nsMemoryImpl::Alloc(size_t size) michael@0: { michael@0: return NS_Alloc(size); michael@0: } michael@0: michael@0: NS_IMETHODIMP_(void*) michael@0: nsMemoryImpl::Realloc(void* ptr, size_t size) michael@0: { michael@0: return NS_Realloc(ptr, size); michael@0: } michael@0: michael@0: NS_IMETHODIMP_(void) michael@0: nsMemoryImpl::Free(void* ptr) michael@0: { michael@0: NS_Free(ptr); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMemoryImpl::HeapMinimize(bool aImmediate) michael@0: { michael@0: return FlushMemory(MOZ_UTF16("heap-minimize"), aImmediate); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMemoryImpl::IsLowMemory(bool *result) michael@0: { michael@0: NS_ERROR("IsLowMemory is deprecated. See bug 592308."); michael@0: *result = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMemoryImpl::IsLowMemoryPlatform(bool *result) michael@0: { michael@0: #ifdef ANDROID michael@0: static int sLowMemory = -1; // initialize to unknown, lazily evaluate to 0 or 1 michael@0: if (sLowMemory == -1) { michael@0: sLowMemory = 0; // assume "not low memory" in case file operations fail michael@0: *result = false; michael@0: michael@0: // check if MemTotal from /proc/meminfo is less than LOW_MEMORY_THRESHOLD_KB michael@0: FILE* fd = fopen("/proc/meminfo", "r"); michael@0: if (!fd) { michael@0: return NS_OK; michael@0: } michael@0: uint64_t mem = 0; michael@0: int rv = fscanf(fd, "MemTotal: %llu kB", &mem); michael@0: if (fclose(fd)) { michael@0: return NS_OK; michael@0: } michael@0: if (rv != 1) { michael@0: return NS_OK; michael@0: } michael@0: sLowMemory = (mem < LOW_MEMORY_THRESHOLD_KB) ? 1 : 0; michael@0: } michael@0: *result = (sLowMemory == 1); michael@0: #else michael@0: *result = false; michael@0: #endif michael@0: return NS_OK; michael@0: } michael@0: michael@0: /*static*/ nsresult michael@0: nsMemoryImpl::Create(nsISupports* outer, const nsIID& aIID, void **aResult) michael@0: { michael@0: if (NS_WARN_IF(outer)) michael@0: return NS_ERROR_NO_AGGREGATION; michael@0: return sGlobalMemory.QueryInterface(aIID, aResult); michael@0: } michael@0: michael@0: nsresult michael@0: nsMemoryImpl::FlushMemory(const char16_t* aReason, bool aImmediate) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: michael@0: if (aImmediate) { michael@0: // They've asked us to run the flusher *immediately*. We've michael@0: // got to be on the UI main thread for us to be able to do michael@0: // that...are we? michael@0: if (!NS_IsMainThread()) { michael@0: NS_ERROR("can't synchronously flush memory: not on UI thread"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } michael@0: michael@0: bool lastVal = sIsFlushing.exchange(true); michael@0: if (lastVal) michael@0: return NS_OK; michael@0: michael@0: PRIntervalTime now = PR_IntervalNow(); michael@0: michael@0: // Run the flushers immediately if we can; otherwise, proxy to the michael@0: // UI thread an run 'em asynchronously. michael@0: if (aImmediate) { michael@0: rv = RunFlushers(aReason); michael@0: } michael@0: else { michael@0: // Don't broadcast more than once every 1000ms to avoid being noisy michael@0: if (PR_IntervalToMicroseconds(now - sLastFlushTime) > 1000) { michael@0: sFlushEvent.mReason = aReason; michael@0: rv = NS_DispatchToMainThread(&sFlushEvent, NS_DISPATCH_NORMAL); michael@0: } michael@0: } michael@0: michael@0: sLastFlushTime = now; michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsMemoryImpl::RunFlushers(const char16_t* aReason) michael@0: { michael@0: nsCOMPtr os = mozilla::services::GetObserverService(); michael@0: if (os) { michael@0: michael@0: // Instead of: michael@0: // os->NotifyObservers(this, "memory-pressure", aReason); michael@0: // we are going to do this manually to see who/what is michael@0: // deallocating. michael@0: michael@0: nsCOMPtr e; michael@0: os->EnumerateObservers("memory-pressure", getter_AddRefs(e)); michael@0: michael@0: if ( e ) { michael@0: nsCOMPtr observer; michael@0: bool loop = true; michael@0: michael@0: while (NS_SUCCEEDED(e->HasMoreElements(&loop)) && loop) michael@0: { michael@0: nsCOMPtr supports; michael@0: e->GetNext(getter_AddRefs(supports)); michael@0: michael@0: if (!supports) michael@0: continue; michael@0: michael@0: observer = do_QueryInterface(supports); michael@0: observer->Observe(observer, "memory-pressure", aReason); michael@0: } michael@0: } michael@0: } michael@0: michael@0: sIsFlushing = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // XXX need NS_IMPL_STATIC_ADDREF/RELEASE michael@0: NS_IMETHODIMP_(MozExternalRefCountType) nsMemoryImpl::FlushEvent::AddRef() { return 2; } michael@0: NS_IMETHODIMP_(MozExternalRefCountType) nsMemoryImpl::FlushEvent::Release() { return 1; } michael@0: NS_IMPL_QUERY_INTERFACE(nsMemoryImpl::FlushEvent, nsIRunnable) michael@0: michael@0: NS_IMETHODIMP michael@0: nsMemoryImpl::FlushEvent::Run() michael@0: { michael@0: sGlobalMemory.RunFlushers(mReason); michael@0: return NS_OK; michael@0: } michael@0: michael@0: mozilla::Atomic michael@0: nsMemoryImpl::sIsFlushing; michael@0: michael@0: PRIntervalTime michael@0: nsMemoryImpl::sLastFlushTime = 0; michael@0: michael@0: nsMemoryImpl::FlushEvent michael@0: nsMemoryImpl::sFlushEvent; michael@0: michael@0: XPCOM_API(void*) michael@0: NS_Alloc(size_t size) michael@0: { michael@0: return moz_xmalloc(size); michael@0: } michael@0: michael@0: XPCOM_API(void*) michael@0: NS_Realloc(void* ptr, size_t size) michael@0: { michael@0: return moz_xrealloc(ptr, size); michael@0: } michael@0: michael@0: XPCOM_API(void) michael@0: NS_Free(void* ptr) michael@0: { michael@0: moz_free(ptr); michael@0: } michael@0: michael@0: nsresult michael@0: NS_GetMemoryManager(nsIMemory* *result) michael@0: { michael@0: return sGlobalMemory.QueryInterface(NS_GET_IID(nsIMemory), (void**) result); michael@0: }