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
52StringLiteralEntryArray *StringLiteralEntry::s_EntryList = NULL;
53DWORD StringLiteralEntry::s_UsedEntries = NULL;
54StringLiteralEntry *StringLiteralEntry::s_FreeEntryList = NULL;
55
56StringLiteralMap::StringLiteralMap()
57: m_StringToEntryHashTable(NULL)
58, m_MemoryPool(NULL)
59{
60 CONTRACTL
61 {
62 THROWS;
63 GC_NOTRIGGER;
64 }
65 CONTRACTL_END;
66}
67
68void 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
89StringLiteralMap::~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
147STRINGREF *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 if (m_StringToEntryHashTable->GetValue(pStringData, &Data, dwHash))
163 {
164 STRINGREF *pStrObj = NULL;
165 pStrObj = ((StringLiteralEntry*)Data)->GetStringObject();
166 _ASSERTE(!bAddIfNotFound || pStrObj);
167 return pStrObj;
168
169 }
170 else
171 {
172 // Retrieve the string literal from the global string literal map.
173 CrstHolder gch(&(SystemDomain::GetGlobalStringLiteralMap()->m_HashTableCrstGlobal));
174
175 // TODO: We can be more efficient by checking our local hash table now to see if
176 // someone beat us to inserting it. (m_StringToEntryHashTable->GetValue(pStringData, &Data))
177 // (Rather than waiting until after we look the string up in the global map)
178
179 StringLiteralEntryHolder pEntry(SystemDomain::GetGlobalStringLiteralMap()->GetStringLiteral(pStringData, dwHash, bAddIfNotFound));
180
181 _ASSERTE(pEntry || !bAddIfNotFound);
182
183 // If pEntry is non-null then the entry exists in the Global map. (either we retrieved it or added it just now)
184 if (pEntry)
185 {
186 // If the entry exists in the Global map and the appdomain wont ever unload then we really don't need to add a
187 // hashentry in the appdomain specific map.
188 // TODO: except that by not inserting into our local table we always take the global map lock
189 // and come into this path, when we could succeed at a lock free lookup above.
190
191 if (!bAppDomainWontUnload)
192 {
193 // Make sure some other thread has not already added it.
194 if (!m_StringToEntryHashTable->GetValue(pStringData, &Data))
195 {
196 // Insert the handle to the string into the hash table.
197 m_StringToEntryHashTable->InsertValue(pStringData, (LPVOID)pEntry, FALSE);
198 }
199 else
200 {
201 pEntry.Release(); //while we're still under lock
202 }
203 }
204#ifdef _DEBUG
205 else
206 {
207 LOG((LF_APPDOMAIN, LL_INFO10000, "Avoided adding String literal to appdomain map: size: %d bytes\n", pStringData->GetCharCount()));
208 }
209#endif
210 pEntry.SuppressRelease();
211 STRINGREF *pStrObj = NULL;
212 // Retrieve the string objectref from the string literal entry.
213 pStrObj = pEntry->GetStringObject();
214 _ASSERTE(!bAddIfNotFound || pStrObj);
215 return pStrObj;
216 }
217 }
218 // If the bAddIfNotFound flag is set then we better have a string
219 // string object at this point.
220 _ASSERTE(!bAddIfNotFound);
221 return NULL;
222}
223
224STRINGREF *StringLiteralMap::GetInternedString(STRINGREF *pString, BOOL bAddIfNotFound, BOOL bAppDomainWontUnload)
225{
226 CONTRACTL
227 {
228 GC_TRIGGERS;
229 THROWS;
230 MODE_COOPERATIVE;
231 PRECONDITION(CheckPointer(this));
232 PRECONDITION(CheckPointer(pString));
233 }
234 CONTRACTL_END;
235
236 HashDatum Data;
237 EEStringData StringData = EEStringData((*pString)->GetStringLength(), (*pString)->GetBuffer());
238
239 DWORD dwHash = m_StringToEntryHashTable->GetHash(&StringData);
240 if (m_StringToEntryHashTable->GetValue(&StringData, &Data, dwHash))
241 {
242 STRINGREF *pStrObj = NULL;
243 pStrObj = ((StringLiteralEntry*)Data)->GetStringObject();
244 _ASSERTE(!bAddIfNotFound || pStrObj);
245 return pStrObj;
246
247 }
248 else
249 {
250 CrstHolder gch(&(SystemDomain::GetGlobalStringLiteralMap()->m_HashTableCrstGlobal));
251
252 // TODO: We can be more efficient by checking our local hash table now to see if
253 // someone beat us to inserting it. (m_StringToEntryHashTable->GetValue(pStringData, &Data))
254 // (Rather than waiting until after we look the string up in the global map)
255
256 // Retrieve the string literal from the global string literal map.
257 StringLiteralEntryHolder pEntry(SystemDomain::GetGlobalStringLiteralMap()->GetInternedString(pString, dwHash, bAddIfNotFound));
258
259 _ASSERTE(pEntry || !bAddIfNotFound);
260
261 // If pEntry is non-null then the entry exists in the Global map. (either we retrieved it or added it just now)
262 if (pEntry)
263 {
264 // If the entry exists in the Global map and the appdomain wont ever unload then we really don't need to add a
265 // hashentry in the appdomain specific map.
266 // TODO: except that by not inserting into our local table we always take the global map lock
267 // and come into this path, when we could succeed at a lock free lookup above.
268
269 if (!bAppDomainWontUnload)
270 {
271 // Since GlobalStringLiteralMap::GetInternedString() could have caused a GC,
272 // we need to recreate the string data.
273 StringData = EEStringData((*pString)->GetStringLength(), (*pString)->GetBuffer());
274
275 // Make sure some other thread has not already added it.
276 if (!m_StringToEntryHashTable->GetValue(&StringData, &Data))
277 {
278 // Insert the handle to the string into the hash table.
279 m_StringToEntryHashTable->InsertValue(&StringData, (LPVOID)pEntry, FALSE);
280 }
281 else
282 {
283 pEntry.Release(); // while we're under lock
284 }
285 }
286 pEntry.SuppressRelease();
287 // Retrieve the string objectref from the string literal entry.
288 STRINGREF *pStrObj = NULL;
289 pStrObj = pEntry->GetStringObject();
290 return pStrObj;
291 }
292 }
293 // If the bAddIfNotFound flag is set then we better have a string
294 // string object at this point.
295 _ASSERTE(!bAddIfNotFound);
296
297 return NULL;
298}
299
300GlobalStringLiteralMap::GlobalStringLiteralMap()
301: m_StringToEntryHashTable(NULL)
302, m_MemoryPool(NULL)
303, m_HashTableCrstGlobal(CrstGlobalStrLiteralMap)
304, m_LargeHeapHandleTable(SystemDomain::System(), GLOBAL_STRING_TABLE_BUCKET_SIZE)
305{
306 CONTRACTL
307 {
308 THROWS;
309 GC_TRIGGERS;
310 }
311 CONTRACTL_END;
312
313#ifdef _DEBUG
314 m_LargeHeapHandleTable.RegisterCrstDebug(&m_HashTableCrstGlobal);
315#endif
316}
317
318GlobalStringLiteralMap::~GlobalStringLiteralMap()
319{
320 CONTRACTL
321 {
322 NOTHROW;
323 GC_TRIGGERS;
324 }
325 CONTRACTL_END;
326
327 // if we are deleting the map then either it is shutdown time or else there was a race trying to create
328 // the initial map and this one was the loser
329 // (i.e. two threads made a map and the InterlockedCompareExchange failed for one of them and
330 // now it is deleting the map)
331 //
332 // if it's not the main map, then the map we are deleting better be empty!
333
334 // there must be *some* global table
335 _ASSERTE(SystemDomain::GetGlobalStringLiteralMapNoCreate() != NULL);
336
337 if (SystemDomain::GetGlobalStringLiteralMapNoCreate() != this)
338 {
339 // if this isn't the real global table then it must be empty
340 _ASSERTE(m_StringToEntryHashTable->IsEmpty());
341
342 // Delete the hash table first. The dtor of the hash table would clean up all the entries.
343 delete m_StringToEntryHashTable;
344 // Delete the pool later, since the dtor above would need it.
345 delete m_MemoryPool;
346 }
347 else
348 {
349 // We are shutting down, the OS will reclaim the memory from the StringLiteralEntries,
350 // m_MemoryPool and m_StringToEntryHashTable.
351 _ASSERTE(g_fProcessDetach);
352 }
353}
354
355void GlobalStringLiteralMap::Init()
356{
357 CONTRACTL
358 {
359 THROWS;
360 GC_NOTRIGGER;
361 PRECONDITION(CheckPointer(this));
362 INJECT_FAULT(ThrowOutOfMemory());
363 }
364 CONTRACTL_END;
365
366 // Allocate the memory pool and set the initial count to quarter as grow count
367 m_MemoryPool = new MemoryPool (SIZEOF_EEHASH_ENTRY, EEHASH_MEMORY_POOL_GROW_COUNT, EEHASH_MEMORY_POOL_GROW_COUNT/4);
368
369 m_StringToEntryHashTable = new EEUnicodeStringLiteralHashTable ();
370
371 LockOwner lock = {&m_HashTableCrstGlobal, IsOwnerOfCrst};
372 if (!m_StringToEntryHashTable->Init(INIT_NUM_GLOBAL_STRING_BUCKETS, &lock, m_MemoryPool))
373 ThrowOutOfMemory();
374}
375
376StringLiteralEntry *GlobalStringLiteralMap::GetStringLiteral(EEStringData *pStringData, DWORD dwHash, BOOL bAddIfNotFound)
377{
378 CONTRACTL
379 {
380 THROWS;
381 GC_TRIGGERS;
382 MODE_COOPERATIVE;
383 PRECONDITION(CheckPointer(this));
384 PRECONDITION(CheckPointer(pStringData));
385 PRECONDITION(m_HashTableCrstGlobal.OwnedByCurrentThread());
386 }
387 CONTRACTL_END;
388
389 HashDatum Data;
390 StringLiteralEntry *pEntry = NULL;
391
392 if (m_StringToEntryHashTable->GetValue(pStringData, &Data, dwHash))
393 {
394 pEntry = (StringLiteralEntry*)Data;
395 // If the entry is already in the table then addref it before we return it.
396 if (pEntry)
397 pEntry->AddRef();
398 }
399 else
400 {
401 if (bAddIfNotFound)
402 pEntry = AddStringLiteral(pStringData);
403 }
404
405 return pEntry;
406}
407
408StringLiteralEntry *GlobalStringLiteralMap::GetInternedString(STRINGREF *pString, DWORD dwHash, BOOL bAddIfNotFound)
409{
410 CONTRACTL
411 {
412 THROWS;
413 GC_TRIGGERS;
414 MODE_COOPERATIVE;
415 PRECONDITION(CheckPointer(this));
416 PRECONDITION(CheckPointer(pString));
417 PRECONDITION(m_HashTableCrstGlobal.OwnedByCurrentThread());
418 }
419 CONTRACTL_END;
420
421 EEStringData StringData = EEStringData((*pString)->GetStringLength(), (*pString)->GetBuffer());
422
423 HashDatum Data;
424 StringLiteralEntry *pEntry = NULL;
425
426 if (m_StringToEntryHashTable->GetValue(&StringData, &Data, dwHash))
427 {
428 pEntry = (StringLiteralEntry*)Data;
429 // If the entry is already in the table then addref it before we return it.
430 if (pEntry)
431 pEntry->AddRef();
432 }
433 else
434 {
435 if (bAddIfNotFound)
436 pEntry = AddInternedString(pString);
437 }
438
439 return pEntry;
440}
441
442#ifdef LOGGING
443static void LogStringLiteral(__in_z const char* action, EEStringData *pStringData)
444{
445 STATIC_CONTRACT_NOTHROW;
446 STATIC_CONTRACT_GC_NOTRIGGER;
447 STATIC_CONTRACT_FORBID_FAULT;
448
449 int length = pStringData->GetCharCount();
450 length = min(length, 100);
451 WCHAR *szString = (WCHAR *)_alloca((length + 1) * sizeof(WCHAR));
452 memcpyNoGCRefs((void*)szString, (void*)pStringData->GetStringBuffer(), length * sizeof(WCHAR));
453 szString[length] = '\0';
454 LOG((LF_APPDOMAIN, LL_INFO10000, "String literal \"%S\" %s to Global map, size %d bytes\n", szString, action, pStringData->GetCharCount()));
455}
456#endif
457
458STRINGREF AllocateStringObject(EEStringData *pStringData)
459{
460 CONTRACTL
461 {
462 THROWS;
463 GC_TRIGGERS;
464 MODE_COOPERATIVE;
465 }
466 CONTRACTL_END;
467
468 // Create the COM+ string object.
469 DWORD cCount = pStringData->GetCharCount();
470
471 STRINGREF strObj = AllocateString(cCount);
472
473 GCPROTECT_BEGIN(strObj)
474 {
475 // Copy the string constant into the COM+ string object. The code
476 // will add an extra null at the end for safety purposes, but since
477 // we support embedded nulls, one should never treat the string as
478 // null termianted.
479 LPWSTR strDest = strObj->GetBuffer();
480 memcpyNoGCRefs(strDest, pStringData->GetStringBuffer(), cCount*sizeof(WCHAR));
481 strDest[cCount] = 0;
482 }
483 GCPROTECT_END();
484
485 return strObj;
486}
487StringLiteralEntry *GlobalStringLiteralMap::AddStringLiteral(EEStringData *pStringData)
488{
489 CONTRACTL
490 {
491 THROWS;
492 GC_TRIGGERS;
493 MODE_COOPERATIVE;
494 PRECONDITION(CheckPointer(this));
495 PRECONDITION(m_HashTableCrstGlobal.OwnedByCurrentThread());
496 }
497 CONTRACTL_END;
498
499 StringLiteralEntry *pRet;
500
501 {
502 LargeHeapHandleBlockHolder pStrObj(&m_LargeHeapHandleTable,1);
503 // Create the COM+ string object.
504 STRINGREF strObj = AllocateStringObject(pStringData);
505
506 // Allocate a handle for the string.
507 SetObjectReference(pStrObj[0], (OBJECTREF) strObj, NULL);
508
509
510 // Allocate the StringLiteralEntry.
511 StringLiteralEntryHolder pEntry(StringLiteralEntry::AllocateEntry(pStringData, (STRINGREF*)pStrObj[0]));
512 pStrObj.SuppressRelease();
513 // Insert the handle to the string into the hash table.
514 m_StringToEntryHashTable->InsertValue(pStringData, (LPVOID)pEntry, FALSE);
515 pEntry.SuppressRelease();
516 pRet = pEntry;
517
518#ifdef LOGGING
519 LogStringLiteral("added", pStringData);
520#endif
521 }
522
523 return pRet;
524}
525
526StringLiteralEntry *GlobalStringLiteralMap::AddInternedString(STRINGREF *pString)
527{
528
529 CONTRACTL
530 {
531 THROWS;
532 GC_TRIGGERS;
533 MODE_COOPERATIVE;
534 PRECONDITION(CheckPointer(this));
535 PRECONDITION(m_HashTableCrstGlobal.OwnedByCurrentThread());
536 }
537 CONTRACTL_END;
538
539 EEStringData StringData = EEStringData((*pString)->GetStringLength(), (*pString)->GetBuffer());
540 StringLiteralEntry *pRet;
541
542 {
543 LargeHeapHandleBlockHolder pStrObj(&m_LargeHeapHandleTable,1);
544 SetObjectReference(pStrObj[0], (OBJECTREF) *pString, NULL);
545
546 // Since the allocation might have caused a GC we need to re-get the
547 // string data.
548 StringData = EEStringData((*pString)->GetStringLength(), (*pString)->GetBuffer());
549
550 StringLiteralEntryHolder pEntry(StringLiteralEntry::AllocateEntry(&StringData, (STRINGREF*)pStrObj[0]));
551 pStrObj.SuppressRelease();
552
553 // Insert the handle to the string into the hash table.
554 m_StringToEntryHashTable->InsertValue(&StringData, (LPVOID)pEntry, FALSE);
555 pEntry.SuppressRelease();
556 pRet = pEntry;
557 }
558
559 return pRet;
560}
561
562void GlobalStringLiteralMap::RemoveStringLiteralEntry(StringLiteralEntry *pEntry)
563{
564 CONTRACTL
565 {
566 NOTHROW;
567 GC_NOTRIGGER;
568 PRECONDITION(CheckPointer(pEntry));
569 PRECONDITION(m_HashTableCrstGlobal.OwnedByCurrentThread());
570 PRECONDITION(CheckPointer(this));
571 }
572 CONTRACTL_END;
573
574 // Remove the entry from the hash table.
575 {
576 GCX_COOP();
577
578 EEStringData StringData;
579 pEntry->GetStringData(&StringData);
580
581 BOOL bSuccess;
582 bSuccess = m_StringToEntryHashTable->DeleteValue(&StringData);
583 // this assert is comented out to accomodate case when StringLiteralEntryHolder
584 // releases this object after failed insertion into hash
585 //_ASSERTE(bSuccess);
586
587#ifdef LOGGING
588 // We need to do this logging within the GCX_COOP(), as a gc will render
589 // our StringData pointers stale.
590 if (bSuccess)
591 {
592 LogStringLiteral("removed", &StringData);
593 }
594#endif
595
596 // Release the object handle that the entry was using.
597 STRINGREF *pObjRef = pEntry->GetStringObject();
598 m_LargeHeapHandleTable.ReleaseHandles((OBJECTREF*)pObjRef, 1);
599 }
600
601 // We do not delete the StringLiteralEntry itself that will be done in the
602 // release method of the StringLiteralEntry.
603}
604
605StringLiteralEntry *StringLiteralEntry::AllocateEntry(EEStringData *pStringData, STRINGREF *pStringObj)
606{
607 CONTRACTL
608 {
609 THROWS;
610 GC_TRIGGERS; // GC_TRIGGERS because in the precondition below GetGlobalStringLiteralMap() might need to create the map
611 MODE_COOPERATIVE;
612 PRECONDITION(SystemDomain::GetGlobalStringLiteralMap()->m_HashTableCrstGlobal.OwnedByCurrentThread());
613 }
614 CONTRACTL_END;
615
616 // Note: we don't synchronize here because allocateEntry is called when HashCrst is held.
617 void *pMem = NULL;
618 if (s_FreeEntryList != NULL)
619 {
620 pMem = s_FreeEntryList;
621 s_FreeEntryList = s_FreeEntryList->m_pNext;
622 _ASSERTE (((StringLiteralEntry*)pMem)->m_bDeleted);
623 }
624 else
625 {
626 if (s_EntryList == NULL || (s_UsedEntries >= MAX_ENTRIES_PER_CHUNK))
627 {
628 StringLiteralEntryArray *pNew = new StringLiteralEntryArray();
629 pNew->m_pNext = s_EntryList;
630 s_EntryList = pNew;
631 s_UsedEntries = 0;
632 }
633 pMem = &(s_EntryList->m_Entries[s_UsedEntries++*sizeof(StringLiteralEntry)]);
634 }
635 _ASSERTE (pMem && "Unable to allocate String literal Entry");
636
637 return new (pMem) StringLiteralEntry (pStringData, pStringObj);
638}
639
640void StringLiteralEntry::DeleteEntry (StringLiteralEntry *pEntry)
641{
642 CONTRACTL
643 {
644 NOTHROW;
645 GC_NOTRIGGER;
646 PRECONDITION(SystemDomain::GetGlobalStringLiteralMapNoCreate()->m_HashTableCrstGlobal.OwnedByCurrentThread());
647 }
648 CONTRACTL_END;
649
650 _ASSERTE (VolatileLoad(&pEntry->m_dwRefCount) == 0);
651
652#ifdef _DEBUG
653 memset (pEntry, 0xc, sizeof(StringLiteralEntry));
654#endif
655
656#ifdef _DEBUG
657 pEntry->m_bDeleted = TRUE;
658#endif
659
660 // The free list needs protection from the m_HashTableCrstGlobal
661 pEntry->m_pNext = s_FreeEntryList;
662 s_FreeEntryList = pEntry;
663}
664
665
666
667