1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
10 // Provides hash table functionality needed in the EE - intended to be replaced later with better
11 // algorithms, but which have the same interface.
13 // Two requirements are:
15 // 1. Any number of threads can be reading the hash table while another thread is writing, without error.
16 // 2. Only one thread can write at a time.
17 // 3. When calling ReplaceValue(), a reader will get the old value, or the new value, but not something
19 // 4. DeleteValue() is an unsafe operation - no other threads can be in the hash table when this happens.
25 #include "exceptmacros.h"
26 #include "syncclean.hpp"
34 #include "corcompile.h"
37 class AllocMemTracker;
43 // The "blob" you get to store in the hash table
45 typedef PTR_VOID HashDatum;
47 // The heap that you want the allocation to be done in
49 typedef void* AllocationHeap;
52 // One of these is present for each element in the table.
53 // Update the SIZEOF_EEHASH_ENTRY macro below if you change this
56 typedef struct EEHashEntry EEHashEntry_t;
57 typedef DPTR(EEHashEntry_t) PTR_EEHashEntry_t;
60 PTR_EEHashEntry_t pNext;
63 BYTE Key[1]; // The key is stored inline
66 // The key[1] is a place holder for the key
67 // SIZEOF_EEHASH_ENTRY is the size of struct up to (and not including) the key
68 #define SIZEOF_EEHASH_ENTRY (offsetof(EEHashEntry,Key[0]))
71 // Struct to hold a client's iteration state
72 struct EEHashTableIteration;
76 // Generic hash table.
78 template <class KeyType, class Helper, BOOL bDefaultCopyIsDeep>
84 BOOL Init(DWORD dwNumBuckets, LockOwner *pLock, AllocationHeap pHeap = 0,BOOL CheckThreadSafety = TRUE);
86 void InsertValue(KeyType pKey, HashDatum Data, BOOL bDeepCopyKey = bDefaultCopyIsDeep);
87 void InsertKeyAsValue(KeyType pKey, BOOL bDeepCopyKey = bDefaultCopyIsDeep);
88 BOOL DeleteValue(KeyType pKey);
89 BOOL ReplaceValue(KeyType pKey, HashDatum Data);
90 BOOL ReplaceKey(KeyType pOldKey, KeyType pNewKey);
91 void ClearHashTable();
92 void EmptyHashTable();
96 // Reader functions. Please place any functions that can be called from the
97 // reader threads here.
98 BOOL GetValue(KeyType pKey, HashDatum *pData);
99 BOOL GetValue(KeyType pKey, HashDatum *pData, DWORD hashValue);
102 // A fast inlinable flavor of GetValue that can return false instead of the actual item
103 // if there is race with updating of the hashtable. Callers of GetValueSpeculative
104 // should fall back to the slow GetValue if GetValueSpeculative returns false.
105 // Assumes that we are in cooperative mode already. For performance-sensitive codepaths.
106 BOOL GetValueSpeculative(KeyType pKey, HashDatum *pData);
108 DWORD GetHash(KeyType Key);
111 // Walk through all the entries in the hash table, in meaningless order, without any
115 // while (IterateNext())
118 // This is guaranteed to be DeleteValue-friendly if you advance the iterator before
119 // deletig, i.e. if used in the following pattern:
122 // BOOL keepGoing = IterateNext();
125 // key = IterateGetKey();
126 // keepGoing = IterateNext();
131 void IterateStart(EEHashTableIteration *pIter);
132 BOOL IterateNext(EEHashTableIteration *pIter);
133 KeyType IterateGetKey(EEHashTableIteration *pIter);
134 HashDatum IterateGetValue(EEHashTableIteration *pIter);
136 void SuppressSyncCheck()
138 LIMITED_METHOD_CONTRACT;
139 m_CheckThreadSafety=FALSE;
143 BOOL GrowHashTable();
144 EEHashEntry_t * FindItem(KeyType pKey);
145 EEHashEntry_t * FindItem(KeyType pKey, DWORD hashValue);
147 // A fast inlinable flavor of FindItem that can return null instead of the actual item
148 // if there is race with updating of the hashtable. Callers of FindItemSpeculative
149 // should fall back to the slow FindItem if FindItemSpeculative returns null.
150 // Assumes that we are in cooperative mode already. For performance-sensitive codepaths.
151 EEHashEntry_t * FindItemSpeculative(KeyType pKey, DWORD hashValue);
153 // Double buffer to fix the race condition of growhashtable (the update
154 // of m_pBuckets and m_dwNumBuckets has to be atomic, so we double buffer
155 // the structure and access it through a pointer, which can be updated
156 // atomically. The union is in order to not change the SOS macros.
160 DPTR(PTR_EEHashEntry_t) m_pBuckets; // Pointer to first entry for each bucket
161 DWORD m_dwNumBuckets;
163 typedef DPTR(BucketTable) PTR_BucketTable;
165 // In a function we MUST only read this value ONCE, as the writer thread can change
166 // the value asynchronously. We make this member volatile the compiler won't do copy propagation
167 // optimizations that can make this read happen more than once. Note that we only need
168 // this property for the readers. As they are the ones that can have
169 // this variable changed (note also that if the variable was enregistered we wouldn't
171 // BE VERY CAREFUL WITH WHAT YOU DO WITH THIS VARIABLE AS USING IT BADLY CAN CAUSE
173 VolatilePtr<BucketTable, PTR_BucketTable> m_pVolatileBucketTable;
176 DWORD m_dwNumEntries;
177 AllocationHeap m_Heap;
178 Volatile<LONG> m_bGrowing;
181 FnLockOwner m_pfnLockOwner;
183 EEThreadId m_writerThreadId;
184 BOOL m_CheckThreadSafety;
189 // A thread must own a lock for a hash if it is a writer.
191 #endif // _DEBUG_IMPL
194 template <class KeyType, class Helper, BOOL bDefaultCopyIsDeep>
195 class EEHashTable : public EEHashTableBase<KeyType, Helper, bDefaultCopyIsDeep>
200 LIMITED_METHOD_CONTRACT;
201 this->m_BucketTable[0].m_pBuckets = NULL;
202 this->m_BucketTable[0].m_dwNumBuckets = 0;
203 this->m_BucketTable[1].m_pBuckets = NULL;
204 this->m_BucketTable[1].m_dwNumBuckets = 0;
205 #ifndef DACCESS_COMPILE
206 this->m_pVolatileBucketTable = NULL;
208 this->m_dwNumEntries = 0;
209 this->m_bGrowing = 0;
211 this->m_lockData = NULL;
212 this->m_pfnLockOwner = NULL;
223 /* to be used as static variable - no constructor/destructor, assumes zero
224 initialized memory */
225 template <class KeyType, class Helper, BOOL bDefaultCopyIsDeep>
226 class EEHashTableStatic : public EEHashTableBase<KeyType, Helper, bDefaultCopyIsDeep>
230 class EEIntHashTableHelper
233 static EEHashEntry_t *AllocateEntry(int iKey, BOOL bDeepCopy, AllocationHeap pHeap = 0)
238 WRAPPER(GC_NOTRIGGER);
239 INJECT_FAULT(return NULL;);
243 _ASSERTE(!bDeepCopy && "Deep copy is not supported by the EEPtrHashTableHelper");
245 EEHashEntry_t *pEntry = (EEHashEntry_t *) new (nothrow) BYTE[SIZEOF_EEHASH_ENTRY + sizeof(int)];
248 *((int*) pEntry->Key) = iKey;
253 static void DeleteEntry(EEHashEntry_t *pEntry, AllocationHeap pHeap = 0)
255 LIMITED_METHOD_CONTRACT;
258 delete [] (BYTE*) pEntry;
261 static BOOL CompareKeys(EEHashEntry_t *pEntry, int iKey)
263 LIMITED_METHOD_CONTRACT;
265 return *((int*)pEntry->Key) == iKey;
268 static DWORD Hash(int iKey)
270 LIMITED_METHOD_CONTRACT;
275 static int GetKey(EEHashEntry_t *pEntry)
277 LIMITED_METHOD_CONTRACT;
279 return *((int*) pEntry->Key);
282 typedef EEHashTable<int, EEIntHashTableHelper, FALSE> EEIntHashTable;
284 typedef struct PtrPlusInt
290 class EEPtrPlusIntHashTableHelper
293 static EEHashEntry_t *AllocateEntry(PtrPlusInt ppiKey, BOOL bDeepCopy, AllocationHeap pHeap = 0)
298 WRAPPER(GC_NOTRIGGER);
299 INJECT_FAULT(return NULL;);
303 _ASSERTE(!bDeepCopy && "Deep copy is not supported by the EEPtrPlusIntHashTableHelper");
305 EEHashEntry_t *pEntry = (EEHashEntry_t *) new (nothrow) BYTE[SIZEOF_EEHASH_ENTRY + sizeof(PtrPlusInt)];
308 *((PPtrPlusInt) pEntry->Key) = ppiKey;
313 static void DeleteEntry(EEHashEntry_t *pEntry, AllocationHeap pHeap = 0)
315 LIMITED_METHOD_CONTRACT;
318 delete [] (BYTE*) pEntry;
321 static BOOL CompareKeys(EEHashEntry_t *pEntry, PtrPlusInt ppiKey)
323 LIMITED_METHOD_CONTRACT;
325 return (((PPtrPlusInt)pEntry->Key)->pValue == ppiKey.pValue) &&
326 (((PPtrPlusInt)pEntry->Key)->iValue == ppiKey.iValue);
329 static DWORD Hash(PtrPlusInt ppiKey)
331 LIMITED_METHOD_CONTRACT;
333 return (DWORD)ppiKey.iValue ^
335 (DWORD)(size_t) ppiKey.pValue;
337 // <TODO> IA64: Is this a good hashing mechanism on IA64?</TODO>
338 (DWORD)(((size_t) ppiKey.pValue) >> 3);
342 static PtrPlusInt GetKey(EEHashEntry_t *pEntry)
344 LIMITED_METHOD_CONTRACT;
346 return *((PPtrPlusInt) pEntry->Key);
350 typedef EEHashTable<PtrPlusInt, EEPtrPlusIntHashTableHelper, FALSE> EEPtrPlusIntHashTable;
352 // UTF8 string hash table. The UTF8 strings are NULL terminated.
354 class EEUtf8HashTableHelper
357 static EEHashEntry_t * AllocateEntry(LPCUTF8 pKey, BOOL bDeepCopy, AllocationHeap Heap);
358 static void DeleteEntry(EEHashEntry_t *pEntry, AllocationHeap Heap);
359 static BOOL CompareKeys(EEHashEntry_t *pEntry, LPCUTF8 pKey);
360 static DWORD Hash(LPCUTF8 pKey);
361 static LPCUTF8 GetKey(EEHashEntry_t *pEntry);
364 typedef EEHashTable<LPCUTF8, EEUtf8HashTableHelper, TRUE> EEUtf8StringHashTable;
365 typedef DPTR(EEUtf8StringHashTable) PTR_EEUtf8StringHashTable;
367 // Unicode String hash table - the keys are UNICODE strings which may
368 // contain embedded nulls. An EEStringData struct is used for the key
369 // which contains the length of the item. Note that this string is
370 // not necessarily null terminated and should never be treated as such.
371 const DWORD ONLY_LOW_CHARS_MASK = 0x80000000;
376 LPCWSTR szString; // The string data.
377 DWORD cch; // Characters in the string.
379 BOOL bDebugOnlyLowChars; // Does the string contain only characters less than 0x80?
384 // explicilty initialize cch to 0 because SetCharCount uses cch
385 EEStringData() : cch(0)
387 LIMITED_METHOD_CONTRACT;
389 SetStringBuffer(NULL);
391 SetIsOnlyLowChars(FALSE);
393 EEStringData(DWORD cchString, LPCWSTR str) : cch(0)
395 LIMITED_METHOD_CONTRACT;
397 SetStringBuffer(str);
398 SetCharCount(cchString);
399 SetIsOnlyLowChars(FALSE);
401 EEStringData(DWORD cchString, LPCWSTR str, BOOL onlyLow) : cch(0)
403 LIMITED_METHOD_CONTRACT;
405 SetStringBuffer(str);
406 SetCharCount(cchString);
407 SetIsOnlyLowChars(onlyLow);
409 inline ULONG GetCharCount() const
411 LIMITED_METHOD_CONTRACT;
413 _ASSERTE ((cch & ~ONLY_LOW_CHARS_MASK) == dwDebugCch);
414 return (cch & ~ONLY_LOW_CHARS_MASK);
416 inline void SetCharCount(ULONG _cch)
418 LIMITED_METHOD_CONTRACT;
423 cch = ((DWORD)_cch) | (cch & ONLY_LOW_CHARS_MASK);
425 inline LPCWSTR GetStringBuffer() const
427 LIMITED_METHOD_CONTRACT;
431 inline void SetStringBuffer(LPCWSTR _szString)
433 LIMITED_METHOD_CONTRACT;
435 szString = _szString;
437 inline BOOL GetIsOnlyLowChars() const
439 LIMITED_METHOD_CONTRACT;
441 _ASSERTE(bDebugOnlyLowChars == ((cch & ONLY_LOW_CHARS_MASK) ? TRUE : FALSE));
442 return ((cch & ONLY_LOW_CHARS_MASK) ? TRUE : FALSE);
444 inline void SetIsOnlyLowChars(BOOL bIsOnlyLowChars)
446 LIMITED_METHOD_CONTRACT;
449 bDebugOnlyLowChars = bIsOnlyLowChars;
451 bIsOnlyLowChars ? (cch |= ONLY_LOW_CHARS_MASK) : (cch &= ~ONLY_LOW_CHARS_MASK);
455 class EEUnicodeHashTableHelper
458 static EEHashEntry_t * AllocateEntry(EEStringData *pKey, BOOL bDeepCopy, AllocationHeap Heap);
459 static void DeleteEntry(EEHashEntry_t *pEntry, AllocationHeap Heap);
460 static BOOL CompareKeys(EEHashEntry_t *pEntry, EEStringData *pKey);
461 static DWORD Hash(EEStringData *pKey);
462 static EEStringData * GetKey(EEHashEntry_t *pEntry);
463 static void ReplaceKey(EEHashEntry_t *pEntry, EEStringData *pNewKey);
466 typedef EEHashTable<EEStringData *, EEUnicodeHashTableHelper, TRUE> EEUnicodeStringHashTable;
469 class EEUnicodeStringLiteralHashTableHelper
472 static EEHashEntry_t * AllocateEntry(EEStringData *pKey, BOOL bDeepCopy, AllocationHeap Heap);
473 static void DeleteEntry(EEHashEntry_t *pEntry, AllocationHeap Heap);
474 static BOOL CompareKeys(EEHashEntry_t *pEntry, EEStringData *pKey);
475 static DWORD Hash(EEStringData *pKey);
476 static void ReplaceKey(EEHashEntry_t *pEntry, EEStringData *pNewKey);
479 typedef EEHashTable<EEStringData *, EEUnicodeStringLiteralHashTableHelper, TRUE> EEUnicodeStringLiteralHashTable;
482 // Generic pointer hash table helper.
484 template <class KeyPointerType>
485 class EEPtrHashTableHelper
488 static EEHashEntry_t *AllocateEntry(KeyPointerType pKey, BOOL bDeepCopy, AllocationHeap Heap)
493 WRAPPER(GC_NOTRIGGER);
494 INJECT_FAULT(return FALSE;);
498 _ASSERTE(!bDeepCopy && "Deep copy is not supported by the EEPtrHashTableHelper");
499 _ASSERTE(sizeof(KeyPointerType) == sizeof(void *) && "KeyPointerType must be a pointer type");
501 EEHashEntry_t *pEntry = (EEHashEntry_t *) new (nothrow) BYTE[SIZEOF_EEHASH_ENTRY + sizeof(KeyPointerType)];
504 *((KeyPointerType*)pEntry->Key) = pKey;
509 static void DeleteEntry(EEHashEntry_t *pEntry, AllocationHeap Heap)
511 LIMITED_METHOD_CONTRACT;
514 delete [] (BYTE*) pEntry;
517 static BOOL CompareKeys(EEHashEntry_t *pEntry, KeyPointerType pKey)
519 LIMITED_METHOD_CONTRACT;
522 KeyPointerType pEntryKey = *((KeyPointerType*)pEntry->Key);
523 return pEntryKey == pKey;
526 static DWORD Hash(KeyPointerType pKey)
528 LIMITED_METHOD_CONTRACT;
532 return (DWORD)(size_t) dac_cast<TADDR>(pKey);
534 // <TODO> IA64: Is this a good hashing mechanism on IA64?</TODO>
535 return (DWORD)(((size_t) dac_cast<TADDR>(pKey)) >> 3);
539 static KeyPointerType GetKey(EEHashEntry_t *pEntry)
541 LIMITED_METHOD_CONTRACT;
543 return *((KeyPointerType*)pEntry->Key);
547 typedef EEHashTable<PTR_VOID, EEPtrHashTableHelper<PTR_VOID>, FALSE> EEPtrHashTable;
548 typedef DPTR(EEPtrHashTable) PTR_EEPtrHashTable;
550 // Define a hash of generic instantiations (represented by a SigTypeContext).
551 class EEInstantiationHashTableHelper
554 static EEHashEntry_t *AllocateEntry(const SigTypeContext *pKey, BOOL bDeepCopy, AllocationHeap pHeap = 0);
555 static void DeleteEntry(EEHashEntry_t *pEntry, AllocationHeap pHeap = 0);
556 static BOOL CompareKeys(EEHashEntry_t *pEntry, const SigTypeContext *pKey);
557 static DWORD Hash(const SigTypeContext *pKey);
558 static const SigTypeContext *GetKey(EEHashEntry_t *pEntry);
560 typedef EEHashTable<const SigTypeContext*, EEInstantiationHashTableHelper, FALSE> EEInstantiationHashTable;
562 // ComComponentInfo hashtable.
564 struct ClassFactoryInfo
567 WCHAR *m_strServerName;
570 class EEClassFactoryInfoHashTableHelper
573 static EEHashEntry_t *AllocateEntry(ClassFactoryInfo *pKey, BOOL bDeepCopy, AllocationHeap Heap);
574 static void DeleteEntry(EEHashEntry_t *pEntry, AllocationHeap Heap);
575 static BOOL CompareKeys(EEHashEntry_t *pEntry, ClassFactoryInfo *pKey);
576 static DWORD Hash(ClassFactoryInfo *pKey);
577 static ClassFactoryInfo *GetKey(EEHashEntry_t *pEntry);
580 typedef EEHashTable<ClassFactoryInfo *, EEClassFactoryInfoHashTableHelper, TRUE> EEClassFactoryInfoHashTable;
581 // Struct to hold a client's iteration state
582 struct EEHashTableIteration
585 EEHashEntry_t *m_pEntry;
592 #endif /* _EE_HASH_H */