summaryrefslogtreecommitdiff
path: root/src/vm/eehash.h
blob: 216a0e19c1b75663a68a46fa7dded8f92819b9e6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
// 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;
class SigTypeContext;

// 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);
    BOOL            GetValueSpeculative(KeyType pKey, HashDatum *pData, DWORD hashValue);

    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;


// 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 */