Bug 193031 fix category manager to use aPersist parameter. Bug 54639 Category Manager is not thread-safe. Bug 208437 separate threadsafe nsBaseHashtable into super-class nsBaseHashtableMT. r=dougt/alecf I know Sun Workshop builds are still broken... working on it.

This commit is contained in:
bsmedberg%covad.net 2005-08-11 19:43:11 +00:00
parent 0c2a7ccbbd
commit eaf415b72d
6 changed files with 367 additions and 211 deletions

View File

@ -39,7 +39,7 @@
#define nsBaseHashtable_h__
#include "nsTHashtable.h"
#include "prrwlock.h"
#include "prlock.h"
#include "nsDebug.h"
template<class KeyClass,class DataType,class UserDataType>
@ -86,27 +86,18 @@ public:
typedef typename KeyClass::KeyType KeyType;
typedef nsBaseHashtableET<KeyClass,DataType> EntryType;
/**
* The constructor does not initialize, you must call Init() after
* construction.
*/
nsBaseHashtable();
/**
* destructor finalizes and deallocates hashtable
*/
~nsBaseHashtable();
// default constructor+destructor are fine
/**
* Initialize the object.
* @param initSize the initial number of buckets in the hashtable,
default 16
* default 16
* @param threadSafe whether to provide read/write
* locking on all class methods
* @return PR_TRUE if the object was initialized properly.
*/
PRBool Init(PRUint32 initSize = PL_DHASH_MIN_SIZE,
PRBool threadSafe = PR_FALSE);
PRBool Init(PRUint32 initSize = PL_DHASH_MIN_SIZE)
{ return nsTHashtable<EntryType>::Init(initSize); }
/**
* Check whether the table has been initialized.
@ -115,6 +106,13 @@ public:
*/
PRBool IsInitialized() const { return mTable.entrySize; }
/**
* Return the number of entries in the table.
* @return number of entries
*/
PRUint32 Count() const
{ return nsTHashtable<EntryType>::Count(); }
/**
* retrieve the value for a key.
* @param aKey the key to retreive
@ -124,7 +122,18 @@ public:
* @return PR_TRUE if the key exists. If key does not exist, pData is not
* modified.
*/
PRBool Get(KeyType aKey, UserDataType* pData) const;
PRBool Get(KeyType aKey, UserDataType* pData) const
{
EntryType* ent = GetEntry(aKey);
if (!ent)
return PR_FALSE;
if (pData)
*pData = ent->mData;
return PR_TRUE;
}
/**
* put a new value for the associated key
@ -132,13 +141,23 @@ public:
* @param aData the new data
* @return always PR_TRUE, unless memory allocation failed
*/
PRBool Put(KeyType aKey, UserDataType aData);
PRBool Put(KeyType aKey, UserDataType aData)
{
EntryType* ent = PutEntry(aKey);
if (!ent)
return PR_FALSE;
ent->mData = aData;
return PR_TRUE;
}
/**
* remove the data for the associated key
* @param aKey the key to remove from the hashtable
*/
void Remove(KeyType aKey);
void Remove(KeyType aKey) { RemoveEntry(aKey); }
/**
* function type provided by the application for enumeration.
@ -161,7 +180,16 @@ public:
* @param enumFunc enumeration callback
* @param userArg passed unchanged to the EnumReadFunction
*/
PRUint32 EnumerateRead(EnumReadFunction enumFunc, void* userArg) const;
PRUint32 EnumerateRead(EnumReadFunction enumFunc, void* userArg) const
{
NS_ASSERTION(mTable.entrySize,
"nsBaseHashtable was not initialized properly.");
s_EnumReadArgs enumData = { enumFunc, userArg };
return PL_DHashTableEnumerate(NS_CONST_CAST(PLDHashTable*, &mTable),
s_EnumReadStub,
&enumData);
}
/**
* function type provided by the application for enumeration.
@ -185,16 +213,23 @@ public:
* @param enumFunc enumeration callback
* @param userArg passed unchanged to the EnumFunction
*/
PRUint32 Enumerate(EnumFunction enumFunc, void* userArg);
PRUint32 Enumerate(EnumFunction enumFunc, void* userArg)
{
NS_ASSERTION(mTable.entrySize,
"nsBaseHashtable was not initialized properly.");
s_EnumArgs enumData = { enumFunc, userArg };
return PL_DHashTableEnumerate(&mTable,
s_EnumStub,
&enumData);
}
/**
* reset the hashtable, removing all entries
*/
void Clear();
void Clear() { nsTHashtable<EntryType>::Clear(); }
protected:
PRRWLock* mLock;
#ifdef HAVE_CPP_AMBIGUITY_RESOLVING_USING
using nsTHashtable<nsBaseHashtableET<KeyClass,DataType> >::mTable;
#endif
@ -227,6 +262,46 @@ protected:
void *arg);
};
/**
* This class is a thread-safe version of nsBaseHashtable.
*/
template<class KeyClass,class DataType,class UserDataType>
class nsBaseHashtableMT :
protected nsBaseHashtable<KeyClass,DataType,UserDataType>
{
public:
typedef typename
nsBaseHashtable<KeyClass,DataType,UserDataType>::EntryType EntryType;
typedef typename
nsBaseHashtable<KeyClass,DataType,UserDataType>::KeyType KeyType;
typedef typename
nsBaseHashtable<KeyClass,DataType,UserDataType>::EnumFunction EnumFunction;
typedef typename
nsBaseHashtable<KeyClass,DataType,UserDataType>::EnumReadFunction EnumReadFunction;
nsBaseHashtableMT() : mLock(nsnull) { }
~nsBaseHashtableMT();
PRBool Init(PRUint32 initSize = PL_DHASH_MIN_SIZE);
PRBool IsInitialized() const { return (PRBool) mLock; }
PRUint32 Count() const;
PRBool Get(KeyType aKey, UserDataType* pData) const;
PRBool Put(KeyType aKey, UserDataType aData);
void Remove(KeyType aKey);
PRUint32 EnumerateRead(EnumReadFunction enumFunc, void* userArg) const;
PRUint32 Enumerate(EnumFunction enumFunc, void* userArg);
void Clear();
protected:
#ifdef HAVE_CPP_AMBIGUITY_RESOLVING_USING
using nsTHashtable<EntryType>::mTable;
#endif
PRLock* mLock;
};
//
// nsBaseHashtableET definitions
//
@ -247,170 +322,11 @@ template<class KeyClass,class DataType>
nsBaseHashtableET<KeyClass,DataType>::~nsBaseHashtableET()
{ }
//
// nsBaseHashtable definitions
//
template<class KeyClass,class DataType,class UserDataType>
nsBaseHashtable<KeyClass,DataType,UserDataType>::nsBaseHashtable()
: mLock(nsnull)
{ }
template<class KeyClass,class DataType,class UserDataType>
nsBaseHashtable<KeyClass,DataType,UserDataType>::~nsBaseHashtable()
{
if (mLock)
PR_DestroyRWLock(mLock);
}
template<class KeyClass,class DataType,class UserDataType>
PRBool
nsBaseHashtable<KeyClass,DataType,UserDataType>::Init(PRUint32 initSize,
PRBool threadSafe)
{
if (!nsTHashtable<EntryType>::Init(initSize))
return PR_FALSE;
if (!threadSafe)
{
mLock = nsnull;
return PR_TRUE;
}
mLock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, "nsBaseHashtable");
NS_WARN_IF_FALSE(mLock, "Error creating lock during nsBaseHashtable::Init()");
if (!mLock)
return PR_FALSE;
return PR_TRUE;
}
template<class KeyClass,class DataType,class UserDataType>
PRBool
nsBaseHashtable<KeyClass,DataType,UserDataType>::Get(KeyType aKey,
UserDataType* pData) const
{
if (mLock)
PR_RWLock_Rlock(mLock);
EntryType* ent = GetEntry(aKey);
if (ent)
{
if (pData)
*pData = ent->mData;
if (mLock)
PR_RWLock_Unlock(mLock);
return PR_TRUE;
}
if (mLock)
PR_RWLock_Unlock(mLock);
return PR_FALSE;
}
template<class KeyClass,class DataType,class UserDataType>
PRBool
nsBaseHashtable<KeyClass,DataType,UserDataType>::Put(KeyType aKey,
UserDataType aData)
{
if (mLock)
PR_RWLock_Wlock(mLock);
EntryType* ent = PutEntry(aKey);
if (!ent)
{
if (mLock)
PR_RWLock_Unlock(mLock);
return PR_FALSE;
}
ent->mData = aData;
if (mLock)
PR_RWLock_Unlock(mLock);
return PR_TRUE;
}
template<class KeyClass,class DataType,class UserDataType>
void
nsBaseHashtable<KeyClass,DataType,UserDataType>::Remove(KeyType aKey)
{
if (mLock)
PR_RWLock_Wlock(mLock);
RemoveEntry(aKey);
if (mLock)
PR_RWLock_Unlock(mLock);
}
template<class KeyClass,class DataType,class UserDataType>
PRUint32
nsBaseHashtable<KeyClass,DataType,UserDataType>::EnumerateRead
(EnumReadFunction fEnumCall, void* userArg) const
{
NS_ASSERTION(mTable.entrySize,
"nsBaseHashtable was not initialized properly.");
if (mLock)
PR_RWLock_Rlock(mLock);
s_EnumReadArgs enumData = { fEnumCall, userArg };
PRUint32 count =
PL_DHashTableEnumerate(NS_CONST_CAST(PLDHashTable*,&mTable),
s_EnumReadStub,
&enumData);
if (mLock)
PR_RWLock_Unlock(mLock);
return count;
}
template<class KeyClass,class DataType,class UserDataType>
PRUint32
nsBaseHashtable<KeyClass,DataType,UserDataType>::Enumerate
(EnumFunction fEnumCall, void* userArg)
{
NS_ASSERTION(mTable.entrySize,
"nsBaseHashtable was not initialized properly.");
if (mLock)
PR_RWLock_Wlock(mLock);
s_EnumArgs enumData = { fEnumCall, userArg };
PRUint32 count =
PL_DHashTableEnumerate(&mTable,
s_EnumStub,
&enumData);
if (mLock)
PR_RWLock_Unlock(mLock);
return count;
}
template<class KeyClass,class DataType,class UserDataType>
void nsBaseHashtable<KeyClass,DataType,UserDataType>::Clear()
{
if (mLock)
PR_RWLock_Wlock(mLock);
nsTHashtable<EntryType>::Clear();
if (mLock)
PR_RWLock_Unlock(mLock);
}
template<class KeyClass,class DataType,class UserDataType>
PLDHashOperator
nsBaseHashtable<KeyClass,DataType,UserDataType>::s_EnumReadStub
@ -441,4 +357,110 @@ nsBaseHashtable<KeyClass,DataType,UserDataType>::s_EnumStub
return (eargs->func)(ent->GetKey(), ent->mData, eargs->userArg);
}
//
// nsBaseHashtableMT definitions
//
template<class KeyClass,class DataType,class UserDataType>
nsBaseHashtableMT<KeyClass,DataType,UserDataType>::~nsBaseHashtableMT()
{
if (mLock)
PR_DestroyLock(mLock);
}
template<class KeyClass,class DataType,class UserDataType>
PRBool
nsBaseHashtableMT<KeyClass,DataType,UserDataType>::Init(PRUint32 initSize)
{
if (!nsTHashtable<EntryType>::IsInitialized() && !nsTHashtable<EntryType>::Init(initSize))
return PR_FALSE;
mLock = PR_NewLock();
NS_WARN_IF_FALSE(mLock, "Error creating lock during nsBaseHashtableL::Init()");
return (mLock != nsnull);
}
template<class KeyClass,class DataType,class UserDataType>
PRUint32
nsBaseHashtableMT<KeyClass,DataType,UserDataType>::Count() const
{
PR_Lock(mLock);
PRUint32 count = nsTHashtable<EntryType>::Count();
PR_Unlock(mLock);
return count;
}
template<class KeyClass,class DataType,class UserDataType>
PRBool
nsBaseHashtableMT<KeyClass,DataType,UserDataType>::Get(KeyType aKey,
UserDataType* pData) const
{
PR_Lock(mLock);
PRBool res =
nsBaseHashtable<KeyClass,DataType,UserDataType>::Get(aKey, pData);
PR_Unlock(mLock);
return res;
}
template<class KeyClass,class DataType,class UserDataType>
PRBool
nsBaseHashtableMT<KeyClass,DataType,UserDataType>::Put(KeyType aKey,
UserDataType aData)
{
PR_Lock(mLock);
PRBool res =
nsBaseHashtable<KeyClass,DataType,UserDataType>::Put(aKey, aData);
PR_Unlock(mLock);
return res;
}
template<class KeyClass,class DataType,class UserDataType>
void
nsBaseHashtableMT<KeyClass,DataType,UserDataType>::Remove(KeyType aKey)
{
PR_Lock(mLock);
nsBaseHashtable<KeyClass,DataType,UserDataType>::Remove(aKey);
PR_Unlock(mLock);
}
template<class KeyClass,class DataType,class UserDataType>
PRUint32
nsBaseHashtableMT<KeyClass,DataType,UserDataType>::EnumerateRead
(EnumReadFunction fEnumCall, void* userArg) const
{
PR_Lock(mLock);
PRUint32 count =
nsBaseHashtable<KeyClass,DataType,UserDataType>::EnumerateRead(fEnumCall, userArg);
PR_Unlock(mLock);
return count;
}
template<class KeyClass,class DataType,class UserDataType>
PRUint32
nsBaseHashtableMT<KeyClass,DataType,UserDataType>::Enumerate
(EnumFunction fEnumCall, void* userArg)
{
PR_Lock(mLock);
PRUint32 count =
nsBaseHashtable<KeyClass,DataType,UserDataType>::Enumerate(fEnumCall, userArg);
PR_Unlock(mLock);
return count;
}
template<class KeyClass,class DataType,class UserDataType>
void
nsBaseHashtableMT<KeyClass,DataType,UserDataType>::Clear()
{
PR_Lock(mLock);
nsBaseHashtable<KeyClass,DataType,UserDataType>::Clear();
PR_Unlock(mLock);
}
#endif // nsBaseHashtable_h__

