From 03ec40bf4bfe3f2c71ec0a12a07e488da31052bb Mon Sep 17 00:00:00 2001 From: "warren%netscape.com" Date: Sat, 3 Jun 2000 07:02:20 +0000 Subject: [PATCH] Adding nsIMemory interface (replacement for nsIAllocator). Part of xpcom API freeze. --- xpcom/base/MANIFEST | 1 + xpcom/base/MANIFEST_IDL | 1 + xpcom/base/Makefile.in | 3 + xpcom/base/makefile.win | 3 + xpcom/base/nsIMemory.idl | 122 +++++++++++++++ xpcom/base/nsMemory.h | 58 +++++++ xpcom/base/nsMemoryImpl.cpp | 294 ++++++++++++++++++++++++++++++++++++ xpcom/base/nsMemoryImpl.h | 52 +++++++ 8 files changed, 534 insertions(+) create mode 100644 xpcom/base/nsIMemory.idl create mode 100644 xpcom/base/nsMemory.h create mode 100644 xpcom/base/nsMemoryImpl.cpp create mode 100644 xpcom/base/nsMemoryImpl.h diff --git a/xpcom/base/MANIFEST b/xpcom/base/MANIFEST index c28d00a6a712..4dc5f4be993f 100644 --- a/xpcom/base/MANIFEST +++ b/xpcom/base/MANIFEST @@ -1,5 +1,6 @@ nsAgg.h nsIAllocator.h +nsMemory.h nsCOMPtr.h nsCom.h nsCWeakReference.h diff --git a/xpcom/base/MANIFEST_IDL b/xpcom/base/MANIFEST_IDL index 3b27aef9bf9b..d8c9eb03a429 100644 --- a/xpcom/base/MANIFEST_IDL +++ b/xpcom/base/MANIFEST_IDL @@ -1,4 +1,5 @@ nsISupports.idl +nsIMemory.idl nsrootidl.idl nsIInterfaceRequestor.idl nsIConsoleService.idl diff --git a/xpcom/base/Makefile.in b/xpcom/base/Makefile.in index 381de84354ac..4691472c9e98 100644 --- a/xpcom/base/Makefile.in +++ b/xpcom/base/Makefile.in @@ -34,6 +34,7 @@ REQUIRES = xpcom CPPSRCS = \ nsAllocator.cpp \ + nsMemory.cpp \ nsDebug.cpp \ nsIInterfaceRequestor.cpp \ nsTraceRefcnt.cpp \ @@ -53,6 +54,7 @@ endif EXPORTS = \ nsAgg.h \ nsIAllocator.h \ + nsMemory.h \ nsCOMPtr.h \ nsCom.h \ nsCWeakReference.h \ @@ -80,6 +82,7 @@ endif XPIDLSRCS = \ nsrootidl.idl \ nsISupports.idl \ + nsIMemory.idl \ nsIInterfaceRequestor.idl \ nsIWeakReference.idl \ nsIConsoleService.idl \ diff --git a/xpcom/base/makefile.win b/xpcom/base/makefile.win index a5679e98bf76..9258fc5df14f 100644 --- a/xpcom/base/makefile.win +++ b/xpcom/base/makefile.win @@ -30,6 +30,7 @@ MODULE = xpcom EXPORTS = \ nsAgg.h \ nsIAllocator.h \ + nsMemory.h \ nsCOMPtr.h \ nsCom.h \ nsCWeakReference.h \ @@ -50,6 +51,7 @@ XPIDL_MODULE = xpcom_base XPIDLSRCS = \ .\nsrootidl.idl \ + .\nsIMemory.idl \ .\nsILeakDetector.idl \ .\nsIInterfaceRequestor.idl \ .\nsISupports.idl \ @@ -84,6 +86,7 @@ LCFLAGS = $(LCFLAGS) -DGC_LEAK_DETECTOR CPP_OBJS = \ .\$(OBJDIR)\nsDebug.obj \ .\$(OBJDIR)\nsAllocator.obj \ + .\$(OBJDIR)\nsMemoryImpl.obj \ .\$(OBJDIR)\nsCOMPtr.obj \ .\$(OBJDIR)\nsCWeakReference.obj \ .\$(OBJDIR)\nsID.obj \ diff --git a/xpcom/base/nsIMemory.idl b/xpcom/base/nsIMemory.idl new file mode 100644 index 000000000000..83da99183e05 --- /dev/null +++ b/xpcom/base/nsIMemory.idl @@ -0,0 +1,122 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + */ + +/** + * W A R N I N G + *

This API is currently under review in preparation for making it an + * officially supported part of the mozilla platform. Please submit comments + * to porkjockeys@mozilla.org before 6/15/00. Until that time, it is not + * advised to base commercial products on this API because it is subject to + * change. Use at your own risk! + * + * + * @status UNDER_REVIEW + */ + +#include "nsISupports.idl" + +interface nsIMemoryPressureObserver; + +/** + * nsIMemory: interface to allocate and deallocate memory. Also provides + * for notifications in low-memory situations. + */ +[scriptable, uuid(59e7e77a-38e4-11d4-8cf5-0060b0fc14a3)] +interface nsIMemory : nsISupports +{ + /** + * Allocates a block of memory of a particular size. If the memory + * cannot be allocated (because of an out-of-memory condition), null + * is returned. + * + * @param size - the size of the block to allocate + * @result the block of memory + */ + [noscript, notxpcom] voidPtr alloc(in size_t size); + + /** + * Reallocates a block of memory to a new size. + * + * @param ptr - the block of memory to reallocate + * @param size - the new size + * @result the reallocated block of memory + * + * If ptr is null, this function behaves like malloc. + * If s is the size of the block to which ptr points, the first + * min(s, size) bytes of ptr's block are copied to the new block. + * If the allocation succeeds, ptr is freed and a pointer to the + * new block returned. If the allocation fails, ptr is not freed + * and null is returned. The returned value may be the same as ptr. + */ + [noscript, notxpcom] voidPtr realloc(in voidPtr ptr, + in size_t newSize); + + /** + * Frees a block of memory. Null is a permissible value, in which case + * nothing happens. + * + * @param ptr - the block of memory to free + */ + [noscript, notxpcom] void free(in voidPtr ptr); + + /** + * Attempts to shrink the heap. + */ + void heapMinimize(); + + /** + * Allows a memory pressure observer to be registered to provide a + * callback for low-memory situations, allowing unnecessary objects to + * be freed (e.g. flushing caches). Observers will be called either + * before alloc or realloc are about to return null, or when + * heapMinimize is called. + * @param obs - the observer to register + */ + void registerObserver(in nsIMemoryPressureObserver obs); + + /** + * Allows a memory pressure observer to be unregistered. + * @param obs - the observer to unregister + */ + void unregisterObserver(in nsIMemoryPressureObserver obs); +}; + +/** + * nsIMemoryPressureObserver: interface for low-memory notification. + */ +[scriptable, uuid(fe6c8bd4-38e4-11d4-8cf5-0060b0fc14a3)] +interface nsIMemoryPressureObserver : nsISupports +{ + const unsigned long REASON_ALLOC_FAILURE = 0; + const unsigned long REASON_HEAP_MINIMIZE = 1; + + /** + * Called in response to a low-memory condition. + * @param reason - either REASON_ALLOC_FAILURE when alloc or realloc + * fails, or REASON_HEAP_MINIMIZE when heapMinimize is + * called + * @param requestedAmount - either the size requested by alloc or realloc, + * or 0 if heapMinimize was called + */ + void flushMemory(in unsigned long reason, + in size_t requestedAmount); +}; diff --git a/xpcom/base/nsMemory.h b/xpcom/base/nsMemory.h new file mode 100644 index 000000000000..f8880ffd6b49 --- /dev/null +++ b/xpcom/base/nsMemory.h @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + */ + +#ifndef nsMemory_h__ +#define nsMemory_h__ + +#include "nsIMemory.h" + +/** + * Static helper routines to manage memory. These routines allow easy access + * to xpcom's built-in (global) nsIMemory implementation, without needing + * to go through the service manager to get it. However this requires clients + * to link with the xpcom DLL. + */ +class nsMemory +{ +public: + static NS_EXPORT void* Alloc(size_t size); + static NS_EXPORT void* Realloc(void* ptr, size_t size); + static NS_EXPORT void Free(void* ptr); + static NS_EXPORT nsresult HeapMinimize(); + static NS_EXPORT nsresult RegisterObserver(nsIMemoryPressureObserver* obs); + static NS_EXPORT nsresult UnregisterObserver(nsIMemoryPressureObserver* obs); + static NS_EXPORT void* Clone(const void* ptr, size_t size); + static NS_EXPORT nsIMemory* GetGlobalMemoryService(); // AddRefs +}; + +// ProgID/CID for the global memory service: +#define NS_MEMORY_PROGID "component://mozilla/xpcom/memory-service" +#define NS_MEMORY_CLASSNAME "Global Memory Service" +#define NS_MEMORY_CID \ +{ /* 30a04e40-38e7-11d4-8cf5-0060b0fc14a3 */ \ + 0x30a04e40, \ + 0x38e7, \ + 0x11d4, \ + {0x8c, 0xf5, 0x00, 0x60, 0xb0, 0xfc, 0x14, 0xa3} \ +} + +#endif // nsMemory_h__ diff --git a/xpcom/base/nsMemoryImpl.cpp b/xpcom/base/nsMemoryImpl.cpp new file mode 100644 index 000000000000..5e782da64b1d --- /dev/null +++ b/xpcom/base/nsMemoryImpl.cpp @@ -0,0 +1,294 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + */ + +#include "nsMemoryImpl.h" +#include "prmem.h" +#include "nsIServiceManager.h" + +NS_IMPL_THREADSAFE_ISUPPORTS1(nsMemoryImpl, nsIMemory) + +NS_METHOD +nsMemoryImpl::Create(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr) +{ + NS_ENSURE_ARG_POINTER(aInstancePtr); + NS_ENSURE_PROPER_AGGREGATION(outer, aIID); + + nsMemoryImpl* mm = new nsMemoryImpl(); + if (mm == NULL) + return NS_ERROR_OUT_OF_MEMORY; + + nsresult rv = mm->QueryInterface(aIID, aInstancePtr); + + if (NS_FAILED(rv)) + delete mm; + return rv; +} + +//////////////////////////////////////////////////////////////////////////////// +// Define NS_OUT_OF_MEMORY_TESTER if you want to force memory failures + +#ifdef DEBUG_warren +#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) +#define MALLOC2(s) mallocator(s, gFailFreq, NS_FAIL_FREQUENCY) +#define REALLOC2(p, s) reallocator(p, s, gFailFreq, NS_FAIL_FREQUENCY) + +#else + +#define MALLOC1(s) PR_Malloc(s) +#define REALLOC1(p, s) PR_Realloc(p, s) +#define MALLOC2(s) PR_Malloc(s) +#define REALLOC2(p, s) PR_Realloc(p, s) + +#endif // NS_OUT_OF_MEMORY_TESTER + +//////////////////////////////////////////////////////////////////////////////// + +NS_IMETHODIMP_(void *) +nsMemoryImpl::Alloc(PRSize size) +{ + nsresult rv; + void* result = MALLOC1(size); + if (result == nsnull) { + rv = FlushMemory(nsIMemoryPressureObserver::REASON_ALLOC_FAILURE, size); + if (NS_FAILED(rv)) { + NS_WARNING("FlushMemory failed"); + } + else { + result = MALLOC2(size); + } + } + return result; +} + +NS_IMETHODIMP_(void *) +nsMemoryImpl::Realloc(void * ptr, PRSize size) +{ + nsresult rv; + void* result = REALLOC1(ptr, size); + if (result == nsnull) { + rv = FlushMemory(nsIMemoryPressureObserver::REASON_ALLOC_FAILURE, size); + if (NS_FAILED(rv)) { + NS_WARNING("FlushMemory failed"); + } + else { + result = REALLOC2(ptr, size); + } + } + return result; +} + +NS_IMETHODIMP_(void) +nsMemoryImpl::Free(void * ptr) +{ + PR_Free(ptr); +} + +NS_IMETHODIMP +nsMemoryImpl::HeapMinimize(void) +{ + return FlushMemory(nsIMemoryPressureObserver::REASON_HEAP_MINIMIZE, 0); +} + +NS_IMETHODIMP +nsMemoryImpl::RegisterObserver(nsIMemoryPressureObserver* obs) +{ + nsresult rv; + if (mObservers.get() == nsnull) { + rv = NS_NewISupportsArray(getter_AddRefs(mObservers)); + if (NS_FAILED(rv)) return rv; + } + return mObservers->AppendElement(obs); +} + +NS_IMETHODIMP +nsMemoryImpl::UnregisterObserver(nsIMemoryPressureObserver* obs) +{ + NS_ASSERTION(mObservers, "never called RegisterObserver"); + return mObservers->RemoveElement(obs); +} + +nsresult +nsMemoryImpl::FlushMemory(PRUint32 reason, PRSize size) +{ + if (mObservers.get() == nsnull) + return NS_OK; + + nsresult rv; + PRUint32 count; + rv = mObservers->Count(&count); + if (NS_FAILED(rv)) return rv; + + for (PRUint32 i = 0; i < count; i++) { + nsCOMPtr obs; + rv = mObservers->GetElementAt(i, (nsISupports**)getter_AddRefs(obs)); + if (NS_FAILED(rv)) return rv; + + rv = obs->FlushMemory(reason, size); + NS_ASSERTION(NS_SUCCEEDED(rv), "call to nsIMemoryPressureObserver::FlushMemory failed"); + // keep going... + } + return NS_OK; +} + +nsIMemory* gMemory = nsnull; + +nsresult +nsMemoryImpl::Startup() +{ + return Create(nsnull, NS_GET_IID(nsIMemory), (void**)&gMemory); +} + +nsresult +nsMemoryImpl::Shutdown() +{ + NS_RELEASE(gMemory); + gMemory = nsnull; + return NS_OK; +} + +//////////////////////////////////////////////////////////////////////////////// +// nsMemory static helper routines + +static void +EnsureGlobalMemoryService() +{ + if (gMemory) return; + nsresult rv = nsMemoryImpl::Startup(); + NS_ASSERTION(NS_SUCCEEDED(rv), "nsMemoryImpl::Startup failed"); + NS_ASSERTION(gMemory, "improper xpcom initialization"); +} + +NS_EXPORT void* +nsMemory::Alloc(PRSize size) +{ + if (gMemory == nsnull) { + EnsureGlobalMemoryService(); + } + return gMemory->Alloc(size); +} + +NS_EXPORT void* +nsMemory::Realloc(void* ptr, PRSize size) +{ + if (gMemory == nsnull) { + EnsureGlobalMemoryService(); + } + return gMemory->Realloc(ptr, size); +} + +NS_EXPORT void +nsMemory::Free(void* ptr) +{ + if (gMemory == nsnull) { + EnsureGlobalMemoryService(); + } + gMemory->Free(ptr); +} + +NS_EXPORT nsresult +nsMemory::HeapMinimize(void) +{ + if (gMemory == nsnull) { + EnsureGlobalMemoryService(); + } + return gMemory->HeapMinimize(); +} + +NS_EXPORT nsresult +nsMemory::RegisterObserver(nsIMemoryPressureObserver* obs) +{ + if (gMemory == nsnull) { + EnsureGlobalMemoryService(); + } + return gMemory->RegisterObserver(obs); +} + +NS_EXPORT nsresult +nsMemory::UnregisterObserver(nsIMemoryPressureObserver* obs) +{ + if (gMemory == nsnull) { + EnsureGlobalMemoryService(); + } + return gMemory->UnregisterObserver(obs); +} + +NS_EXPORT void* +nsMemory::Clone(const void* ptr, PRSize size) +{ + if (gMemory == nsnull) { + EnsureGlobalMemoryService(); + } + void* newPtr = gMemory->Alloc(size); + if (newPtr) + memcpy(newPtr, ptr, size); + return newPtr; +} + +NS_EXPORT nsIMemory* +nsMemory::GetGlobalMemoryService() +{ + if (gMemory == nsnull) { + EnsureGlobalMemoryService(); + } + NS_ADDREF(gMemory); + return gMemory; +} + +//////////////////////////////////////////////////////////////////////////////// + diff --git a/xpcom/base/nsMemoryImpl.h b/xpcom/base/nsMemoryImpl.h new file mode 100644 index 000000000000..e1cb005c6fab --- /dev/null +++ b/xpcom/base/nsMemoryImpl.h @@ -0,0 +1,52 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + */ + +#ifndef nsMemoryImpl_h__ +#define nsMemoryImpl_h__ + +#include "nsMemory.h" +#include "nsISupportsArray.h" +#include "nsCOMPtr.h" + +class nsMemoryImpl : public nsIMemory +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIMEMORY + + nsMemoryImpl() { NS_INIT_REFCNT(); } + virtual ~nsMemoryImpl() {} + + nsresult FlushMemory(PRUint32 reason, size_t size); + + // called from xpcom initialization/finalization: + static nsresult Startup(); + static nsresult Shutdown(); + + static NS_METHOD + Create(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr); + +protected: + nsCOMPtr mObservers; +}; + +#endif // nsMemoryImpl_h__