Merge pull request #24232 from janvorli/fix-large-version-bubble-2
[platform/upstream/coreclr.git] / src / vm / stringliteralmap.cpp
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.
4
5
6 /*============================================================
7 **
8 ** Header:  Map used for interning of string literals.
9 **
10 ===========================================================*/
11
12 #include "common.h"
13 #include "eeconfig.h"
14 #include "stringliteralmap.h"
15
16 /*
17     Thread safety in GlobalStringLiteralMap / StringLiteralMap
18
19     A single lock protects the N StringLiteralMap objects and single
20     GlobalStringLiteralMap rooted in the SystemDomain at any time. It is
21
22     SystemDomain::GetGlobalStringLiteralMap()->m_HashTableCrstGlobal
23
24     At one time each StringLiteralMap had it's own lock to protect
25     the entry hash table as well, and Interlocked operations were done on the
26     ref count of the contained StringLiteralEntries. But anything of import
27     needed to be done under the global lock mentioned above or races would
28     result. (For example, an app domain shuts down, doing final release on
29     a StringLiteralEntry, but at that moment the entry is being handed out
30     in another appdomain and addref'd only after the count went to 0.)
31
32     The rule is:
33
34     Any AddRef()/Release() calls on StringLiteralEntry need to be under the lock.
35     Any insert/deletes from the StringLiteralMap or GlobalStringLiteralMap
36     need to be done under the lock.
37
38     The only thing you can do without the lock is look up an existing StringLiteralEntry
39     in an StringLiteralMap hash table. This is true because these lookup calls
40     will all come before destruction of the map, the hash table is safe for multiple readers,
41     and we know the StringLiteralEntry so found 1) can't be destroyed because that table keeps
42     an AddRef on it and 2) isn't internally modified once created.
43 */
44     
45 #define GLOBAL_STRING_TABLE_BUCKET_SIZE 128
46 #define INIT_NUM_APP_DOMAIN_STRING_BUCKETS 59
47 #define INIT_NUM_GLOBAL_STRING_BUCKETS 131
48
49 // assumes that memory pools's per block data is same as sizeof (StringLiteralEntry) 
50 #define EEHASH_MEMORY_POOL_GROW_COUNT 128
51
52 StringLiteralEntryArray *StringLiteralEntry::s_EntryList = NULL;
53 DWORD StringLiteralEntry::s_UsedEntries = NULL;
54 StringLiteralEntry *StringLiteralEntry::s_FreeEntryList = NULL;
55
56 StringLiteralMap::StringLiteralMap()
57 : m_StringToEntryHashTable(NULL)
58 , m_MemoryPool(NULL)
59 {
60     CONTRACTL
61     {
62         THROWS;
63         GC_NOTRIGGER;
64     }
65     CONTRACTL_END;
66 }
67
68 void StringLiteralMap::Init()
69 {
70     CONTRACTL
71     {
72         THROWS;
73         GC_TRIGGERS;
74         PRECONDITION(CheckPointer(this));
75         INJECT_FAULT(ThrowOutOfMemory());
76     }
77     CONTRACTL_END;
78
79     // Allocate the memory pool and set the initial count to quarter as grow count
80     m_MemoryPool = new MemoryPool (SIZEOF_EEHASH_ENTRY, EEHASH_MEMORY_POOL_GROW_COUNT, EEHASH_MEMORY_POOL_GROW_COUNT/4);
81
82     m_StringToEntryHashTable =  new EEUnicodeStringLiteralHashTable ();
83
84     LockOwner lock = {&(SystemDomain::GetGlobalStringLiteralMap()->m_HashTableCrstGlobal), IsOwnerOfCrst};
85     if (!m_StringToEntryHashTable->Init(INIT_NUM_APP_DOMAIN_STRING_BUCKETS, &lock, m_MemoryPool))
86         ThrowOutOfMemory();
87 }
88
89 StringLiteralMap::~StringLiteralMap()
90 {
91     CONTRACTL
92     {
93         NOTHROW;
94         GC_TRIGGERS;
95     }
96     CONTRACTL_END;
97
98     // We do need to take the globalstringliteralmap lock because we are manipulating
99     // StringLiteralEntry objects that belong to it.
100     // Note that we remember the current entry and relaese it only when the 
101     // enumerator has advanced to the next entry so that we don't endup deleteing the
102     // current entry itself and killing the enumerator.
103
104     if (m_StringToEntryHashTable != NULL)
105     {
106         // We need the global lock anytime we release StringLiteralEntry objects
107         CrstHolder gch(&(SystemDomain::GetGlobalStringLiteralMapNoCreate()->m_HashTableCrstGlobal));
108
109         StringLiteralEntry *pEntry = NULL;
110         EEHashTableIteration Iter;
111
112 #ifdef _DEBUG
113         m_StringToEntryHashTable->SuppressSyncCheck();
114 #endif
115
116         m_StringToEntryHashTable->IterateStart(&Iter);
117         if (m_StringToEntryHashTable->IterateNext(&Iter))
118         {
119             pEntry = (StringLiteralEntry*)m_StringToEntryHashTable->IterateGetValue(&Iter);
120
121             while (m_StringToEntryHashTable->IterateNext(&Iter))
122             {
123                 // Release the previous entry
124                 _ASSERTE(pEntry);
125                 pEntry->Release();
126
127                 // Set the 
128                 pEntry = (StringLiteralEntry*)m_StringToEntryHashTable->IterateGetValue(&Iter);
129             }
130             // Release the last entry
131             _ASSERTE(pEntry);
132             pEntry->Release();
133         }
134         // else there were no entries.
135
136         // Delete the hash table first. The dtor of the hash table would clean up all the entries.
137         delete m_StringToEntryHashTable;
138     }
139
140     // Delete the pool later, since the dtor above would need it.
141     if (m_MemoryPool != NULL)
142         delete m_MemoryPool;
143 }
144
145
146
147 STRINGREF *StringLiteralMap::GetStringLiteral(EEStringData *pStringData, BOOL bAddIfNotFound, BOOL bAppDomainWontUnload)
148 {
149     CONTRACTL
150     {
151         THROWS;
152         GC_TRIGGERS;
153         MODE_COOPERATIVE;
154         PRECONDITION(CheckPointer(this));
155         PRECONDITION(CheckPointer(pStringData));
156     }
157     CONTRACTL_END;
158
159     HashDatum Data;
160
161     DWORD dwHash = m_StringToEntryHashTable->GetHash(pStringData);
162     // Retrieve the string literal from the global string literal map.
163     CrstHolder gch(&(SystemDomain::GetGlobalStringLiteralMap()->m_HashTableCrstGlobal));
164
165     // TODO: We can be more efficient by checking our local hash table now to see if
166     // someone beat us to inserting it. (m_StringToEntryHashTable->GetValue(pStringData, &Data))
167     // (Rather than waiting until after we look the string up in the global map) 
168     
169     StringLiteralEntryHolder pEntry(SystemDomain::GetGlobalStringLiteralMap()->GetStringLiteral(pStringData, dwHash, bAddIfNotFound));
170
171     _ASSERTE(pEntry || !bAddIfNotFound);
172
173     // If pEntry is non-null then the entry exists in the Global map. (either we retrieved it or added it just now)
174     if (pEntry)
175     {
176         // If the entry exists in the Global map and the appdomain wont ever unload then we really don't need to add a
177         // hashentry in the appdomain specific map.
178         // TODO: except that by not inserting into our local table we always take the global map lock
179         // and come into this path, when we could succeed at a lock free lookup above.
180         
181         if (!bAppDomainWontUnload)
182         {                
183             // Make sure some other thread has not already added it.
184             if (!m_StringToEntryHashTable->GetValue(pStringData, &Data))
185             {
186                 // Insert the handle to the string into the hash table.
187                 m_StringToEntryHashTable->InsertValue(pStringData, (LPVOID)pEntry, FALSE);
188             }
189             else
190             {
191                 pEntry.Release(); //while we're still under lock
192             }
193         }
194 #ifdef _DEBUG
195         else
196         {
197             LOG((LF_APPDOMAIN, LL_INFO10000, "Avoided adding String literal to appdomain map: size: %d bytes\n", pStringData->GetCharCount()));
198         }
199 #endif
200         pEntry.SuppressRelease();
201         STRINGREF *pStrObj = NULL;
202         // Retrieve the string objectref from the string literal entry.
203         pStrObj = pEntry->GetStringObject();
204         _ASSERTE(!bAddIfNotFound || pStrObj);
205         return pStrObj;
206     }
207     // If the bAddIfNotFound flag is set then we better have a string
208     // string object at this point.
209     _ASSERTE(!bAddIfNotFound);
210     return NULL;
211 }
212
213 STRINGREF *StringLiteralMap::GetInternedString(STRINGREF *pString, BOOL bAddIfNotFound, BOOL bAppDomainWontUnload)
214 {
215     CONTRACTL
216     {
217         GC_TRIGGERS;
218         THROWS;
219         MODE_COOPERATIVE;
220         PRECONDITION(CheckPointer(this));
221         PRECONDITION(CheckPointer(pString));
222     }
223     CONTRACTL_END;
224
225     HashDatum Data;
226     EEStringData StringData = EEStringData((*pString)->GetStringLength(), (*pString)->GetBuffer());
227
228     DWORD dwHash = m_StringToEntryHashTable->GetHash(&StringData);
229     if (m_StringToEntryHashTable->GetValue(&StringData, &Data, dwHash))
230     {
231         STRINGREF *pStrObj = NULL;
232         pStrObj = ((StringLiteralEntry*)Data)->GetStringObject();
233         _ASSERTE(!bAddIfNotFound || pStrObj);
234         return pStrObj;
235
236     }
237     else
238     {
239         CrstHolder gch(&(SystemDomain::GetGlobalStringLiteralMap()->m_HashTableCrstGlobal));
240
241         // TODO: We can be more efficient by checking our local hash table now to see if
242         // someone beat us to inserting it. (m_StringToEntryHashTable->GetValue(pStringData, &Data))
243         // (Rather than waiting until after we look the string up in the global map) 
244         
245         // Retrieve the string literal from the global string literal map.
246         StringLiteralEntryHolder pEntry(SystemDomain::GetGlobalStringLiteralMap()->GetInternedString(pString, dwHash, bAddIfNotFound));
247
248         _ASSERTE(pEntry || !bAddIfNotFound);
249
250         // If pEntry is non-null then the entry exists in the Global map. (either we retrieved it or added it just now)
251         if (pEntry)
252         {
253             // If the entry exists in the Global map and the appdomain wont ever unload then we really don't need to add a
254             // hashentry in the appdomain specific map.
255             // TODO: except that by not inserting into our local table we always take the global map lock
256             // and come into this path, when we could succeed at a lock free lookup above.
257
258             if (!bAppDomainWontUnload)
259             {
260                 // Since GlobalStringLiteralMap::GetInternedString() could have caused a GC,
261                 // we need to recreate the string data.
262                 StringData = EEStringData((*pString)->GetStringLength(), (*pString)->GetBuffer());
263
264                 // Make sure some other thread has not already added it.
265                 if (!m_StringToEntryHashTable->GetValue(&StringData, &Data))
266                 {
267                     // Insert the handle to the string into the hash table.
268                     m_StringToEntryHashTable->InsertValue(&StringData, (LPVOID)pEntry, FALSE);
269                 }
270                 else
271                 {
272                     pEntry.Release(); // while we're under lock
273                 }
274             }
275             pEntry.SuppressRelease();
276             // Retrieve the string objectref from the string literal entry.
277             STRINGREF *pStrObj = NULL;
278             pStrObj = pEntry->GetStringObject();
279             return pStrObj;
280         }
281     }
282     // If the bAddIfNotFound flag is set then we better have a string
283     // string object at this point.
284     _ASSERTE(!bAddIfNotFound);
285
286     return NULL;
287 }
288
289 GlobalStringLiteralMap::GlobalStringLiteralMap()
290 : m_StringToEntryHashTable(NULL)
291 , m_MemoryPool(NULL)
292 , m_HashTableCrstGlobal(CrstGlobalStrLiteralMap)
293 , m_LargeHeapHandleTable(SystemDomain::System(), GLOBAL_STRING_TABLE_BUCKET_SIZE)
294 {
295     CONTRACTL
296     {
297         THROWS;
298         GC_TRIGGERS;
299     }
300     CONTRACTL_END;
301
302 #ifdef _DEBUG
303     m_LargeHeapHandleTable.RegisterCrstDebug(&m_HashTableCrstGlobal);
304 #endif
305 }
306
307 GlobalStringLiteralMap::~GlobalStringLiteralMap()
308 {
309     CONTRACTL
310     {
311         NOTHROW;
312         GC_TRIGGERS;
313     }
314     CONTRACTL_END;
315     
316     // if we are deleting the map then either it is shutdown time or else there was a race trying to create
317     // the initial map and this one was the loser 
318     // (i.e. two threads made a map and the InterlockedCompareExchange failed for one of them and 
319     // now it is deleting the map)
320     //
321     // if it's not the main map, then the map we are deleting better be empty!
322
323     // there must be *some* global table
324     _ASSERTE(SystemDomain::GetGlobalStringLiteralMapNoCreate()  != NULL);
325
326     if (SystemDomain::GetGlobalStringLiteralMapNoCreate() != this)
327     {
328         // if this isn't the real global table then it must be empty
329         _ASSERTE(m_StringToEntryHashTable->IsEmpty());  
330
331         // Delete the hash table first. The dtor of the hash table would clean up all the entries.
332         delete m_StringToEntryHashTable;
333         // Delete the pool later, since the dtor above would need it.
334         delete m_MemoryPool;        
335     }
336     else
337     {
338         // We are shutting down, the OS will reclaim the memory from the StringLiteralEntries,
339         // m_MemoryPool and m_StringToEntryHashTable.
340         _ASSERTE(g_fProcessDetach);
341     }        
342 }
343
344 void GlobalStringLiteralMap::Init()
345 {
346     CONTRACTL
347     {
348         THROWS;
349         GC_NOTRIGGER;
350         PRECONDITION(CheckPointer(this));
351         INJECT_FAULT(ThrowOutOfMemory());
352     } 
353     CONTRACTL_END;
354
355     // Allocate the memory pool and set the initial count to quarter as grow count
356     m_MemoryPool = new MemoryPool (SIZEOF_EEHASH_ENTRY, EEHASH_MEMORY_POOL_GROW_COUNT, EEHASH_MEMORY_POOL_GROW_COUNT/4);
357
358     m_StringToEntryHashTable =  new EEUnicodeStringLiteralHashTable ();
359
360     LockOwner lock = {&m_HashTableCrstGlobal, IsOwnerOfCrst};
361     if (!m_StringToEntryHashTable->Init(INIT_NUM_GLOBAL_STRING_BUCKETS, &lock, m_MemoryPool))
362         ThrowOutOfMemory();
363 }
364
365 StringLiteralEntry *GlobalStringLiteralMap::GetStringLiteral(EEStringData *pStringData, DWORD dwHash, BOOL bAddIfNotFound)
366 {
367     CONTRACTL
368     {
369         THROWS;
370         GC_TRIGGERS;
371         MODE_COOPERATIVE;
372         PRECONDITION(CheckPointer(this));
373         PRECONDITION(CheckPointer(pStringData));
374         PRECONDITION(m_HashTableCrstGlobal.OwnedByCurrentThread());        
375     }
376     CONTRACTL_END;
377
378     HashDatum Data;
379     StringLiteralEntry *pEntry = NULL;
380
381     if (m_StringToEntryHashTable->GetValueSpeculative(pStringData, &Data, dwHash)) // Since we hold the critical section here, we can safely use the speculative variant of GetValue
382     {
383         pEntry = (StringLiteralEntry*)Data;
384         // If the entry is already in the table then addref it before we return it.
385         if (pEntry)
386             pEntry->AddRef();
387     }
388     else
389     {
390         if (bAddIfNotFound)
391             pEntry = AddStringLiteral(pStringData);
392     }
393
394     return pEntry;
395 }
396
397 StringLiteralEntry *GlobalStringLiteralMap::GetInternedString(STRINGREF *pString, DWORD dwHash, BOOL bAddIfNotFound)
398 {
399     CONTRACTL
400     {
401         THROWS;
402         GC_TRIGGERS;
403         MODE_COOPERATIVE;
404         PRECONDITION(CheckPointer(this));
405         PRECONDITION(CheckPointer(pString));
406         PRECONDITION(m_HashTableCrstGlobal.OwnedByCurrentThread());
407     }
408     CONTRACTL_END;  
409
410     EEStringData StringData = EEStringData((*pString)->GetStringLength(), (*pString)->GetBuffer());
411
412     HashDatum Data;
413     StringLiteralEntry *pEntry = NULL;
414
415     if (m_StringToEntryHashTable->GetValue(&StringData, &Data, dwHash))
416     {
417         pEntry = (StringLiteralEntry*)Data;
418         // If the entry is already in the table then addref it before we return it.
419         if (pEntry)
420             pEntry->AddRef();
421     }
422     else
423     {
424         if (bAddIfNotFound)
425             pEntry = AddInternedString(pString);
426     }
427
428     return pEntry;
429 }
430
431 #ifdef LOGGING
432 static void LogStringLiteral(__in_z const char* action, EEStringData *pStringData)
433 {
434     STATIC_CONTRACT_NOTHROW;
435     STATIC_CONTRACT_GC_NOTRIGGER;
436     STATIC_CONTRACT_FORBID_FAULT;
437
438     int length = pStringData->GetCharCount();
439     length = min(length, 100);
440     WCHAR *szString = (WCHAR *)_alloca((length + 1) * sizeof(WCHAR));
441     memcpyNoGCRefs((void*)szString, (void*)pStringData->GetStringBuffer(), length * sizeof(WCHAR));
442     szString[length] = '\0';
443     LOG((LF_APPDOMAIN, LL_INFO10000, "String literal \"%S\" %s to Global map, size %d bytes\n", szString, action, pStringData->GetCharCount()));
444 }
445 #endif
446
447 STRINGREF AllocateStringObject(EEStringData *pStringData)
448 {
449     CONTRACTL
450     {
451         THROWS;
452         GC_TRIGGERS;
453         MODE_COOPERATIVE;  
454     }
455     CONTRACTL_END;  
456    
457     // Create the COM+ string object.
458     DWORD cCount = pStringData->GetCharCount();
459     
460     STRINGREF strObj = AllocateString(cCount);
461
462     GCPROTECT_BEGIN(strObj)
463     {
464         // Copy the string constant into the COM+ string object.  The code
465         // will add an extra null at the end for safety purposes, but since
466         // we support embedded nulls, one should never treat the string as
467         // null termianted.
468         LPWSTR strDest = strObj->GetBuffer();
469         memcpyNoGCRefs(strDest, pStringData->GetStringBuffer(), cCount*sizeof(WCHAR));
470         strDest[cCount] = 0;
471     }
472     GCPROTECT_END();
473
474     return strObj;
475 }
476 StringLiteralEntry *GlobalStringLiteralMap::AddStringLiteral(EEStringData *pStringData)
477 {
478     CONTRACTL
479     {
480         THROWS;
481         GC_TRIGGERS;
482         MODE_COOPERATIVE;
483         PRECONDITION(CheckPointer(this));
484         PRECONDITION(m_HashTableCrstGlobal.OwnedByCurrentThread());        
485     }
486     CONTRACTL_END;  
487
488     StringLiteralEntry *pRet;
489
490     {
491     LargeHeapHandleBlockHolder pStrObj(&m_LargeHeapHandleTable,1);
492     // Create the COM+ string object.
493     STRINGREF strObj = AllocateStringObject(pStringData);
494                 
495     // Allocate a handle for the string.
496     SetObjectReference(pStrObj[0], (OBJECTREF) strObj);
497    
498
499     // Allocate the StringLiteralEntry.
500     StringLiteralEntryHolder pEntry(StringLiteralEntry::AllocateEntry(pStringData, (STRINGREF*)pStrObj[0]));
501     pStrObj.SuppressRelease();
502     // Insert the handle to the string into the hash table.
503     m_StringToEntryHashTable->InsertValue(pStringData, (LPVOID)pEntry, FALSE);
504     pEntry.SuppressRelease();
505     pRet = pEntry;
506
507 #ifdef LOGGING
508     LogStringLiteral("added", pStringData);
509 #endif
510     }
511
512     return pRet;
513 }
514
515 StringLiteralEntry *GlobalStringLiteralMap::AddInternedString(STRINGREF *pString)
516 {
517  
518     CONTRACTL
519     {
520         THROWS;
521         GC_TRIGGERS;
522         MODE_COOPERATIVE;
523         PRECONDITION(CheckPointer(this));
524         PRECONDITION(m_HashTableCrstGlobal.OwnedByCurrentThread());        
525     }
526     CONTRACTL_END;  
527
528     EEStringData StringData = EEStringData((*pString)->GetStringLength(), (*pString)->GetBuffer());    
529     StringLiteralEntry *pRet;
530
531     {
532     LargeHeapHandleBlockHolder pStrObj(&m_LargeHeapHandleTable,1);
533     SetObjectReference(pStrObj[0], (OBJECTREF) *pString);
534
535     // Since the allocation might have caused a GC we need to re-get the
536     // string data.
537     StringData = EEStringData((*pString)->GetStringLength(), (*pString)->GetBuffer());
538
539     StringLiteralEntryHolder pEntry(StringLiteralEntry::AllocateEntry(&StringData, (STRINGREF*)pStrObj[0]));
540     pStrObj.SuppressRelease();
541
542     // Insert the handle to the string into the hash table.
543     m_StringToEntryHashTable->InsertValue(&StringData, (LPVOID)pEntry, FALSE);
544     pEntry.SuppressRelease();
545     pRet = pEntry;
546     }
547
548     return pRet;
549 }
550
551 void GlobalStringLiteralMap::RemoveStringLiteralEntry(StringLiteralEntry *pEntry)
552 {
553    CONTRACTL
554     {
555         NOTHROW;
556         GC_NOTRIGGER;
557         PRECONDITION(CheckPointer(pEntry)); 
558         PRECONDITION(m_HashTableCrstGlobal.OwnedByCurrentThread());
559         PRECONDITION(CheckPointer(this));
560     }
561     CONTRACTL_END;      
562
563     // Remove the entry from the hash table.
564     {
565         GCX_COOP();
566
567         EEStringData StringData;    
568         pEntry->GetStringData(&StringData);
569
570         BOOL bSuccess;
571         bSuccess = m_StringToEntryHashTable->DeleteValue(&StringData);
572         // this assert is comented out to accomodate case when StringLiteralEntryHolder 
573         // releases this object after failed insertion into hash
574         //_ASSERTE(bSuccess);
575
576 #ifdef LOGGING
577         // We need to do this logging within the GCX_COOP(), as a gc will render
578         // our StringData pointers stale.
579         if (bSuccess)
580         {
581             LogStringLiteral("removed", &StringData);
582         }
583 #endif
584
585         // Release the object handle that the entry was using.
586         STRINGREF *pObjRef = pEntry->GetStringObject();
587         m_LargeHeapHandleTable.ReleaseHandles((OBJECTREF*)pObjRef, 1);
588     }
589
590     // We do not delete the StringLiteralEntry itself that will be done in the
591     // release method of the StringLiteralEntry.
592 }
593
594 StringLiteralEntry *StringLiteralEntry::AllocateEntry(EEStringData *pStringData, STRINGREF *pStringObj)
595 {
596    CONTRACTL
597     {
598         THROWS;
599         GC_TRIGGERS; // GC_TRIGGERS because in the precondition below GetGlobalStringLiteralMap() might need to create the map
600         MODE_COOPERATIVE;
601         PRECONDITION(SystemDomain::GetGlobalStringLiteralMap()->m_HashTableCrstGlobal.OwnedByCurrentThread());
602     }
603     CONTRACTL_END; 
604
605     // Note: we don't synchronize here because allocateEntry is called when HashCrst is held.
606     void *pMem = NULL;
607     if (s_FreeEntryList != NULL)
608     {
609         pMem = s_FreeEntryList;
610         s_FreeEntryList = s_FreeEntryList->m_pNext;
611         _ASSERTE (((StringLiteralEntry*)pMem)->m_bDeleted);        
612     }
613     else
614     {
615         if (s_EntryList == NULL || (s_UsedEntries >= MAX_ENTRIES_PER_CHUNK))
616         {
617             StringLiteralEntryArray *pNew = new StringLiteralEntryArray();
618             pNew->m_pNext = s_EntryList;
619             s_EntryList = pNew;
620             s_UsedEntries = 0;
621         }
622         pMem = &(s_EntryList->m_Entries[s_UsedEntries++*sizeof(StringLiteralEntry)]);
623     }
624     _ASSERTE (pMem && "Unable to allocate String literal Entry");
625
626     return new (pMem) StringLiteralEntry (pStringData, pStringObj);
627 }
628
629 void StringLiteralEntry::DeleteEntry (StringLiteralEntry *pEntry)
630 {
631     CONTRACTL
632     {
633         NOTHROW;
634         GC_NOTRIGGER;
635         PRECONDITION(SystemDomain::GetGlobalStringLiteralMapNoCreate()->m_HashTableCrstGlobal.OwnedByCurrentThread());
636     }
637     CONTRACTL_END; 
638
639     _ASSERTE (VolatileLoad(&pEntry->m_dwRefCount) == 0);
640     
641 #ifdef _DEBUG
642     memset (pEntry, 0xc, sizeof(StringLiteralEntry));
643 #endif
644
645 #ifdef _DEBUG        
646     pEntry->m_bDeleted = TRUE;
647 #endif
648     
649     // The free list needs protection from the m_HashTableCrstGlobal
650     pEntry->m_pNext = s_FreeEntryList;
651     s_FreeEntryList = pEntry;
652 }
653
654
655