From 85c206679a827451bcedea11521d05996125d47a Mon Sep 17 00:00:00 2001 From: "dbaron%fas.harvard.edu" Date: Sat, 20 Oct 2001 23:19:07 +0000 Subject: [PATCH] Reduce the performance cost of (threadsafely) refcounting many atoms by creating a second implementation of nsIAtom (derived from the first) that does not refcount and gets destroyed at XPCOM shutdown. Normal atoms are converted to permanent ones without loss of pointer identity when a permanent atom is requested and a normal one already exists. b=92141 r=waterson sr=brendan git-svn-id: svn://10.0.0.236/trunk@105889 18797224-902f-48f8-a5cc-f745e15eee43 --- .../content/html/content/src/nsHTMLAtoms.cpp | 2 +- mozilla/content/shared/src/nsCSSAtoms.cpp | 2 +- mozilla/content/shared/src/nsHTMLAtoms.cpp | 2 +- mozilla/content/shared/src/nsLayoutAtoms.cpp | 2 +- mozilla/content/shared/src/nsXULAtoms.cpp | 2 +- mozilla/layout/base/nsLayoutAtoms.cpp | 2 +- .../mathml/content/src/nsMathMLAtoms.cpp | 2 +- mozilla/layout/svg/content/src/nsSVGAtoms.cpp | 2 +- .../widget/src/xpwidgets/nsWidgetAtoms.cpp | 2 +- mozilla/xpcom/build/nsXPComInit.cpp | 1 + mozilla/xpcom/ds/nsAtomService.cpp | 13 +- mozilla/xpcom/ds/nsAtomTable.cpp | 283 +++++++++++++----- mozilla/xpcom/ds/nsAtomTable.h | 33 +- mozilla/xpcom/ds/nsIAtom.idl | 47 ++- mozilla/xpcom/ds/nsIAtomService.idl | 16 + mozilla/xpcom/tests/Makefile.in | 22 +- mozilla/xpcom/tests/TestPermanentAtoms.cpp | 88 ++++++ mozilla/xpcom/tests/makefile.win | 6 +- 18 files changed, 419 insertions(+), 108 deletions(-) create mode 100644 mozilla/xpcom/tests/TestPermanentAtoms.cpp diff --git a/mozilla/content/html/content/src/nsHTMLAtoms.cpp b/mozilla/content/html/content/src/nsHTMLAtoms.cpp index 224874fab32..0fd9bb7c0cd 100644 --- a/mozilla/content/html/content/src/nsHTMLAtoms.cpp +++ b/mozilla/content/html/content/src/nsHTMLAtoms.cpp @@ -49,7 +49,7 @@ void nsHTMLAtoms::AddRefAtoms() { if (0 == gRefCnt++) { // create atoms -#define HTML_ATOM(_name, _value) _name = NS_NewAtom(_value); +#define HTML_ATOM(_name, _value) _name = NS_NewPermanentAtom(_value); #include "nsHTMLAtomList.h" #undef HTML_ATOM } diff --git a/mozilla/content/shared/src/nsCSSAtoms.cpp b/mozilla/content/shared/src/nsCSSAtoms.cpp index 854982378c0..d9ef7923e15 100644 --- a/mozilla/content/shared/src/nsCSSAtoms.cpp +++ b/mozilla/content/shared/src/nsCSSAtoms.cpp @@ -49,7 +49,7 @@ void nsCSSAtoms::AddRefAtoms() { if (0 == gRefCnt++) { // create atoms -#define CSS_ATOM(_name, _value) _name = NS_NewAtom(_value); +#define CSS_ATOM(_name, _value) _name = NS_NewPermanentAtom(_value); #include "nsCSSAtomList.h" #undef CSS_ATOM } diff --git a/mozilla/content/shared/src/nsHTMLAtoms.cpp b/mozilla/content/shared/src/nsHTMLAtoms.cpp index 224874fab32..0fd9bb7c0cd 100644 --- a/mozilla/content/shared/src/nsHTMLAtoms.cpp +++ b/mozilla/content/shared/src/nsHTMLAtoms.cpp @@ -49,7 +49,7 @@ void nsHTMLAtoms::AddRefAtoms() { if (0 == gRefCnt++) { // create atoms -#define HTML_ATOM(_name, _value) _name = NS_NewAtom(_value); +#define HTML_ATOM(_name, _value) _name = NS_NewPermanentAtom(_value); #include "nsHTMLAtomList.h" #undef HTML_ATOM } diff --git a/mozilla/content/shared/src/nsLayoutAtoms.cpp b/mozilla/content/shared/src/nsLayoutAtoms.cpp index 2f65a33fdcc..0f130c46da7 100644 --- a/mozilla/content/shared/src/nsLayoutAtoms.cpp +++ b/mozilla/content/shared/src/nsLayoutAtoms.cpp @@ -49,7 +49,7 @@ void nsLayoutAtoms::AddRefAtoms() { if (0 == gRefCnt++) { // create atoms -#define LAYOUT_ATOM(_name, _value) _name = NS_NewAtom(_value); +#define LAYOUT_ATOM(_name, _value) _name = NS_NewPermanentAtom(_value); #include "nsLayoutAtomList.h" #undef LAYOUT_ATOM } diff --git a/mozilla/content/shared/src/nsXULAtoms.cpp b/mozilla/content/shared/src/nsXULAtoms.cpp index c295bfb384a..9d8f5643125 100644 --- a/mozilla/content/shared/src/nsXULAtoms.cpp +++ b/mozilla/content/shared/src/nsXULAtoms.cpp @@ -71,7 +71,7 @@ void nsXULAtoms::AddRefAtoms() { } // now register the atoms -#define XUL_ATOM(_name, _value) _name = NS_NewAtom(_value); +#define XUL_ATOM(_name, _value) _name = NS_NewPermanentAtom(_value); #include "nsXULAtomList.h" #undef XUL_ATOM } diff --git a/mozilla/layout/base/nsLayoutAtoms.cpp b/mozilla/layout/base/nsLayoutAtoms.cpp index 2f65a33fdcc..0f130c46da7 100644 --- a/mozilla/layout/base/nsLayoutAtoms.cpp +++ b/mozilla/layout/base/nsLayoutAtoms.cpp @@ -49,7 +49,7 @@ void nsLayoutAtoms::AddRefAtoms() { if (0 == gRefCnt++) { // create atoms -#define LAYOUT_ATOM(_name, _value) _name = NS_NewAtom(_value); +#define LAYOUT_ATOM(_name, _value) _name = NS_NewPermanentAtom(_value); #include "nsLayoutAtomList.h" #undef LAYOUT_ATOM } diff --git a/mozilla/layout/mathml/content/src/nsMathMLAtoms.cpp b/mozilla/layout/mathml/content/src/nsMathMLAtoms.cpp index e407f226465..0baaa3c205f 100644 --- a/mozilla/layout/mathml/content/src/nsMathMLAtoms.cpp +++ b/mozilla/layout/mathml/content/src/nsMathMLAtoms.cpp @@ -59,7 +59,7 @@ void nsMathMLAtoms::AddRefAtoms() { } // now register the atoms -#define MATHML_ATOM(_name, _value) _name = NS_NewAtom(_value); +#define MATHML_ATOM(_name, _value) _name = NS_NewPermanentAtom(_value); #include "nsMathMLAtomList.h" #undef MATHML_ATOM } diff --git a/mozilla/layout/svg/content/src/nsSVGAtoms.cpp b/mozilla/layout/svg/content/src/nsSVGAtoms.cpp index 3bc9fc1ce53..39d4aaafa5d 100644 --- a/mozilla/layout/svg/content/src/nsSVGAtoms.cpp +++ b/mozilla/layout/svg/content/src/nsSVGAtoms.cpp @@ -76,7 +76,7 @@ void nsSVGAtoms::AddRefAtoms() { } // now register the atoms -#define SVG_ATOM(_name, _value) _name = NS_NewAtom(_value); +#define SVG_ATOM(_name, _value) _name = NS_NewPermanentAtom(_value); #include "nsSVGAtomList.h" #undef SVG_ATOM } diff --git a/mozilla/widget/src/xpwidgets/nsWidgetAtoms.cpp b/mozilla/widget/src/xpwidgets/nsWidgetAtoms.cpp index af5bf7619ad..e38ce598055 100644 --- a/mozilla/widget/src/xpwidgets/nsWidgetAtoms.cpp +++ b/mozilla/widget/src/xpwidgets/nsWidgetAtoms.cpp @@ -50,7 +50,7 @@ void nsWidgetAtoms::AddRefAtoms() { if (gRefCnt == 0) { // now register the atoms -#define WIDGET_ATOM(_name, _value) _name = NS_NewAtom(_value); +#define WIDGET_ATOM(_name, _value) _name = NS_NewPermanentAtom(_value); #include "nsWidgetAtomList.h" #undef WIDGET_ATOM } diff --git a/mozilla/xpcom/build/nsXPComInit.cpp b/mozilla/xpcom/build/nsXPComInit.cpp index 5d66d3600b8..09d8b7c4f04 100644 --- a/mozilla/xpcom/build/nsXPComInit.cpp +++ b/mozilla/xpcom/build/nsXPComInit.cpp @@ -89,6 +89,7 @@ #include "nsFastLoadService.h" #include "nsAtomService.h" +#include "nsAtomTable.h" #include "nsTraceRefcnt.h" #include "nsTimelineService.h" diff --git a/mozilla/xpcom/ds/nsAtomService.cpp b/mozilla/xpcom/ds/nsAtomService.cpp index 393e53d72fb..cf79d965689 100644 --- a/mozilla/xpcom/ds/nsAtomService.cpp +++ b/mozilla/xpcom/ds/nsAtomService.cpp @@ -52,8 +52,6 @@ nsAtomService::~nsAtomService() nsresult nsAtomService::GetAtom(const PRUnichar *aString, nsIAtom ** aResult) { - NS_ENSURE_ARG_POINTER(aResult); - *aResult = NS_NewAtom(aString); if (!*aResult) @@ -61,3 +59,14 @@ nsAtomService::GetAtom(const PRUnichar *aString, nsIAtom ** aResult) return NS_OK; } + +nsresult +nsAtomService::GetPermanentAtom(const PRUnichar *aString, nsIAtom ** aResult) +{ + *aResult = NS_NewPermanentAtom(aString); + + if (!*aResult) + return NS_ERROR_OUT_OF_MEMORY; + + return NS_OK; +} diff --git a/mozilla/xpcom/ds/nsAtomTable.cpp b/mozilla/xpcom/ds/nsAtomTable.cpp index 4f7d2246ff0..7dc646e0c01 100644 --- a/mozilla/xpcom/ds/nsAtomTable.cpp +++ b/mozilla/xpcom/ds/nsAtomTable.cpp @@ -1,4 +1,5 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +// vim:cindent:ts=2:et:sw=2: /* ***** BEGIN LICENSE BLOCK ***** * Version: NPL 1.1/GPL 2.0/LGPL 2.1 * @@ -38,76 +39,161 @@ #include "nsAtomTable.h" #include "nsString.h" #include "nsCRT.h" -#include "plhash.h" +#include "pldhash.h" +#include "prenv.h" #include "nsISizeOfHandler.h" -#include "nslog.h" - -NS_IMPL_LOG(nsAtomTableLog) -#define PRINTF NS_LOG_PRINTF(nsAtomTableLog) -#define FLUSH NS_LOG_FLUSH(nsAtomTableLog) /** * The shared hash table for atom lookups. + * + * XXX This should be manipulated in a threadsafe way or we should make + * sure it's only manipulated from the main thread. Probably the latter + * is better, since the former would hurt performance. + * + * If |gAtomTable.entryCount| is 0, then the table is uninitialized. */ -static nsrefcnt gAtoms; -static struct PLHashTable* gAtomHashTable; +static PLDHashTable gAtomTable; -#if defined(DEBUG) && (defined(XP_UNIX) || defined(XP_PC)) -static PRIntn PR_CALLBACK -DumpAtomLeaks(PLHashEntry *he, PRIntn index, void *arg) +struct AtomTableEntry : public PLDHashEntryHdr { + AtomImpl *mAtom; +}; + +PR_STATIC_CALLBACK(const void *) +AtomTableGetKey(PLDHashTable *table, PLDHashEntryHdr *entry) { - AtomImpl* atom = (AtomImpl*) he->value; - if (atom) { - nsAutoString tmp; - atom->ToString(tmp); - fputs(NS_LossyConvertUCS2toASCII(tmp).get(), stdout); + AtomTableEntry *he = NS_STATIC_CAST(AtomTableEntry*, entry); + return he->mAtom->mString; +} + +PR_STATIC_CALLBACK(PLDHashNumber) +AtomTableHashKey(PLDHashTable *table, const void *key) +{ + return nsCRT::HashCode(NS_STATIC_CAST(const PRUnichar*,key)); +} + +PR_STATIC_CALLBACK(PRBool) +AtomTableMatchKey(PLDHashTable *table, + const PLDHashEntryHdr *entry, + const void *key) +{ + const AtomTableEntry *he = NS_STATIC_CAST(const AtomTableEntry*, entry); + const PRUnichar* keyStr = NS_STATIC_CAST(const PRUnichar*, key); + return nsCRT::strcmp(keyStr, he->mAtom->mString) == 0; +} + +PR_STATIC_CALLBACK(void) +AtomTableClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry) +{ + AtomTableEntry *he = NS_STATIC_CAST(AtomTableEntry*, entry); + AtomImpl *atom = he->mAtom; + he->mAtom = 0; + he->keyHash = 0; + + // Normal |AtomImpl| atoms are deleted when their refcount hits 0, and + // they then remove themselves from the table. In other words, they + // are owned by the callers who own references to them. + // |PermanentAtomImpl| permanent atoms ignore their refcount and are + // deleted when they are removed from the table at table destruction. + // In other words, they are owned by the atom table. + if (atom->IsPermanent()) + delete atom; +} + +static PLDHashTableOps AtomTableOps = { + PL_DHashAllocTable, + PL_DHashFreeTable, + AtomTableGetKey, + AtomTableHashKey, + AtomTableMatchKey, + PL_DHashMoveEntryStub, + AtomTableClearEntry, + PL_DHashFinalizeStub, + NULL +}; + + +#ifdef DEBUG + +PR_STATIC_CALLBACK(PLDHashOperator) +DumpAtomLeaks(PLDHashTable *table, PLDHashEntryHdr *he, + PRUint32 index, void *arg) +{ + AtomTableEntry *entry = NS_STATIC_CAST(AtomTableEntry*, he); + AtomImpl* atom = entry->mAtom; + if (!atom->IsPermanent()) { + ++*NS_STATIC_CAST(PRUint32*, arg); + const PRUnichar *str; + atom->GetUnicode(&str); + fputs(NS_LossyConvertUCS2toASCII(str).get(), stdout); fputs("\n", stdout); } - return HT_ENUMERATE_NEXT; + return PL_DHASH_NEXT; } + #endif -NS_COM void NS_PurgeAtomTable(void) +void NS_PurgeAtomTable() { - if (gAtomHashTable) { -#if defined(DEBUG) && (defined(XP_UNIX) || defined(XP_PC)) - if (gAtoms) { - if (NS_LOG_ENABLED(nsAtomTableLog)) { - PRINTF("*** leaking %d atoms\n", gAtoms); - PL_HashTableEnumerateEntries(gAtomHashTable, DumpAtomLeaks, 0); - } + if (gAtomTable.entryCount) { +#ifdef DEBUG + if (PR_GetEnv("MOZ_DUMP_ATOM_LEAKS")) { + PRUint32 leaked = 0; + printf("*** %d atoms still exist (including permanent):\n", + gAtomTable.entryCount); + PL_DHashTableEnumerate(&gAtomTable, DumpAtomLeaks, &leaked); + printf("*** %u non-permanent atoms leaked\n", leaked); } #endif - PL_HashTableDestroy(gAtomHashTable); - gAtomHashTable = nsnull; + PL_DHashTableFinish(&gAtomTable); + gAtomTable.entryCount = 0; } } AtomImpl::AtomImpl() { - NS_INIT_REFCNT(); - // Every live atom holds a reference on the atom hashtable - gAtoms++; + NS_INIT_ISUPPORTS(); } AtomImpl::~AtomImpl() { - NS_PRECONDITION(nsnull != gAtomHashTable, "null atom hashtable"); - if (nsnull != gAtomHashTable) { - - PL_HashTableRemove(gAtomHashTable, mString); - nsrefcnt cnt = --gAtoms; - if (0 == cnt) { - // When the last atom is destroyed, the atom arena is destroyed - NS_ASSERTION(0 == gAtomHashTable->nentries, "bad atom table"); - PL_HashTableDestroy(gAtomHashTable); - gAtomHashTable = nsnull; + NS_PRECONDITION(gAtomTable.entryCount, "uninitialized atom hashtable"); + // Permanent atoms are removed from the hashtable at shutdown, and we + // don't want to remove them twice. See comment above in + // |AtomTableClearEntry|. + if (!IsPermanent()) { + PL_DHashTableOperate(&gAtomTable, mString, PL_DHASH_REMOVE); + if (gAtomTable.entryCount == 0) { + PL_DHashTableFinish(&gAtomTable); + NS_ASSERTION(gAtomTable.entryCount == 0, + "PL_DHashTableFinish changed the entry count"); } } } NS_IMPL_THREADSAFE_ISUPPORTS1(AtomImpl, nsIAtom) +NS_IMETHODIMP_(nsrefcnt) PermanentAtomImpl::AddRef() +{ + return 2; +} + +NS_IMETHODIMP_(nsrefcnt) PermanentAtomImpl::Release() +{ + return 1; +} + +/* virtual */ PRBool +AtomImpl::IsPermanent() +{ + return PR_FALSE; +} + +/* virtual */ PRBool +PermanentAtomImpl::IsPermanent() +{ + return PR_TRUE; +} + void* AtomImpl::operator new ( size_t size, const nsAReadableString& aString ) { /* @@ -120,7 +206,7 @@ void* AtomImpl::operator new ( size_t size, const nsAReadableString& aString ) http://lxr.mozilla.org/seamonkey/source/xpcom/ds/nsSharedString.h#174 */ size += aString.Length() * sizeof(PRUnichar); - AtomImpl* ii = (AtomImpl*) ::operator new(size); + AtomImpl* ii = NS_STATIC_CAST(AtomImpl*, ::operator new(size)); PRUnichar* toBegin = &ii->mString[0]; nsReadingIterator fromBegin, fromEnd; @@ -128,6 +214,14 @@ void* AtomImpl::operator new ( size_t size, const nsAReadableString& aString ) return ii; } +void* PermanentAtomImpl::operator new ( size_t size, AtomImpl* aAtom ) { + NS_ASSERTION(!aAtom->IsPermanent(), + "converting atom that's already permanent"); + + // Just let the constructor overwrite the vtable pointer. + return aAtom; +} + NS_IMETHODIMP AtomImpl::ToString(nsAWritableString& aBuf) /*FIX: const */ { @@ -138,7 +232,7 @@ AtomImpl::ToString(nsAWritableString& aBuf) /*FIX: const */ NS_IMETHODIMP AtomImpl::GetUnicode(const PRUnichar **aResult) /*FIX: const */ { - NS_ENSURE_ARG_POINTER(aResult); + NS_PRECONDITION(aResult, "null out param"); *aResult = mString; return NS_OK; } @@ -147,61 +241,84 @@ NS_IMETHODIMP AtomImpl::SizeOf(nsISizeOfHandler* aHandler, PRUint32* _retval) /*FIX: const */ { #ifdef DEBUG - NS_ENSURE_ARG_POINTER(_retval); - PRUint32 sum = sizeof(*this) + nsCRT::strlen(mString) * sizeof(PRUnichar); - *_retval = sum; + NS_PRECONDITION(_retval, "null out param"); + *_retval = sizeof(*this) + nsCRT::strlen(mString) * sizeof(PRUnichar); #endif return NS_OK; } //---------------------------------------------------------------------- -static PLHashNumber HashKey(const PRUnichar* k) -{ - return nsCRT::HashCode(k); -} - -static PRIntn CompareKeys( const PRUnichar* k1, const PRUnichar* k2 ) -{ - return nsCRT::strcmp(k1, k2) == 0; -} - NS_COM nsIAtom* NS_NewAtom(const char* isolatin1) { return NS_NewAtom(NS_ConvertASCIItoUCS2(isolatin1)); } -NS_COM nsIAtom* NS_NewAtom( const nsAReadableString& aString ) +NS_COM nsIAtom* NS_NewPermanentAtom(const char* isolatin1) { - if ( !gAtomHashTable ) - gAtomHashTable = PL_NewHashTable(2048, (PLHashFunction)HashKey, - (PLHashComparator)CompareKeys, - (PLHashComparator)0, 0, 0); + return NS_NewPermanentAtom(NS_ConvertASCIItoUCS2(isolatin1)); +} - const nsPromiseFlatString& flat = PromiseFlatString(aString); - const PRUnichar *str = flat.get(); +static AtomTableEntry* GetAtomHashEntry(const nsAString& aString) +{ + if ( !gAtomTable.entryCount ) + PL_DHashTableInit(&gAtomTable, &AtomTableOps, 0, + sizeof(AtomTableEntry), 2048); - PRUint32 hashCode = HashKey(str); + return NS_STATIC_CAST(AtomTableEntry*, + PL_DHashTableOperate(&gAtomTable, + PromiseFlatString(aString).get(), + PL_DHASH_ADD)); +} - PLHashEntry** hep = PL_HashTableRawLookup(gAtomHashTable, hashCode, str); +NS_COM nsIAtom* NS_NewAtom( const nsAString& aString ) +{ + AtomTableEntry *he = GetAtomHashEntry(aString); + AtomImpl* atom = he->mAtom; - PLHashEntry* he = *hep; - - AtomImpl* id; - - if ( he ) { - // if we found one, great - id = NS_STATIC_CAST(AtomImpl*, he->value); - } else { - // otherwise, we'll make a new atom - id = new (aString) AtomImpl(); - if ( id ) { - PL_HashTableRawAdd(gAtomHashTable, hep, hashCode, id->mString, id); + if (!atom) { + atom = new (aString) AtomImpl(); + he->mAtom = atom; + if (!atom) { + PL_DHashTableRawRemove(&gAtomTable, he); + return nsnull; } } - NS_IF_ADDREF(id); - return id; + NS_ADDREF(atom); + return atom; +} + +NS_COM nsIAtom* NS_NewPermanentAtom( const nsAString& aString ) +{ + AtomTableEntry *he = GetAtomHashEntry(aString); + AtomImpl* atom = he->mAtom; + + if (atom) { + // ensure that it's permanent + if (!atom->IsPermanent()) { +#ifdef NS_BUILD_REFCNT_LOGGING + { + nsrefcnt refcount = atom->GetRefCount(); + do { + NS_LOG_RELEASE(atom, --refcount, "AtomImpl"); + } while (refcount); + } +#endif + atom = new (atom) PermanentAtomImpl(); + } + } else { + // otherwise, make a new atom + atom = new (aString) PermanentAtomImpl(); + he->mAtom = atom; + if ( !atom ) { + PL_DHashTableRawRemove(&gAtomTable, he); + return nsnull; + } + } + + NS_ADDREF(atom); + return atom; } NS_COM nsIAtom* NS_NewAtom( const PRUnichar* str ) @@ -209,10 +326,12 @@ NS_COM nsIAtom* NS_NewAtom( const PRUnichar* str ) return NS_NewAtom(nsDependentString(str)); } +NS_COM nsIAtom* NS_NewPermanentAtom( const PRUnichar* str ) +{ + return NS_NewPermanentAtom(nsDependentString(str)); +} + NS_COM nsrefcnt NS_GetNumberOfAtoms(void) { - if (nsnull != gAtomHashTable) { - NS_PRECONDITION(nsrefcnt(gAtomHashTable->nentries) == gAtoms, "bad atom table"); - } - return gAtoms; + return gAtomTable.entryCount; } diff --git a/mozilla/xpcom/ds/nsAtomTable.h b/mozilla/xpcom/ds/nsAtomTable.h index 9c17a37ab15..b1bfba068b8 100644 --- a/mozilla/xpcom/ds/nsAtomTable.h +++ b/mozilla/xpcom/ds/nsAtomTable.h @@ -40,6 +40,12 @@ #include "nsIAtom.h" +/** + * A threadsafely-refcounted implementation of nsIAtom. Note that + * AtomImpl objects are sometimes converted into PermanentAtomImpl + * objects using placement new and just overwriting the vtable pointer. + */ + class AtomImpl : public nsIAtom { public: AtomImpl(); @@ -48,15 +54,40 @@ public: NS_DECL_ISUPPORTS NS_DECL_NSIATOM - void* operator new(size_t size, const nsAReadableString& aString); + virtual PRBool IsPermanent(); + + void* operator new(size_t size, const nsAString& aString); void operator delete(void* ptr) { ::operator delete(ptr); } + // for |#ifdef NS_BUILD_REFCNT_LOGGING| access to reference count + nsrefcnt GetRefCount() { return mRefCnt; } + // Actually more; 0 terminated. This slot is reserved for the // terminating zero. PRUnichar mString[1]; }; +/** + * A non-refcounted implementation of nsIAtom. + */ + +class PermanentAtomImpl : public AtomImpl { +public: + NS_IMETHOD_(nsrefcnt) AddRef(); + NS_IMETHOD_(nsrefcnt) Release(); + + virtual PRBool IsPermanent(); + + void* operator new(size_t size, const nsAReadableString& aString) { + return AtomImpl::operator new(size, aString); + } + void* operator new(size_t size, AtomImpl* aAtom); + +}; + +void NS_PurgeAtomTable(); + #endif // nsAtomTable_h__ diff --git a/mozilla/xpcom/ds/nsIAtom.idl b/mozilla/xpcom/ds/nsIAtom.idl index 8605231f340..5b42bb389fc 100644 --- a/mozilla/xpcom/ds/nsIAtom.idl +++ b/mozilla/xpcom/ds/nsIAtom.idl @@ -27,9 +27,16 @@ interface nsISizeOfHandler; %{C++ #include "nsAWritableString.h" +#include "nsCOMPtr.h" %} [ref] native nsStringRef(nsAWritableString); +/* + * Should this really be scriptable? Using atoms from script or proxies + * could be dangerous since double-wrapping could lead to loss of + * pointer identity. + */ + [scriptable, uuid(3d1b15b0-93b4-11d1-895b-006008911b81)] interface nsIAtom : nsISupports { @@ -51,23 +58,57 @@ interface nsIAtom : nsISupports %{C++ +/* + * The three forms of NS_NewAtom and do_GetAtom (for use with + * |nsCOMPtr|) return the atom for the string given. At any + * given time there will always be one atom representing a given string. + * Atoms are intended to make string comparison cheaper by simplifying + * it to pointer equality. A pointer to the atom that does not own a + * reference is not guaranteed to be valid. + * + * The three forms of NS_NewPermanentAtom and do_GetPermanentAtom return + * the atom for the given string and ensure that the atom is permanent. + * An atom that is permanent will exist (occupy space at a specific + * location in memory) until XPCOM is shut down. The advantage of + * permanent atoms is that they do not need to maintain a reference + * count, which requires locking and hurts performance. + */ + /** - * Find an atom that matches the given iso-latin1 C string. The - * C string is translated into it's unicode equivalent. + * Find an atom that matches the given ISO-Latin1 C string. The + * C string is translated into its unicode equivalent. */ extern NS_COM nsIAtom* NS_NewAtom(const char* isolatin1); +extern NS_COM nsIAtom* NS_NewPermanentAtom(const char* isolatin1); + +inline already_AddRefed do_GetAtom(const char* isolatin1) + { return NS_NewAtom(isolatin1); } +inline already_AddRefed do_GetPermanentAtom(const char* isolatin1) + { return NS_NewPermanentAtom(isolatin1); } /** * Find an atom that matches the given unicode string. The string is assumed * to be zero terminated. */ extern NS_COM nsIAtom* NS_NewAtom(const PRUnichar* unicode); +extern NS_COM nsIAtom* NS_NewPermanentAtom(const PRUnichar* unicode); + +inline already_AddRefed do_GetAtom(const PRUnichar* unicode) + { return NS_NewAtom(unicode); } +inline already_AddRefed do_GetPermanentAtom(const PRUnichar* unicode) + { return NS_NewPermanentAtom(unicode); } /** * Find an atom that matches the given string. */ extern NS_COM nsIAtom* NS_NewAtom(const nsAReadableString& aString); +extern NS_COM nsIAtom* NS_NewPermanentAtom(const nsAReadableString& aString); + +inline already_AddRefed do_GetAtom(const nsAReadableString& aString) + { return NS_NewAtom(aString); } +inline already_AddRefed do_GetPermanentAtom(const nsAReadableString& aString) + { return NS_NewPermanentAtom(aString); } /** * Return a count of the total number of atoms currently @@ -75,6 +116,4 @@ extern NS_COM nsIAtom* NS_NewAtom(const nsAReadableString& aString); */ extern NS_COM nsrefcnt NS_GetNumberOfAtoms(void); -extern NS_COM void NS_PurgeAtomTable(void); - %} diff --git a/mozilla/xpcom/ds/nsIAtomService.idl b/mozilla/xpcom/ds/nsIAtomService.idl index 1e5cad5c06b..c655e6d78ec 100644 --- a/mozilla/xpcom/ds/nsIAtomService.idl +++ b/mozilla/xpcom/ds/nsIAtomService.idl @@ -50,8 +50,24 @@ #define NS_ATOMSERVICE_CLASSNAME "Atom Service" %} +/* + * Should this really be scriptable? Using atoms from script or proxies + * could be dangerous since double-wrapping could lead to loss of + * pointer identity. + */ [scriptable, uuid(e5d0d92b-ea45-4622-ab48-302baf2094ee)] interface nsIAtomService : nsISupports { + + /** + * Version of NS_NewAtom that doesn't require linking against the + * XPCOM library. See nsIAtom.idl. + */ nsIAtom getAtom(in wstring value); + + /** + * Version of NS_NewPermanentAtom that doesn't require linking against + * the XPCOM library. See nsIAtom.idl. + */ + nsIAtom getPermanentAtom(in wstring value); }; diff --git a/mozilla/xpcom/tests/Makefile.in b/mozilla/xpcom/tests/Makefile.in index 8bc5b21892e..3cca8b6344c 100644 --- a/mozilla/xpcom/tests/Makefile.in +++ b/mozilla/xpcom/tests/Makefile.in @@ -33,24 +33,25 @@ REQUIRES = necko \ $(NULL) CPPSRCS = \ - nsIFileTest.cpp \ + FilesTest.cpp \ nsIFileEnumerator.cpp \ + nsIFileTest.cpp \ TestArray.cpp \ - TestServMgr.cpp \ - TestFactory.cpp \ + TestAtoms.cpp \ + TestAutoLock.cpp \ TestCallTemplates.cpp \ TestCOMPtr.cpp \ TestCOMPtrEq.cpp \ - TestID.cpp \ - TestXPIDLString.cpp \ - TestAtoms.cpp \ TestCRT.cpp \ - FilesTest.cpp \ - TestAutoLock.cpp \ + TestFactory.cpp \ + TestID.cpp \ TestObserverService.cpp \ + TestPermanentAtoms.cpp \ TestPipes.cpp \ + TestServMgr.cpp \ TestThreads.cpp \ TestVoidBTree.cpp \ + TestXPIDLString.cpp \ $(NULL) SIMPLE_PROGRAMS = $(CPPSRCS:.cpp=$(BIN_SUFFIX)) @@ -80,7 +81,10 @@ endif include $(topsrcdir)/config/rules.mk DEFINES += -DUSE_NSREG -INCLUDES += -I$(srcdir)/../public -I$(srcdir)/services +INCLUDES += \ + -I$(srcdir)/../ds \ + -I$(srcdir)/services \ + $(NULL) install:: $(INSTALL) $(srcdir)/test.properties $(DIST)/bin/res diff --git a/mozilla/xpcom/tests/TestPermanentAtoms.cpp b/mozilla/xpcom/tests/TestPermanentAtoms.cpp new file mode 100644 index 00000000000..8293b061a4a --- /dev/null +++ b/mozilla/xpcom/tests/TestPermanentAtoms.cpp @@ -0,0 +1,88 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +// vim:cindent:ts=8:et:sw=4: +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla 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/MPL/ + * + * 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 TestPermanentAtoms.cpp. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2001 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * L. David Baron (original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsIAtom.h" +#include "nsAtomTable.h" +#include "nsCOMPtr.h" +#include +#include "nsString.h" +#include "nsReadableUtils.h" + +static void Assert(PRBool aCondition, const char* aStatement) +{ + printf("%s: %s\n", aCondition?"PASS":"FAIL", aStatement); +} + +static void AssertString(nsIAtom *aAtom, const nsAString& aString) +{ + const PRUnichar *str; + NS_STATIC_CAST(AtomImpl*,aAtom)->GetUnicode(&str); + Assert(nsDependentString(str) == aString, "string is correct"); +} + +static void AssertPermanence(nsIAtom *aAtom, PRBool aPermanence) +{ + Assert(NS_STATIC_CAST(AtomImpl*,aAtom)->IsPermanent() == aPermanence, + aPermanence ? "atom is permanent" : "atom is not permanent"); +} + +int main() +{ + nsCOMPtr foo = do_GetAtom("foo"); + AssertString(foo, NS_LITERAL_STRING("foo")); + AssertPermanence(foo, PR_FALSE); + + nsCOMPtr foop = do_GetPermanentAtom("foo"); + AssertString(foop, NS_LITERAL_STRING("foo")); + AssertPermanence(foop, PR_TRUE); + + Assert(foo == foop, "atoms are equal"); + + nsCOMPtr barp = do_GetPermanentAtom("bar"); + AssertString(barp, NS_LITERAL_STRING("bar")); + AssertPermanence(barp, PR_TRUE); + + nsCOMPtr bar = do_GetAtom("bar"); + AssertString(bar, NS_LITERAL_STRING("bar")); + AssertPermanence(bar, PR_TRUE); + + Assert(bar == barp, "atoms are equal"); + + return 0; +} diff --git a/mozilla/xpcom/tests/makefile.win b/mozilla/xpcom/tests/makefile.win index 52770189296..1eb2f01ad52 100644 --- a/mozilla/xpcom/tests/makefile.win +++ b/mozilla/xpcom/tests/makefile.win @@ -51,6 +51,7 @@ PROG19 = .\$(OBJDIR)\TestVoidBTree.exe PROG20 = .\$(OBJDIR)\nsIFileEnumerator.exe PROG21 = .\$(OBJDIR)\TestCOMPtrEq.exe PROG22 = .\$(OBJDIR)\TestCallTemplates.exe +PROG23 = .\$(OBJDIR)\TestPermanentAtoms.exe RESFILE = timer.res @@ -74,6 +75,7 @@ PROGRAMS = $(PROG1) \ $(PROG20) \ $(PROG21) \ $(PROG22) \ + $(PROG23) \ $(NULL) LCFLAGS=-DUSE_NSREG -GX @@ -81,7 +83,8 @@ LCFLAGS=-DUSE_NSREG -GX LINCS= \ -Iservices \ - $(NULL) + -I..\ds \ + $(NULL) LLIBS= \ $(DIST)\lib\xpcom.lib \ @@ -115,5 +118,6 @@ $(PROG19): $(OBJDIR) TestVoidBTree.cpp $(PROG20): $(OBJDIR) nsIFileEnumerator.cpp $(PROG21): $(OBJDIR) TestCOMPtrEq.cpp $(PROG22): $(OBJDIR) TestCallTemplates.cpp +$(PROG23): $(OBJDIR) TestPermanentAtoms.cpp