summaryrefslogtreecommitdiff
path: root/src/vm/eehash.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/eehash.h')
-rw-r--r--src/vm/eehash.h609
1 files changed, 609 insertions, 0 deletions
diff --git a/src/vm/eehash.h b/src/vm/eehash.h
new file mode 100644
index 0000000000..8e92ad35d9
--- /dev/null
+++ b/src/vm/eehash.h
@@ -0,0 +1,609 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+
+//
+//emp
+// File: eehash.h
+//
+// Provides hash table functionality needed in the EE - intended to be replaced later with better
+// algorithms, but which have the same interface.
+//
+// Two requirements are:
+//
+// 1. Any number of threads can be reading the hash table while another thread is writing, without error.
+// 2. Only one thread can write at a time.
+// 3. When calling ReplaceValue(), a reader will get the old value, or the new value, but not something
+// in between.
+// 4. DeleteValue() is an unsafe operation - no other threads can be in the hash table when this happens.
+//
+
+#ifndef _EE_HASH_H
+#define _EE_HASH_H
+
+#include "exceptmacros.h"
+#include "syncclean.hpp"
+#ifdef FEATURE_PREJIT
+class DataImage;
+#endif
+
+#include "util.hpp"
+
+#ifdef FEATURE_PREJIT
+#include "corcompile.h"
+#endif
+
+class AllocMemTracker;
+class ClassLoader;
+struct LockOwner;
+class NameHandle;
+struct PsetCacheKey;
+class SigTypeContext;
+
+typedef PsetCacheKey* PPsetCacheKey;
+
+// The "blob" you get to store in the hash table
+
+typedef PTR_VOID HashDatum;
+
+// The heap that you want the allocation to be done in
+
+typedef void* AllocationHeap;
+
+
+// One of these is present for each element in the table.
+// Update the SIZEOF_EEHASH_ENTRY macro below if you change this
+// struct
+
+typedef struct EEHashEntry EEHashEntry_t;
+typedef DPTR(EEHashEntry_t) PTR_EEHashEntry_t;
+struct EEHashEntry
+{
+ PTR_EEHashEntry_t pNext;
+ DWORD dwHashValue;
+ HashDatum Data;
+ BYTE Key[1]; // The key is stored inline
+};
+
+// The key[1] is a place holder for the key
+// SIZEOF_EEHASH_ENTRY is the size of struct up to (and not including) the key
+#define SIZEOF_EEHASH_ENTRY (offsetof(EEHashEntry,Key[0]))
+
+
+// Struct to hold a client's iteration state
+struct EEHashTableIteration;
+
+class GCHeap;
+
+// Generic hash table.
+
+template <class KeyType, class Helper, BOOL bDefaultCopyIsDeep>
+class EEHashTableBase
+{
+public:
+
+
+ BOOL Init(DWORD dwNumBuckets, LockOwner *pLock, AllocationHeap pHeap = 0,BOOL CheckThreadSafety = TRUE);
+
+ void InsertValue(KeyType pKey, HashDatum Data, BOOL bDeepCopyKey = bDefaultCopyIsDeep);
+ void InsertKeyAsValue(KeyType pKey, BOOL bDeepCopyKey = bDefaultCopyIsDeep);
+ BOOL DeleteValue(KeyType pKey);
+ BOOL ReplaceValue(KeyType pKey, HashDatum Data);
+ BOOL ReplaceKey(KeyType pOldKey, KeyType pNewKey);
+ void ClearHashTable();
+ void EmptyHashTable();
+ BOOL IsEmpty();
+ void Destroy();
+
+ // Reader functions. Please place any functions that can be called from the
+ // reader threads here.
+ BOOL GetValue(KeyType pKey, HashDatum *pData);
+ BOOL GetValue(KeyType pKey, HashDatum *pData, DWORD hashValue);
+
+
+ // A fast inlinable flavor of GetValue that can return false instead of the actual item
+ // if there is race with updating of the hashtable. Callers of GetValueSpeculative
+ // should fall back to the slow GetValue if GetValueSpeculative returns false.
+ // Assumes that we are in cooperative mode already. For performance-sensitive codepaths.
+ BOOL GetValueSpeculative(KeyType pKey, HashDatum *pData);
+
+ DWORD GetHash(KeyType Key);
+ DWORD GetCount();
+
+ // Walk through all the entries in the hash table, in meaningless order, without any
+ // synchronization.
+ //
+ // IterateStart()
+ // while (IterateNext())
+ // IterateGetKey();
+ //
+ // This is guaranteed to be DeleteValue-friendly if you advance the iterator before
+ // deletig, i.e. if used in the following pattern:
+ //
+ // IterateStart();
+ // BOOL keepGoing = IterateNext();
+ // while(keepGoing)
+ // {
+ // key = IterateGetKey();
+ // keepGoing = IterateNext();
+ // ...
+ // DeleteValue(key);
+ // ..
+ // }
+ void IterateStart(EEHashTableIteration *pIter);
+ BOOL IterateNext(EEHashTableIteration *pIter);
+ KeyType IterateGetKey(EEHashTableIteration *pIter);
+ HashDatum IterateGetValue(EEHashTableIteration *pIter);
+#ifdef _DEBUG
+ void SuppressSyncCheck()
+ {
+ LIMITED_METHOD_CONTRACT;
+ m_CheckThreadSafety=FALSE;
+ }
+#endif
+protected:
+ BOOL GrowHashTable();
+ EEHashEntry_t * FindItem(KeyType pKey);
+ EEHashEntry_t * FindItem(KeyType pKey, DWORD hashValue);
+
+ // A fast inlinable flavor of FindItem that can return null instead of the actual item
+ // if there is race with updating of the hashtable. Callers of FindItemSpeculative
+ // should fall back to the slow FindItem if FindItemSpeculative returns null.
+ // Assumes that we are in cooperative mode already. For performance-sensitive codepaths.
+ EEHashEntry_t * FindItemSpeculative(KeyType pKey, DWORD hashValue);
+
+ // Double buffer to fix the race condition of growhashtable (the update
+ // of m_pBuckets and m_dwNumBuckets has to be atomic, so we double buffer
+ // the structure and access it through a pointer, which can be updated
+ // atomically. The union is in order to not change the SOS macros.
+
+ struct BucketTable
+ {
+ DPTR(PTR_EEHashEntry_t) m_pBuckets; // Pointer to first entry for each bucket
+ DWORD m_dwNumBuckets;
+ } m_BucketTable[2];
+ typedef DPTR(BucketTable) PTR_BucketTable;
+
+ // In a function we MUST only read this value ONCE, as the writer thread can change
+ // the value asynchronously. We make this member volatile the compiler won't do copy propagation
+ // optimizations that can make this read happen more than once. Note that we only need
+ // this property for the readers. As they are the ones that can have
+ // this variable changed (note also that if the variable was enregistered we wouldn't
+ // have any problem)
+ // BE VERY CAREFUL WITH WHAT YOU DO WITH THIS VARIABLE AS USING IT BADLY CAN CAUSE
+ // RACING CONDITIONS
+ VolatilePtr<BucketTable, PTR_BucketTable> m_pVolatileBucketTable;
+
+
+ DWORD m_dwNumEntries;
+ AllocationHeap m_Heap;
+ Volatile<LONG> m_bGrowing;
+#ifdef _DEBUG
+ LPVOID m_lockData;
+ FnLockOwner m_pfnLockOwner;
+
+ EEThreadId m_writerThreadId;
+ BOOL m_CheckThreadSafety;
+
+#endif
+
+#ifdef _DEBUG_IMPL
+ // A thread must own a lock for a hash if it is a writer.
+ BOOL OwnLock();
+#endif // _DEBUG_IMPL
+};
+
+template <class KeyType, class Helper, BOOL bDefaultCopyIsDeep>
+class EEHashTable : public EEHashTableBase<KeyType, Helper, bDefaultCopyIsDeep>
+{
+public:
+ EEHashTable()
+ {
+ LIMITED_METHOD_CONTRACT;
+ this->m_BucketTable[0].m_pBuckets = NULL;
+ this->m_BucketTable[0].m_dwNumBuckets = 0;
+ this->m_BucketTable[1].m_pBuckets = NULL;
+ this->m_BucketTable[1].m_dwNumBuckets = 0;
+#ifndef DACCESS_COMPILE
+ this->m_pVolatileBucketTable = NULL;
+#endif
+ this->m_dwNumEntries = 0;
+ this->m_bGrowing = 0;
+#ifdef _DEBUG
+ this->m_lockData = NULL;
+ this->m_pfnLockOwner = NULL;
+#endif
+ }
+
+ ~EEHashTable()
+ {
+ WRAPPER_NO_CONTRACT;
+ this->Destroy();
+ }
+};
+
+/* to be used as static variable - no constructor/destructor, assumes zero
+ initialized memory */
+template <class KeyType, class Helper, BOOL bDefaultCopyIsDeep>
+class EEHashTableStatic : public EEHashTableBase<KeyType, Helper, bDefaultCopyIsDeep>
+{
+};
+
+class EEIntHashTableHelper
+{
+public:
+ static EEHashEntry_t *AllocateEntry(int iKey, BOOL bDeepCopy, AllocationHeap pHeap = 0)
+ {
+ CONTRACTL
+ {
+ WRAPPER(THROWS);
+ WRAPPER(GC_NOTRIGGER);
+ INJECT_FAULT(return NULL;);
+ }
+ CONTRACTL_END
+
+ _ASSERTE(!bDeepCopy && "Deep copy is not supported by the EEPtrHashTableHelper");
+
+ EEHashEntry_t *pEntry = (EEHashEntry_t *) new (nothrow) BYTE[SIZEOF_EEHASH_ENTRY + sizeof(int)];
+ if (!pEntry)
+ return NULL;
+ *((int*) pEntry->Key) = iKey;
+
+ return pEntry;
+ }
+
+ static void DeleteEntry(EEHashEntry_t *pEntry, AllocationHeap pHeap = 0)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ // Delete the entry.
+ delete [] (BYTE*) pEntry;
+ }
+
+ static BOOL CompareKeys(EEHashEntry_t *pEntry, int iKey)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ return *((int*)pEntry->Key) == iKey;
+ }
+
+ static DWORD Hash(int iKey)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ return (DWORD)iKey;
+ }
+
+ static int GetKey(EEHashEntry_t *pEntry)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ return *((int*) pEntry->Key);
+ }
+};
+typedef EEHashTable<int, EEIntHashTableHelper, FALSE> EEIntHashTable;
+
+typedef struct PtrPlusInt
+{
+ void* pValue;
+ int iValue;
+} *PPtrPlusInt;
+
+class EEPtrPlusIntHashTableHelper
+{
+public:
+ static EEHashEntry_t *AllocateEntry(PtrPlusInt ppiKey, BOOL bDeepCopy, AllocationHeap pHeap = 0)
+ {
+ CONTRACTL
+ {
+ WRAPPER(THROWS);
+ WRAPPER(GC_NOTRIGGER);
+ INJECT_FAULT(return NULL;);
+ }
+ CONTRACTL_END
+
+ _ASSERTE(!bDeepCopy && "Deep copy is not supported by the EEPtrPlusIntHashTableHelper");
+
+ EEHashEntry_t *pEntry = (EEHashEntry_t *) new (nothrow) BYTE[SIZEOF_EEHASH_ENTRY + sizeof(PtrPlusInt)];
+ if (!pEntry)
+ return NULL;
+ *((PPtrPlusInt) pEntry->Key) = ppiKey;
+
+ return pEntry;
+ }
+
+ static void DeleteEntry(EEHashEntry_t *pEntry, AllocationHeap pHeap = 0)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ // Delete the entry.
+ delete [] (BYTE*) pEntry;
+ }
+
+ static BOOL CompareKeys(EEHashEntry_t *pEntry, PtrPlusInt ppiKey)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ return (((PPtrPlusInt)pEntry->Key)->pValue == ppiKey.pValue) &&
+ (((PPtrPlusInt)pEntry->Key)->iValue == ppiKey.iValue);
+ }
+
+ static DWORD Hash(PtrPlusInt ppiKey)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ return (DWORD)ppiKey.iValue ^
+#ifdef _TARGET_X86_
+ (DWORD)(size_t) ppiKey.pValue;
+#else
+ // <TODO> IA64: Is this a good hashing mechanism on IA64?</TODO>
+ (DWORD)(((size_t) ppiKey.pValue) >> 3);
+#endif
+ }
+
+ static PtrPlusInt GetKey(EEHashEntry_t *pEntry)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ return *((PPtrPlusInt) pEntry->Key);
+ }
+};
+
+typedef EEHashTable<PtrPlusInt, EEPtrPlusIntHashTableHelper, FALSE> EEPtrPlusIntHashTable;
+
+// UTF8 string hash table. The UTF8 strings are NULL terminated.
+
+class EEUtf8HashTableHelper
+{
+public:
+ static EEHashEntry_t * AllocateEntry(LPCUTF8 pKey, BOOL bDeepCopy, AllocationHeap Heap);
+ static void DeleteEntry(EEHashEntry_t *pEntry, AllocationHeap Heap);
+ static BOOL CompareKeys(EEHashEntry_t *pEntry, LPCUTF8 pKey);
+ static DWORD Hash(LPCUTF8 pKey);
+ static LPCUTF8 GetKey(EEHashEntry_t *pEntry);
+};
+
+typedef EEHashTable<LPCUTF8, EEUtf8HashTableHelper, TRUE> EEUtf8StringHashTable;
+typedef DPTR(EEUtf8StringHashTable) PTR_EEUtf8StringHashTable;
+
+// Unicode String hash table - the keys are UNICODE strings which may
+// contain embedded nulls. An EEStringData struct is used for the key
+// which contains the length of the item. Note that this string is
+// not necessarily null terminated and should never be treated as such.
+const DWORD ONLY_LOW_CHARS_MASK = 0x80000000;
+
+class EEStringData
+{
+private:
+ LPCWSTR szString; // The string data.
+ DWORD cch; // Characters in the string.
+#ifdef _DEBUG
+ BOOL bDebugOnlyLowChars; // Does the string contain only characters less than 0x80?
+ DWORD dwDebugCch;
+#endif // _DEBUG
+
+public:
+ // explicilty initialize cch to 0 because SetCharCount uses cch
+ EEStringData() : cch(0)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ SetStringBuffer(NULL);
+ SetCharCount(0);
+ SetIsOnlyLowChars(FALSE);
+ };
+ EEStringData(DWORD cchString, LPCWSTR str) : cch(0)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ SetStringBuffer(str);
+ SetCharCount(cchString);
+ SetIsOnlyLowChars(FALSE);
+ };
+ EEStringData(DWORD cchString, LPCWSTR str, BOOL onlyLow) : cch(0)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ SetStringBuffer(str);
+ SetCharCount(cchString);
+ SetIsOnlyLowChars(onlyLow);
+ };
+ inline ULONG GetCharCount() const
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ _ASSERTE ((cch & ~ONLY_LOW_CHARS_MASK) == dwDebugCch);
+ return (cch & ~ONLY_LOW_CHARS_MASK);
+ }
+ inline void SetCharCount(ULONG _cch)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+#ifdef _DEBUG
+ dwDebugCch = _cch;
+#endif // _DEBUG
+ cch = ((DWORD)_cch) | (cch & ONLY_LOW_CHARS_MASK);
+ }
+ inline LPCWSTR GetStringBuffer() const
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ return (szString);
+ }
+ inline void SetStringBuffer(LPCWSTR _szString)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ szString = _szString;
+ }
+ inline BOOL GetIsOnlyLowChars() const
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ _ASSERTE(bDebugOnlyLowChars == ((cch & ONLY_LOW_CHARS_MASK) ? TRUE : FALSE));
+ return ((cch & ONLY_LOW_CHARS_MASK) ? TRUE : FALSE);
+ }
+ inline void SetIsOnlyLowChars(BOOL bIsOnlyLowChars)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+#ifdef _DEBUG
+ bDebugOnlyLowChars = bIsOnlyLowChars;
+#endif // _DEBUG
+ bIsOnlyLowChars ? (cch |= ONLY_LOW_CHARS_MASK) : (cch &= ~ONLY_LOW_CHARS_MASK);
+ }
+};
+
+class EEUnicodeHashTableHelper
+{
+public:
+ static EEHashEntry_t * AllocateEntry(EEStringData *pKey, BOOL bDeepCopy, AllocationHeap Heap);
+ static void DeleteEntry(EEHashEntry_t *pEntry, AllocationHeap Heap);
+ static BOOL CompareKeys(EEHashEntry_t *pEntry, EEStringData *pKey);
+ static DWORD Hash(EEStringData *pKey);
+ static EEStringData * GetKey(EEHashEntry_t *pEntry);
+ static void ReplaceKey(EEHashEntry_t *pEntry, EEStringData *pNewKey);
+};
+
+typedef EEHashTable<EEStringData *, EEUnicodeHashTableHelper, TRUE> EEUnicodeStringHashTable;
+
+
+class EEUnicodeStringLiteralHashTableHelper
+{
+public:
+ static EEHashEntry_t * AllocateEntry(EEStringData *pKey, BOOL bDeepCopy, AllocationHeap Heap);
+ static void DeleteEntry(EEHashEntry_t *pEntry, AllocationHeap Heap);
+ static BOOL CompareKeys(EEHashEntry_t *pEntry, EEStringData *pKey);
+ static DWORD Hash(EEStringData *pKey);
+ static void ReplaceKey(EEHashEntry_t *pEntry, EEStringData *pNewKey);
+};
+
+typedef EEHashTable<EEStringData *, EEUnicodeStringLiteralHashTableHelper, TRUE> EEUnicodeStringLiteralHashTable;
+
+// Permission set hash table.
+
+class EEPsetHashTableHelper
+{
+public:
+ static EEHashEntry_t * AllocateEntry(PsetCacheKey *pKey, BOOL bDeepCopy, AllocationHeap Heap);
+ static void DeleteEntry(EEHashEntry_t *pEntry, AllocationHeap Heap);
+ static BOOL CompareKeys(EEHashEntry_t *pEntry, PsetCacheKey *pKey);
+ static DWORD Hash(PsetCacheKey *pKey);
+ static PsetCacheKey *GetKey(EEHashEntry_t *pEntry);
+};
+
+typedef EEHashTable<PsetCacheKey *, EEPsetHashTableHelper, FALSE> EEPsetHashTable;
+
+
+// Generic pointer hash table helper.
+
+template <class KeyPointerType>
+class EEPtrHashTableHelper
+{
+public:
+ static EEHashEntry_t *AllocateEntry(KeyPointerType pKey, BOOL bDeepCopy, AllocationHeap Heap)
+ {
+ CONTRACTL
+ {
+ WRAPPER(THROWS);
+ WRAPPER(GC_NOTRIGGER);
+ INJECT_FAULT(return FALSE;);
+ }
+ CONTRACTL_END
+
+ _ASSERTE(!bDeepCopy && "Deep copy is not supported by the EEPtrHashTableHelper");
+ _ASSERTE(sizeof(KeyPointerType) == sizeof(void *) && "KeyPointerType must be a pointer type");
+
+ EEHashEntry_t *pEntry = (EEHashEntry_t *) new (nothrow) BYTE[SIZEOF_EEHASH_ENTRY + sizeof(KeyPointerType)];
+ if (!pEntry)
+ return NULL;
+ *((KeyPointerType*)pEntry->Key) = pKey;
+
+ return pEntry;
+ }
+
+ static void DeleteEntry(EEHashEntry_t *pEntry, AllocationHeap Heap)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ // Delete the entry.
+ delete [] (BYTE*) pEntry;
+ }
+
+ static BOOL CompareKeys(EEHashEntry_t *pEntry, KeyPointerType pKey)
+ {
+ LIMITED_METHOD_CONTRACT;
+ SUPPORTS_DAC;
+
+ KeyPointerType pEntryKey = *((KeyPointerType*)pEntry->Key);
+ return pEntryKey == pKey;
+ }
+
+ static DWORD Hash(KeyPointerType pKey)
+ {
+ LIMITED_METHOD_CONTRACT;
+ SUPPORTS_DAC;
+
+#ifdef _TARGET_X86_
+ return (DWORD)(size_t) dac_cast<TADDR>(pKey);
+#else
+ // <TODO> IA64: Is this a good hashing mechanism on IA64?</TODO>
+ return (DWORD)(((size_t) dac_cast<TADDR>(pKey)) >> 3);
+#endif
+ }
+
+ static KeyPointerType GetKey(EEHashEntry_t *pEntry)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ return *((KeyPointerType*)pEntry->Key);
+ }
+};
+
+typedef EEHashTable<PTR_VOID, EEPtrHashTableHelper<PTR_VOID>, FALSE> EEPtrHashTable;
+typedef DPTR(EEPtrHashTable) PTR_EEPtrHashTable;
+
+// Define a hash of generic instantiations (represented by a SigTypeContext).
+class EEInstantiationHashTableHelper
+{
+public:
+ static EEHashEntry_t *AllocateEntry(const SigTypeContext *pKey, BOOL bDeepCopy, AllocationHeap pHeap = 0);
+ static void DeleteEntry(EEHashEntry_t *pEntry, AllocationHeap pHeap = 0);
+ static BOOL CompareKeys(EEHashEntry_t *pEntry, const SigTypeContext *pKey);
+ static DWORD Hash(const SigTypeContext *pKey);
+ static const SigTypeContext *GetKey(EEHashEntry_t *pEntry);
+};
+typedef EEHashTable<const SigTypeContext*, EEInstantiationHashTableHelper, FALSE> EEInstantiationHashTable;
+
+// ComComponentInfo hashtable.
+
+struct ClassFactoryInfo
+{
+ GUID m_clsid;
+ WCHAR *m_strServerName;
+};
+
+class EEClassFactoryInfoHashTableHelper
+{
+public:
+ static EEHashEntry_t *AllocateEntry(ClassFactoryInfo *pKey, BOOL bDeepCopy, AllocationHeap Heap);
+ static void DeleteEntry(EEHashEntry_t *pEntry, AllocationHeap Heap);
+ static BOOL CompareKeys(EEHashEntry_t *pEntry, ClassFactoryInfo *pKey);
+ static DWORD Hash(ClassFactoryInfo *pKey);
+ static ClassFactoryInfo *GetKey(EEHashEntry_t *pEntry);
+};
+
+typedef EEHashTable<ClassFactoryInfo *, EEClassFactoryInfoHashTableHelper, TRUE> EEClassFactoryInfoHashTable;
+// Struct to hold a client's iteration state
+struct EEHashTableIteration
+{
+ DWORD m_dwBucket;
+ EEHashEntry_t *m_pEntry;
+
+#ifdef _DEBUG
+ void *m_pTable;
+#endif
+};
+
+#endif /* _EE_HASH_H */