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.
14 #include "threadstatics.h"
18 #ifndef DACCESS_COMPILE
20 void ThreadLocalBlock::FreeTLM(SIZE_T i)
30 _ASSERTE(m_pTLMTable != NULL);
32 PTR_ThreadLocalModule pThreadLocalModule = m_pTLMTable[i].pTLM;
33 m_pTLMTable[i].pTLM = NULL;
35 if (pThreadLocalModule != NULL)
37 if (pThreadLocalModule->m_pDynamicClassTable != NULL)
39 for (DWORD k = 0; k < pThreadLocalModule->m_aDynamicEntries; ++k)
41 if (pThreadLocalModule->m_pDynamicClassTable[k].m_pDynamicEntry != NULL)
43 delete pThreadLocalModule->m_pDynamicClassTable[k].m_pDynamicEntry;
44 pThreadLocalModule->m_pDynamicClassTable[k].m_pDynamicEntry = NULL;
47 delete pThreadLocalModule->m_pDynamicClassTable;
48 pThreadLocalModule->m_pDynamicClassTable = NULL;
51 delete pThreadLocalModule;
55 void ThreadLocalBlock::FreeTable()
66 if (m_pTLMTable != NULL)
68 // Iterate over the table and free each TLM
69 for (SIZE_T i = 0; i < m_TLMTableSize; ++i)
71 if (m_pTLMTable[i].pTLM != NULL)
77 // Free the table itself
82 // Set table size to zero
85 // Free the ThreadStaticHandleTable
86 if (m_pThreadStaticHandleTable != NULL)
88 delete m_pThreadStaticHandleTable;
89 m_pThreadStaticHandleTable = NULL;
92 // Free any pinning handles we may have created
96 void ThreadLocalBlock::EnsureModuleIndex(ModuleIndex index)
103 if (m_TLMTableSize > index.m_dwIndex)
105 _ASSERTE(m_pTLMTable != NULL);
109 SIZE_T aModuleIndices = max(16, m_TLMTableSize);
110 while (aModuleIndices <= index.m_dwIndex)
115 // If this allocation fails, we will throw. If it succeeds,
116 // then we are good to go
117 PTR_TLMTableEntry pNewModuleSlots = (PTR_TLMTableEntry) (void*) new BYTE[sizeof(TLMTableEntry) * aModuleIndices];
119 // Zero out the new TLM table
120 memset(pNewModuleSlots, 0 , sizeof(TLMTableEntry) * aModuleIndices);
122 if (m_pTLMTable != NULL)
124 memcpy(pNewModuleSlots, m_pTLMTable, sizeof(TLMTableEntry) * m_TLMTableSize);
128 _ASSERTE(m_TLMTableSize == 0);
131 PTR_TLMTableEntry pOldModuleSlots = m_pTLMTable;
133 m_pTLMTable = pNewModuleSlots;
134 m_TLMTableSize = aModuleIndices;
136 if (pOldModuleSlots != NULL)
137 delete pOldModuleSlots;
142 void ThreadLocalBlock::SetModuleSlot(ModuleIndex index, PTR_ThreadLocalModule pLocalModule)
149 // This method will not grow the table. You need to grow
150 // the table explicitly before calling SetModuleSlot()
152 _ASSERTE(index.m_dwIndex < m_TLMTableSize);
154 m_pTLMTable[index.m_dwIndex].pTLM = pLocalModule;
157 #ifdef DACCESS_COMPILE
160 ThreadLocalModule::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
164 // Enumerate the ThreadLocalModule itself. TLMs are allocated to be larger than
165 // sizeof(ThreadLocalModule) to make room for ClassInit flags and non-GC statics.
166 // "DAC_ENUM_DTHIS()" probably does not account for this, so we might not enumerate
167 // all of the ClassInit flags and non-GC statics.
170 if (m_pDynamicClassTable != NULL)
172 DacEnumMemoryRegion(dac_cast<TADDR>(m_pDynamicClassTable),
173 m_aDynamicEntries * sizeof(DynamicClassInfo));
175 for (SIZE_T i = 0; i < m_aDynamicEntries; i++)
177 PTR_DynamicEntry entry = dac_cast<PTR_DynamicEntry>(m_pDynamicClassTable[i].m_pDynamicEntry);
187 ThreadLocalBlock::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
191 // Enumerate the ThreadLocalBlock itself
194 if (m_pTLMTable.IsValid())
196 DacEnumMemoryRegion(dac_cast<TADDR>(m_pTLMTable),
197 m_TLMTableSize * sizeof(TADDR));
199 for (SIZE_T i = 0; i < m_TLMTableSize; i++)
201 PTR_ThreadLocalModule domMod = m_pTLMTable[i].pTLM;
202 if (domMod.IsValid())
204 domMod->EnumMemoryRegions(flags);
212 DWORD ThreadLocalModule::GetClassFlags(MethodTable* pMT, DWORD iClassIndex) // iClassIndex defaults to (DWORD)-1
220 if (pMT->IsDynamicStatics())
222 DWORD dynamicClassID = pMT->GetModuleDynamicEntryID();
223 if(m_aDynamicEntries <= dynamicClassID)
225 return (m_pDynamicClassTable[dynamicClassID].m_dwFlags);
229 if (iClassIndex == (DWORD)-1)
230 iClassIndex = pMT->GetClassIndex();
231 return GetPrecomputedStaticsClassData()[iClassIndex];
235 #ifndef DACCESS_COMPILE
237 void ThreadLocalModule::SetClassFlags(MethodTable* pMT, DWORD dwFlags)
244 if (pMT->IsDynamicStatics())
246 DWORD dwID = pMT->GetModuleDynamicEntryID();
247 EnsureDynamicClassIndex(dwID);
248 m_pDynamicClassTable[dwID].m_dwFlags |= dwFlags;
252 GetPrecomputedStaticsClassData()[pMT->GetClassIndex()] |= dwFlags;
256 void ThreadLocalBlock::AddPinningHandleToList(OBJECTHANDLE oh)
266 ObjectHandleList::NodeType* pNewNode = new ObjectHandleList::NodeType(oh);
267 m_PinningHandleList.LinkHead(pNewNode);
270 void ThreadLocalBlock::FreePinningHandles()
280 // Destroy all pinning handles in the list, and free the nodes
281 ObjectHandleList::NodeType* pHandleNode;
282 while ((pHandleNode = m_PinningHandleList.UnlinkHead()) != NULL)
284 DestroyPinningHandle(pHandleNode->data);
289 void ThreadLocalBlock::AllocateThreadStaticHandles(Module * pModule, PTR_ThreadLocalModule pThreadLocalModule)
298 _ASSERTE(pThreadLocalModule->GetPrecomputedGCStaticsBaseHandleAddress() != NULL);
299 _ASSERTE(pThreadLocalModule->GetPrecomputedGCStaticsBaseHandle() == NULL);
301 if (pModule->GetNumGCThreadStaticHandles() > 0)
303 AllocateStaticFieldObjRefPtrs(pModule->GetNumGCThreadStaticHandles(),
304 pThreadLocalModule->GetPrecomputedGCStaticsBaseHandleAddress());
306 // We should throw if we fail to allocate and never hit this assert
307 _ASSERTE(pThreadLocalModule->GetPrecomputedGCStaticsBaseHandle() != NULL);
308 _ASSERTE(pThreadLocalModule->GetPrecomputedGCStaticsBasePointer() != NULL);
312 OBJECTHANDLE ThreadLocalBlock::AllocateStaticFieldObjRefPtrs(int nRequested, OBJECTHANDLE * ppLazyAllocate)
319 PRECONDITION((nRequested > 0));
320 INJECT_FAULT(COMPlusThrowOM(););
324 if (ppLazyAllocate && *ppLazyAllocate)
326 // Allocation already happened
327 return *ppLazyAllocate;
330 // Make sure the large heap handle table is initialized.
331 if (!m_pThreadStaticHandleTable)
332 InitThreadStaticHandleTable();
334 // Allocate the handles.
335 OBJECTHANDLE result = m_pThreadStaticHandleTable->AllocateHandles(nRequested);
339 *ppLazyAllocate = result;
345 void ThreadLocalBlock::InitThreadStaticHandleTable()
352 PRECONDITION(m_pThreadStaticHandleTable==NULL);
353 INJECT_FAULT(COMPlusThrowOM(););
357 // If the allocation fails this will throw; callers need
358 // to account for this possibility
359 m_pThreadStaticHandleTable = new ThreadStaticHandleTable(GetAppDomain());
362 void ThreadLocalBlock::AllocateThreadStaticBoxes(MethodTable * pMT)
369 PRECONDITION(pMT->GetNumBoxedThreadStatics() > 0);
370 INJECT_FAULT(COMPlusThrowOM(););
374 FieldDesc *pField = pMT->HasGenericsStaticsInfo() ?
375 pMT->GetGenericsStaticFieldDescs() : (pMT->GetApproxFieldDescListRaw() + pMT->GetNumIntroducedInstanceFields());
377 // Move pField to point to the list of thread statics
378 pField += pMT->GetNumStaticFields() - pMT->GetNumThreadStaticFields();
380 FieldDesc *pFieldEnd = pField + pMT->GetNumThreadStaticFields();
382 while (pField < pFieldEnd)
384 _ASSERTE(pField->IsThreadStatic());
386 // We only care about thread statics which are value types
387 if (pField->IsByValue())
389 TypeHandle th = pField->GetFieldTypeHandleThrowing();
390 MethodTable* pFieldMT = th.GetMethodTable();
392 // AllocateStaticBox will pin this object if this class is FixedAddressVTStatics.
393 // We save this pinning handle in a list attached to the ThreadLocalBlock. When
394 // the thread dies, we release all the pinning handles in the list.
397 OBJECTREF obj = MethodTable::AllocateStaticBox(pFieldMT, pMT->HasFixedAddressVTStatics(), &handle);
399 PTR_BYTE pStaticBase = pMT->GetGCThreadStaticsBasePointer();
400 _ASSERTE(pStaticBase != NULL);
402 SetObjectReference( (OBJECTREF*)(pStaticBase + pField->GetOffset()), obj, GetAppDomain() );
404 // If we created a pinning handle, save it to the list
406 AddPinningHandleToList(handle);
415 #ifndef DACCESS_COMPILE
417 void ThreadLocalModule::EnsureDynamicClassIndex(DWORD dwID)
424 INJECT_FAULT(COMPlusThrowOM(););
428 if (dwID < m_aDynamicEntries)
430 _ASSERTE(m_pDynamicClassTable != NULL);
434 SIZE_T aDynamicEntries = max(16, m_aDynamicEntries);
435 while (aDynamicEntries <= dwID)
437 aDynamicEntries *= 2;
440 DynamicClassInfo* pNewDynamicClassTable;
442 // If this allocation fails, we throw. If it succeeds,
443 // then we are good to go
444 pNewDynamicClassTable = (DynamicClassInfo*)(void*)new BYTE[sizeof(DynamicClassInfo) * aDynamicEntries];
446 // Zero out the dynamic class table
447 memset(pNewDynamicClassTable, 0, sizeof(DynamicClassInfo) * aDynamicEntries);
449 // We might always be guaranteed that this will be non-NULL, but just to be safe
450 if (m_pDynamicClassTable != NULL)
452 memcpy(pNewDynamicClassTable, m_pDynamicClassTable, sizeof(DynamicClassInfo) * m_aDynamicEntries);
456 _ASSERTE(m_aDynamicEntries == 0);
459 _ASSERTE(m_aDynamicEntries%2 == 0);
461 DynamicClassInfo* pOldDynamicClassTable = m_pDynamicClassTable;
463 m_pDynamicClassTable = pNewDynamicClassTable;
464 m_aDynamicEntries = aDynamicEntries;
466 if (pOldDynamicClassTable != NULL)
467 delete pOldDynamicClassTable;
470 void ThreadLocalModule::AllocateDynamicClass(MethodTable *pMT)
477 INJECT_FAULT(COMPlusThrowOM(););
481 _ASSERTE(!pMT->IsSharedByGenericInstantiations());
482 _ASSERTE(pMT->IsDynamicStatics());
484 DWORD dwID = pMT->GetModuleDynamicEntryID();
486 EnsureDynamicClassIndex(dwID);
488 _ASSERTE(m_aDynamicEntries > dwID);
490 EEClass *pClass = pMT->GetClass();
491 DWORD dwStaticBytes = pClass->GetNonGCThreadStaticFieldBytes();
492 DWORD dwNumHandleStatics = pClass->GetNumHandleThreadStatics();
494 _ASSERTE(!IsClassAllocated(pMT));
495 _ASSERTE(!IsClassInitialized(pMT));
496 _ASSERTE(!IsClassInitError(pMT));
498 DynamicEntry *pDynamicStatics = m_pDynamicClassTable[dwID].m_pDynamicEntry;
500 // We need this check because maybe a class had a cctor but no statics
501 if (dwStaticBytes > 0 || dwNumHandleStatics > 0)
503 // Collectible types do not support static fields yet
504 if (pMT->Collectible())
505 COMPlusThrow(kNotSupportedException, W("NotSupported_CollectibleNotYet"));
507 if (pDynamicStatics == NULL)
509 // If this allocation fails, we will throw
510 pDynamicStatics = (DynamicEntry*)new BYTE[sizeof(DynamicEntry) + dwStaticBytes];
512 #ifdef FEATURE_64BIT_ALIGNMENT
513 // The memory block has be aligned at MAX_PRIMITIVE_FIELD_SIZE to guarantee alignment of statics
514 static_assert_no_msg(sizeof(DynamicEntry) % MAX_PRIMITIVE_FIELD_SIZE == 0);
515 _ASSERTE(IS_ALIGNED(pDynamicStatics, MAX_PRIMITIVE_FIELD_SIZE));
518 // Zero out the new DynamicEntry
519 memset((BYTE*)pDynamicStatics, 0, sizeof(DynamicEntry) + dwStaticBytes);
521 // Save the DynamicEntry in the DynamicClassTable
522 m_pDynamicClassTable[dwID].m_pDynamicEntry = pDynamicStatics;
525 if (dwNumHandleStatics > 0)
527 PTR_ThreadLocalBlock pThreadLocalBlock = GetThread()->m_pThreadLocalBlock;
528 _ASSERTE(pThreadLocalBlock != NULL);
529 pThreadLocalBlock->AllocateStaticFieldObjRefPtrs(dwNumHandleStatics,
530 &pDynamicStatics->m_pGCStatics);
535 void ThreadLocalModule::PopulateClass(MethodTable *pMT)
542 INJECT_FAULT(COMPlusThrowOM(););
546 _ASSERTE(this != NULL);
547 _ASSERTE(pMT != NULL);
548 _ASSERTE(!IsClassAllocated(pMT));
550 // If this is a dynamic class then we need to allocate
551 // an entry in our dynamic class table
552 if (pMT->IsDynamicStatics())
553 AllocateDynamicClass(pMT);
555 // We need to allocate boxes any value-type statics that are not
556 // primitives or enums, because these statics may contain references
557 // to objects on the GC heap
558 if (pMT->GetNumBoxedThreadStatics() > 0)
560 PTR_ThreadLocalBlock pThreadLocalBlock = ThreadStatics::GetCurrentTLB();
561 _ASSERTE(pThreadLocalBlock != NULL);
562 pThreadLocalBlock->AllocateThreadStaticBoxes(pMT);
565 // Mark the class as allocated
566 SetClassAllocated(pMT);
569 PTR_ThreadLocalModule ThreadStatics::AllocateAndInitTLM(ModuleIndex index, PTR_ThreadLocalBlock pThreadLocalBlock, Module * pModule) //static
576 INJECT_FAULT(COMPlusThrowOM(););
580 pThreadLocalBlock->EnsureModuleIndex(index);
582 _ASSERTE(pThreadLocalBlock != NULL);
583 _ASSERTE(pModule != NULL);
585 NewHolder<ThreadLocalModule> pThreadLocalModule = AllocateTLM(pModule);
587 pThreadLocalBlock->AllocateThreadStaticHandles(pModule, pThreadLocalModule);
589 pThreadLocalBlock->SetModuleSlot(index, pThreadLocalModule);
590 pThreadLocalModule.SuppressRelease();
592 return pThreadLocalModule;
596 PTR_ThreadLocalModule ThreadStatics::GetTLM(ModuleIndex index, Module * pModule) //static
606 // Get the TLM if it already exists
607 PTR_ThreadLocalModule pThreadLocalModule = ThreadStatics::GetTLMIfExists(index);
609 // If the TLM does not exist, create it now
610 if (pThreadLocalModule == NULL)
612 // Get the current ThreadLocalBlock
613 PTR_ThreadLocalBlock pThreadLocalBlock = ThreadStatics::GetCurrentTLB();
614 _ASSERTE(pThreadLocalBlock != NULL);
616 // Allocate and initialize the TLM, and add it to the TLB's table
617 pThreadLocalModule = AllocateAndInitTLM(index, pThreadLocalBlock, pModule);
620 return pThreadLocalModule;
623 PTR_ThreadLocalModule ThreadStatics::GetTLM(MethodTable * pMT) //static
625 Module * pModule = pMT->GetModuleForStatics();
626 return GetTLM(pModule->GetModuleIndex(), pModule);
629 PTR_ThreadLocalBlock ThreadStatics::AllocateTLB(PTR_Thread pThread, ADIndex index) //static
639 _ASSERTE(pThread->m_pThreadLocalBlock == NULL);
641 // Grow the TLB table so that it has room to store the newly allocated
642 // ThreadLocalBlock. If this growing the table fails we cannot proceed.
643 ThreadStatics::EnsureADIndex(pThread, index);
645 // Allocate a new TLB and update this Thread's pointer to the current
646 // ThreadLocalBlock. Constructor zeroes out everything for us.
647 pThread->m_pThreadLocalBlock = new ThreadLocalBlock();
649 // Store the newly allocated ThreadLocalBlock in the TLB table
650 if (pThread->m_pThreadLocalBlock != NULL)
652 // We grew the TLB table earlier, so it should have room
653 _ASSERTE(index.m_dwIndex >= 0 && index.m_dwIndex < pThread->m_TLBTableSize);
654 pThread->m_pTLBTable[index.m_dwIndex] = pThread->m_pThreadLocalBlock;
657 return pThread->m_pThreadLocalBlock;
660 PTR_ThreadLocalModule ThreadStatics::AllocateTLM(Module * pModule)
667 INJECT_FAULT(COMPlusThrowOM(););
671 SIZE_T size = pModule->GetThreadLocalModuleSize();
673 _ASSERTE(size >= ThreadLocalModule::OffsetOfDataBlob());
675 PTR_ThreadLocalModule pThreadLocalModule = (ThreadLocalModule*)new BYTE[size];
677 // We guarantee alignment for 64-bit regular thread statics on 32-bit platforms even without FEATURE_64BIT_ALIGNMENT for performance reasons.
679 // The memory block has to be aligned at MAX_PRIMITIVE_FIELD_SIZE to guarantee alignment of statics
680 _ASSERTE(IS_ALIGNED(pThreadLocalModule, MAX_PRIMITIVE_FIELD_SIZE));
682 // Zero out the part of memory where the TLM resides
683 memset(pThreadLocalModule, 0, size);
685 return pThreadLocalModule;
690 PTR_ThreadLocalBlock ThreadStatics::GetTLBIfExists(PTR_Thread pThread, ADIndex index) //static
702 // Check to see if we have a ThreadLocalBlock for the this AppDomain,
703 if (index.m_dwIndex < pThread->m_TLBTableSize)
705 return pThread->m_pTLBTable[index.m_dwIndex];