View File

@ -59,14 +59,27 @@ public:
typedef T* UserDataType;
/**
* @copydoc nsBaseHashtable::nsBaseHashtable
* @copydoc nsBaseHashtable::Get
* @param pData if the key doesn't exist, pData will be set to nsnull.
*/
nsClassHashtable() { }
PRBool Get(KeyType aKey, UserDataType* pData) const;
};
/**
* destructor, cleans up properly
*/
~nsClassHashtable() { }
/**
* Thread-safe version of nsClassHashtable
* @param KeyClass a wrapper-class for the hashtable key, see nsHashKeys.h
* for a complete specification.
* @param Class the class-type being wrapped
* @see nsInterfaceHashtable, nsClassHashtable
*/
template<class KeyClass,class T>
class nsClassHashtableMT :
public nsBaseHashtableMT< KeyClass, nsAutoPtr<T>, T* >
{
public:
typedef typename KeyClass::KeyType KeyType;
typedef T* UserDataType;
/**
* @copydoc nsBaseHashtable::Get
@ -76,7 +89,7 @@ public:
protected:
#ifdef HAVE_CPP_AMBIGUITY_RESOLVING_USING
using nsBaseHashtable<KeyClass, nsAutoPtr<T>, T*>::mLock;
using nsBaseHashtableMT<KeyClass, nsAutoPtr<T>, T*>::mLock;
#endif
};
@ -89,9 +102,6 @@ template<class KeyClass,class T>
PRBool
nsClassHashtable<KeyClass,T>::Get(KeyType aKey, T** retVal) const
{
if (mLock)
PR_RWLock_Rlock(mLock);
typename nsBaseHashtable<KeyClass,nsAutoPtr<T>,T*>::EntryType* ent =
GetEntry(aKey);
@ -100,16 +110,43 @@ nsClassHashtable<KeyClass,T>::Get(KeyType aKey, T** retVal) const
if (retVal)
*retVal = ent->mData;
if (mLock)
PR_RWLock_Unlock(mLock);
return PR_TRUE;
}
if (retVal)
*retVal = nsnull;
return PR_FALSE;
};
//
// nsClassHashtableMT definitions
//
template<class KeyClass,class T>
PRBool
nsClassHashtableMT<KeyClass,T>::Get(KeyType aKey, T** retVal) const
{
PR_Lock(mLock);
typename nsBaseHashtableMT<KeyClass,nsAutoPtr<T>,T*>::EntryType* ent =
GetEntry(aKey);
if (ent)
{
if (retVal)
*retVal = ent->mData;
PR_Unlock(mLock);
return PR_TRUE;
}
*retVal = nsnull;
if (retVal)
*retVal = nsnull;
if (mLock)
PR_RWLock_Unlock(mLock);
PR_Unlock(mLock);
return PR_FALSE;
};

View File

@ -54,4 +54,9 @@ class nsDataHashtable :
public nsBaseHashtable<KeyClass,DataType,DataType>
{ };
template<class KeyClass,class DataType>
class nsDataHashtableMT :
public nsBaseHashtableMT<KeyClass,DataType,DataType>
{ };
#endif // nsDataHashtable_h__

View File

@ -58,6 +58,7 @@
* nsUint32HashKey
* nsISupportsHashKey
* nsIDHashKey
* nsDepCharHashKey
*/
/**
@ -211,4 +212,40 @@ private:
const nsID mID;
};
/**
* hashkey wrapper for "dependent" const char*; this class does not "own"
* its string pointer.
*
* This class must only be used if the strings have a lifetime longer than
* the hashtable they occupy. This normally occurs only for static
* strings or strings that have been arena-allocated.
*
* @see nsTHashtable::EntryType for specification
*/
class NS_COM nsDepCharHashKey : public PLDHashEntryHdr
{
public:
typedef const char* KeyType;
typedef const char* KeyTypePointer;
nsDepCharHashKey(const char* aKey) { mKey = aKey; }
nsDepCharHashKey(const nsDepCharHashKey& toCopy) { mKey = toCopy.mKey; }
~nsDepCharHashKey() { }
const char* GetKey() const { return mKey; }
const char* GetKeyPointer() const { return mKey; }
PRBool KeyEquals(const char* aKey) const
{
return !strcmp(mKey, aKey);
}
static const char* KeyToPointer(const char* aKey) { return aKey; }
static PLDHashNumber HashKey(const char* aKey) { return nsCRT::HashCode(aKey); }
enum { ALLOW_MEMMOVE = PR_TRUE };
private:
const char* mKey;
};
#endif // nsTHashKeys_h__

View File

@ -57,15 +57,28 @@ class nsInterfaceHashtable :
public:
typedef typename KeyClass::KeyType KeyType;
typedef Interface* UserDataType;
/**
* @copydoc nsBaseHashtable::nsBaseHashtable
*/
nsInterfaceHashtable() { }
/**
* destructor, cleans up properly
* @copydoc nsBaseHashtable::Get
* @param pData This is an XPCOM getter, so pData is already_addrefed.
* If the key doesn't exist, pData will be set to nsnull.
*/
~nsInterfaceHashtable() { }
PRBool Get(KeyType aKey, UserDataType* pData) const;
};
/**
* Thread-safe version of nsInterfaceHashtable
* @param KeyClass a wrapper-class for the hashtable key, see nsHashKeys.h
* for a complete specification.
* @param Interface the interface-type being wrapped
*/
template<class KeyClass,class Interface>
class nsInterfaceHashtableMT :
public nsBaseHashtableMT< KeyClass, nsCOMPtr<Interface> , Interface* >
{
public:
typedef typename KeyClass::KeyType KeyType;
typedef Interface* UserDataType;
/**
* @copydoc nsBaseHashtable::Get
@ -76,7 +89,7 @@ public:
protected:
#ifdef HAVE_CPP_AMBIGUITY_RESOLVING_USING
using nsBaseHashtable<KeyClass, nsCOMPtr<Interface>, Interface*>::mLock;
using nsBaseHashtableMT<KeyClass, nsCOMPtr<Interface>, Interface*>::mLock;
#endif
};
@ -90,9 +103,6 @@ PRBool
nsInterfaceHashtable<KeyClass,Interface>::Get
(KeyType aKey, UserDataType* pInterface) const
{
if (mLock)
PR_RWLock_Rlock(mLock);
typename nsBaseHashtable<KeyClass, nsCOMPtr<Interface>, Interface*>::EntryType* ent =
GetEntry(aKey);
@ -105,8 +115,42 @@ nsInterfaceHashtable<KeyClass,Interface>::Get
NS_IF_ADDREF(*pInterface);
}
if (mLock)
PR_RWLock_Unlock(mLock);
return PR_TRUE;
}
// if the key doesn't exist, set *pInterface to null
// so that it is a valid XPCOM getter
if (pInterface)
*pInterface = nsnull;
return PR_FALSE;
}
//
// nsInterfaceHashtableMT definitions
//
template<class KeyClass,class Interface>
PRBool
nsInterfaceHashtableMT<KeyClass,Interface>::Get
(KeyType aKey, UserDataType* pInterface) const
{
PR_Lock(mLock);
typename nsBaseHashtableMT<KeyClass, nsCOMPtr<Interface>, Interface*>::EntryType* ent =
GetEntry(aKey);
if (ent)
{
if (pInterface)
{
*pInterface = ent->mData;
NS_IF_ADDREF(*pInterface);
}
PR_Unlock(mLock);
return PR_TRUE;
}
@ -116,8 +160,7 @@ nsInterfaceHashtable<KeyClass,Interface>::Get
if (pInterface)
*pInterface = nsnull;
if (mLock)
PR_RWLock_Unlock(mLock);
PR_Unlock(mLock);
return PR_FALSE;
}

View File

@ -143,6 +143,12 @@ public:
*/
typedef typename EntryType::KeyTypePointer KeyTypePointer;
/**
* Return the number of entries in the table.
* @return number of entries
*/
PRUint32 Count() const { return mTable.entryCount; }
/**
* Get the entry associated with a key.
* @param aKey the key to retrieve
@ -283,6 +289,12 @@ protected:
PLDHashEntryHdr *entry,
PRUint32 number,
void *arg);
private:
// copy constructor, not implemented
nsTHashtable(nsTHashtable<EntryType>& toCopy);
// assignment operator, not implemented
nsTHashtable<EntryType>& operator= (nsTHashtable<EntryType>& toEqual);
};
//