diff options
Diffstat (limited to 'src/vm/threadstatics.cpp')
-rw-r--r-- | src/vm/threadstatics.cpp | 709 |
1 files changed, 709 insertions, 0 deletions
diff --git a/src/vm/threadstatics.cpp b/src/vm/threadstatics.cpp new file mode 100644 index 0000000000..501cbbcba8 --- /dev/null +++ b/src/vm/threadstatics.cpp @@ -0,0 +1,709 @@ +// 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. +// +// ThreadStatics.cpp +// + +// +// + + +#include "common.h" + +#include "threadstatics.h" +#include "field.h" + + +#ifndef DACCESS_COMPILE + +void ThreadLocalBlock::FreeTLM(SIZE_T i) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + SO_TOLERANT; + MODE_ANY; + } + CONTRACTL_END; + _ASSERTE(m_pTLMTable != NULL); + + PTR_ThreadLocalModule pThreadLocalModule = m_pTLMTable[i].pTLM; + m_pTLMTable[i].pTLM = NULL; + + if (pThreadLocalModule != NULL) + { + if (pThreadLocalModule->m_pDynamicClassTable != NULL) + { + for (DWORD k = 0; k < pThreadLocalModule->m_aDynamicEntries; ++k) + { + if (pThreadLocalModule->m_pDynamicClassTable[k].m_pDynamicEntry != NULL) + { + delete pThreadLocalModule->m_pDynamicClassTable[k].m_pDynamicEntry; + pThreadLocalModule->m_pDynamicClassTable[k].m_pDynamicEntry = NULL; + } + } + delete pThreadLocalModule->m_pDynamicClassTable; + pThreadLocalModule->m_pDynamicClassTable = NULL; + } + + delete pThreadLocalModule; + } +} + +void ThreadLocalBlock::FreeTable() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + SO_INTOLERANT; + MODE_COOPERATIVE; + } + CONTRACTL_END; + // Free the TLM table + if (m_pTLMTable != NULL) + { + // Iterate over the table and free each TLM + for (SIZE_T i = 0; i < m_TLMTableSize; ++i) + { + if (m_pTLMTable[i].pTLM != NULL) + { + FreeTLM(i); + } + } + + // Free the table itself + delete m_pTLMTable; + m_pTLMTable = NULL; + } + + // Set table size to zero + m_TLMTableSize = 0; + + // Free the ThreadStaticHandleTable + if (m_pThreadStaticHandleTable != NULL) + { + delete m_pThreadStaticHandleTable; + m_pThreadStaticHandleTable = NULL; + } + + // Free any pinning handles we may have created + FreePinningHandles(); +} + +void ThreadLocalBlock::EnsureModuleIndex(ModuleIndex index) +{ + CONTRACTL { + THROWS; + GC_NOTRIGGER; + } CONTRACTL_END; + + if (m_TLMTableSize > index.m_dwIndex) + { + _ASSERTE(m_pTLMTable != NULL); + return; + } + + SIZE_T aModuleIndices = max(16, m_TLMTableSize); + while (aModuleIndices <= index.m_dwIndex) + { + aModuleIndices *= 2; + } + + // If this allocation fails, we will throw. If it succeeds, + // then we are good to go + PTR_TLMTableEntry pNewModuleSlots = (PTR_TLMTableEntry) (void*) new BYTE[sizeof(TLMTableEntry) * aModuleIndices]; + + // Zero out the new TLM table + memset(pNewModuleSlots, 0 , sizeof(TLMTableEntry) * aModuleIndices); + + if (m_pTLMTable != NULL) + { + memcpy(pNewModuleSlots, m_pTLMTable, sizeof(TLMTableEntry) * m_TLMTableSize); + } + else + { + _ASSERTE(m_TLMTableSize == 0); + } + + PTR_TLMTableEntry pOldModuleSlots = m_pTLMTable; + + m_pTLMTable = pNewModuleSlots; + m_TLMTableSize = aModuleIndices; + + if (pOldModuleSlots != NULL) + delete pOldModuleSlots; +} + +#endif + +void ThreadLocalBlock::SetModuleSlot(ModuleIndex index, PTR_ThreadLocalModule pLocalModule) +{ + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + } CONTRACTL_END; + + // This method will not grow the table. You need to grow + // the table explicitly before calling SetModuleSlot() + + _ASSERTE(index.m_dwIndex < m_TLMTableSize); + + m_pTLMTable[index.m_dwIndex].pTLM = pLocalModule; +} + +#ifdef DACCESS_COMPILE + +void +ThreadLocalModule::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) +{ + SUPPORTS_DAC; + + // Enumerate the ThreadLocalModule itself. TLMs are allocated to be larger than + // sizeof(ThreadLocalModule) to make room for ClassInit flags and non-GC statics. + // "DAC_ENUM_DTHIS()" probably does not account for this, so we might not enumerate + // all of the ClassInit flags and non-GC statics. + DAC_ENUM_DTHIS(); + + if (m_pDynamicClassTable != NULL) + { + DacEnumMemoryRegion(dac_cast<TADDR>(m_pDynamicClassTable), + m_aDynamicEntries * sizeof(DynamicClassInfo)); + + for (SIZE_T i = 0; i < m_aDynamicEntries; i++) + { + PTR_DynamicEntry entry = dac_cast<PTR_DynamicEntry>(m_pDynamicClassTable[i].m_pDynamicEntry); + if (entry.IsValid()) + { + entry.EnumMem(); + } + } + } +} + +void +ThreadLocalBlock::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) +{ + SUPPORTS_DAC; + + // Enumerate the ThreadLocalBlock itself + DAC_ENUM_DTHIS(); + + if (m_pTLMTable.IsValid()) + { + DacEnumMemoryRegion(dac_cast<TADDR>(m_pTLMTable), + m_TLMTableSize * sizeof(TADDR)); + + for (SIZE_T i = 0; i < m_TLMTableSize; i++) + { + PTR_ThreadLocalModule domMod = m_pTLMTable[i].pTLM; + if (domMod.IsValid()) + { + domMod->EnumMemoryRegions(flags); + } + } + } +} + +#endif + +DWORD ThreadLocalModule::GetClassFlags(MethodTable* pMT, DWORD iClassIndex) // iClassIndex defaults to (DWORD)-1 +{ + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + SO_TOLERANT; + } CONTRACTL_END; + + if (pMT->IsDynamicStatics()) + { + DWORD dynamicClassID = pMT->GetModuleDynamicEntryID(); + if(m_aDynamicEntries <= dynamicClassID) + return FALSE; + return (m_pDynamicClassTable[dynamicClassID].m_dwFlags); + } + else + { + if (iClassIndex == (DWORD)-1) + iClassIndex = pMT->GetClassIndex(); + return GetPrecomputedStaticsClassData()[iClassIndex]; + } +} + +#ifndef DACCESS_COMPILE + +void ThreadLocalModule::SetClassFlags(MethodTable* pMT, DWORD dwFlags) +{ + CONTRACTL { + THROWS; + GC_NOTRIGGER; + } CONTRACTL_END; + + if (pMT->IsDynamicStatics()) + { + DWORD dwID = pMT->GetModuleDynamicEntryID(); + EnsureDynamicClassIndex(dwID); + m_pDynamicClassTable[dwID].m_dwFlags |= dwFlags; + } + else + { + GetPrecomputedStaticsClassData()[pMT->GetClassIndex()] |= dwFlags; + } +} + +void ThreadLocalBlock::AddPinningHandleToList(OBJECTHANDLE oh) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + SO_TOLERANT; + MODE_ANY; + } + CONTRACTL_END; + ObjectHandleList::NodeType* pNewNode = new ObjectHandleList::NodeType(oh); + m_PinningHandleList.LinkHead(pNewNode); +} + +void ThreadLocalBlock::FreePinningHandles() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + SO_TOLERANT; + MODE_ANY; + } + CONTRACTL_END; + // Destroy all pinning handles in the list, and free the nodes + ObjectHandleList::NodeType* pHandleNode; + while ((pHandleNode = m_PinningHandleList.UnlinkHead()) != NULL) + { + DestroyPinningHandle(pHandleNode->data); + delete pHandleNode; + } +} + +void ThreadLocalBlock::AllocateThreadStaticHandles(Module * pModule, PTR_ThreadLocalModule pThreadLocalModule) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + } + CONTRACTL_END; + + _ASSERTE(pThreadLocalModule->GetPrecomputedGCStaticsBaseHandleAddress() != NULL); + _ASSERTE(pThreadLocalModule->GetPrecomputedGCStaticsBaseHandle() == NULL); + + if (pModule->GetNumGCThreadStaticHandles() > 0) + { + AllocateStaticFieldObjRefPtrs(pModule->GetNumGCThreadStaticHandles(), + pThreadLocalModule->GetPrecomputedGCStaticsBaseHandleAddress()); + + // We should throw if we fail to allocate and never hit this assert + _ASSERTE(pThreadLocalModule->GetPrecomputedGCStaticsBaseHandle() != NULL); + _ASSERTE(pThreadLocalModule->GetPrecomputedGCStaticsBasePointer() != NULL); + } +} + +OBJECTHANDLE ThreadLocalBlock::AllocateStaticFieldObjRefPtrs(int nRequested, OBJECTHANDLE * ppLazyAllocate) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + PRECONDITION((nRequested > 0)); + INJECT_FAULT(COMPlusThrowOM();); + } + CONTRACTL_END; + + if (ppLazyAllocate && *ppLazyAllocate) + { + // Allocation already happened + return *ppLazyAllocate; + } + + // Make sure the large heap handle table is initialized. + if (!m_pThreadStaticHandleTable) + InitThreadStaticHandleTable(); + + // Allocate the handles. + OBJECTHANDLE result = m_pThreadStaticHandleTable->AllocateHandles(nRequested); + + if (ppLazyAllocate) + { + *ppLazyAllocate = result; + } + + return result; +} + +void ThreadLocalBlock::InitThreadStaticHandleTable() +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + PRECONDITION(m_pThreadStaticHandleTable==NULL); + INJECT_FAULT(COMPlusThrowOM();); + } + CONTRACTL_END; + + // If the allocation fails this will throw; callers need + // to account for this possibility + m_pThreadStaticHandleTable = new ThreadStaticHandleTable(GetAppDomain()); +} + +void ThreadLocalBlock::AllocateThreadStaticBoxes(MethodTable * pMT) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + PRECONDITION(pMT->GetNumBoxedThreadStatics() > 0); + INJECT_FAULT(COMPlusThrowOM();); + } + CONTRACTL_END; + + FieldDesc *pField = pMT->HasGenericsStaticsInfo() ? + pMT->GetGenericsStaticFieldDescs() : (pMT->GetApproxFieldDescListRaw() + pMT->GetNumIntroducedInstanceFields()); + + // Move pField to point to the list of thread statics + pField += pMT->GetNumStaticFields() - pMT->GetNumThreadStaticFields(); + + FieldDesc *pFieldEnd = pField + pMT->GetNumThreadStaticFields(); + + while (pField < pFieldEnd) + { + _ASSERTE(pField->IsThreadStatic()); + + // We only care about thread statics which are value types + if (pField->IsByValue()) + { + TypeHandle th = pField->GetFieldTypeHandleThrowing(); + MethodTable* pFieldMT = th.GetMethodTable(); + + // AllocateStaticBox will pin this object if this class is FixedAddressVTStatics. + // We save this pinning handle in a list attached to the ThreadLocalBlock. When + // the thread dies, we release all the pinning handles in the list. + + OBJECTHANDLE handle; + OBJECTREF obj = MethodTable::AllocateStaticBox(pFieldMT, pMT->HasFixedAddressVTStatics(), &handle); + + PTR_BYTE pStaticBase = pMT->GetGCThreadStaticsBasePointer(); + _ASSERTE(pStaticBase != NULL); + + SetObjectReference( (OBJECTREF*)(pStaticBase + pField->GetOffset()), obj, GetAppDomain() ); + + // If we created a pinning handle, save it to the list + if (handle != NULL) + AddPinningHandleToList(handle); + } + + pField++; + } +} + +#endif + +#ifndef DACCESS_COMPILE + +void ThreadLocalModule::EnsureDynamicClassIndex(DWORD dwID) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + INJECT_FAULT(COMPlusThrowOM();); + } + CONTRACTL_END; + + if (dwID < m_aDynamicEntries) + { + _ASSERTE(m_pDynamicClassTable != NULL); + return; + } + + SIZE_T aDynamicEntries = max(16, m_aDynamicEntries); + while (aDynamicEntries <= dwID) + { + aDynamicEntries *= 2; + } + + DynamicClassInfo* pNewDynamicClassTable; + + // If this allocation fails, we throw. If it succeeds, + // then we are good to go + pNewDynamicClassTable = (DynamicClassInfo*)(void*)new BYTE[sizeof(DynamicClassInfo) * aDynamicEntries]; + + // Zero out the dynamic class table + memset(pNewDynamicClassTable, 0, sizeof(DynamicClassInfo) * aDynamicEntries); + + // We might always be guaranteed that this will be non-NULL, but just to be safe + if (m_pDynamicClassTable != NULL) + { + memcpy(pNewDynamicClassTable, m_pDynamicClassTable, sizeof(DynamicClassInfo) * m_aDynamicEntries); + } + else + { + _ASSERTE(m_aDynamicEntries == 0); + } + + _ASSERTE(m_aDynamicEntries%2 == 0); + + DynamicClassInfo* pOldDynamicClassTable = m_pDynamicClassTable; + + m_pDynamicClassTable = pNewDynamicClassTable; + m_aDynamicEntries = aDynamicEntries; + + if (pOldDynamicClassTable != NULL) + delete pOldDynamicClassTable; +} + +void ThreadLocalModule::AllocateDynamicClass(MethodTable *pMT) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + INJECT_FAULT(COMPlusThrowOM();); + } + CONTRACTL_END; + + _ASSERTE(!pMT->IsSharedByGenericInstantiations()); + _ASSERTE(pMT->IsDynamicStatics()); + + DWORD dwID = pMT->GetModuleDynamicEntryID(); + + EnsureDynamicClassIndex(dwID); + + _ASSERTE(m_aDynamicEntries > dwID); + + EEClass *pClass = pMT->GetClass(); + DWORD dwStaticBytes = pClass->GetNonGCThreadStaticFieldBytes(); + DWORD dwNumHandleStatics = pClass->GetNumHandleThreadStatics(); + + _ASSERTE(!IsClassAllocated(pMT)); + _ASSERTE(!IsClassInitialized(pMT)); + _ASSERTE(!IsClassInitError(pMT)); + + DynamicEntry *pDynamicStatics = m_pDynamicClassTable[dwID].m_pDynamicEntry; + + // We need this check because maybe a class had a cctor but no statics + if (dwStaticBytes > 0 || dwNumHandleStatics > 0) + { + // Collectible types do not support static fields yet + if (pMT->Collectible()) + COMPlusThrow(kNotSupportedException, W("NotSupported_CollectibleNotYet")); + + if (pDynamicStatics == NULL) + { + // If this allocation fails, we will throw + pDynamicStatics = (DynamicEntry*)new BYTE[sizeof(DynamicEntry) + dwStaticBytes]; + +#ifdef FEATURE_64BIT_ALIGNMENT + // The memory block has be aligned at MAX_PRIMITIVE_FIELD_SIZE to guarantee alignment of statics + static_assert_no_msg(sizeof(DynamicEntry) % MAX_PRIMITIVE_FIELD_SIZE == 0); + _ASSERTE(IS_ALIGNED(pDynamicStatics, MAX_PRIMITIVE_FIELD_SIZE)); +#endif + + // Zero out the new DynamicEntry + memset((BYTE*)pDynamicStatics, 0, sizeof(DynamicEntry) + dwStaticBytes); + + // Save the DynamicEntry in the DynamicClassTable + m_pDynamicClassTable[dwID].m_pDynamicEntry = pDynamicStatics; + } + + if (dwNumHandleStatics > 0) + { + PTR_ThreadLocalBlock pThreadLocalBlock = GetThread()->m_pThreadLocalBlock; + _ASSERTE(pThreadLocalBlock != NULL); + pThreadLocalBlock->AllocateStaticFieldObjRefPtrs(dwNumHandleStatics, + &pDynamicStatics->m_pGCStatics); + } + } +} + +void ThreadLocalModule::PopulateClass(MethodTable *pMT) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + INJECT_FAULT(COMPlusThrowOM();); + } + CONTRACTL_END; + + _ASSERTE(this != NULL); + _ASSERTE(pMT != NULL); + _ASSERTE(!IsClassAllocated(pMT)); + + // If this is a dynamic class then we need to allocate + // an entry in our dynamic class table + if (pMT->IsDynamicStatics()) + AllocateDynamicClass(pMT); + + // We need to allocate boxes any value-type statics that are not + // primitives or enums, because these statics may contain references + // to objects on the GC heap + if (pMT->GetNumBoxedThreadStatics() > 0) + { + PTR_ThreadLocalBlock pThreadLocalBlock = ThreadStatics::GetCurrentTLB(); + _ASSERTE(pThreadLocalBlock != NULL); + pThreadLocalBlock->AllocateThreadStaticBoxes(pMT); + } + + // Mark the class as allocated + SetClassAllocated(pMT); +} + +PTR_ThreadLocalModule ThreadStatics::AllocateAndInitTLM(ModuleIndex index, PTR_ThreadLocalBlock pThreadLocalBlock, Module * pModule) //static +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + INJECT_FAULT(COMPlusThrowOM();); + } + CONTRACTL_END; + + pThreadLocalBlock->EnsureModuleIndex(index); + + _ASSERTE(pThreadLocalBlock != NULL); + _ASSERTE(pModule != NULL); + + NewHolder<ThreadLocalModule> pThreadLocalModule = AllocateTLM(pModule); + + pThreadLocalBlock->AllocateThreadStaticHandles(pModule, pThreadLocalModule); + + pThreadLocalBlock->SetModuleSlot(index, pThreadLocalModule); + pThreadLocalModule.SuppressRelease(); + + return pThreadLocalModule; +} + + +PTR_ThreadLocalModule ThreadStatics::GetTLM(ModuleIndex index, Module * pModule) //static +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + } + CONTRACTL_END; + + // Get the TLM if it already exists + PTR_ThreadLocalModule pThreadLocalModule = ThreadStatics::GetTLMIfExists(index); + + // If the TLM does not exist, create it now + if (pThreadLocalModule == NULL) + { + // Get the current ThreadLocalBlock + PTR_ThreadLocalBlock pThreadLocalBlock = ThreadStatics::GetCurrentTLB(); + _ASSERTE(pThreadLocalBlock != NULL); + + // Allocate and initialize the TLM, and add it to the TLB's table + pThreadLocalModule = AllocateAndInitTLM(index, pThreadLocalBlock, pModule); + } + + return pThreadLocalModule; +} + +PTR_ThreadLocalModule ThreadStatics::GetTLM(MethodTable * pMT) //static +{ + Module * pModule = pMT->GetModuleForStatics(); + return GetTLM(pModule->GetModuleIndex(), pModule); +} + +PTR_ThreadLocalBlock ThreadStatics::AllocateTLB(PTR_Thread pThread, ADIndex index) //static +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + SO_TOLERANT; + MODE_ANY; + } + CONTRACTL_END; + _ASSERTE(pThread->m_pThreadLocalBlock == NULL); + + // Grow the TLB table so that it has room to store the newly allocated + // ThreadLocalBlock. If this growing the table fails we cannot proceed. + ThreadStatics::EnsureADIndex(pThread, index); + + // Allocate a new TLB and update this Thread's pointer to the current + // ThreadLocalBlock. Constructor zeroes out everything for us. + pThread->m_pThreadLocalBlock = new ThreadLocalBlock(); + + // Store the newly allocated ThreadLocalBlock in the TLB table + if (pThread->m_pThreadLocalBlock != NULL) + { + // We grew the TLB table earlier, so it should have room + _ASSERTE(index.m_dwIndex >= 0 && index.m_dwIndex < pThread->m_TLBTableSize); + pThread->m_pTLBTable[index.m_dwIndex] = pThread->m_pThreadLocalBlock; + } + + return pThread->m_pThreadLocalBlock; +} + +PTR_ThreadLocalModule ThreadStatics::AllocateTLM(Module * pModule) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + INJECT_FAULT(COMPlusThrowOM();); + } + CONTRACTL_END; + + SIZE_T size = pModule->GetThreadLocalModuleSize(); + + _ASSERTE(size >= ThreadLocalModule::OffsetOfDataBlob()); + + PTR_ThreadLocalModule pThreadLocalModule = (ThreadLocalModule*)new BYTE[size]; + + // We guarantee alignment for 64-bit regular thread statics on 32-bit platforms even without FEATURE_64BIT_ALIGNMENT for performance reasons. + + // The memory block has to be aligned at MAX_PRIMITIVE_FIELD_SIZE to guarantee alignment of statics + _ASSERTE(IS_ALIGNED(pThreadLocalModule, MAX_PRIMITIVE_FIELD_SIZE)); + + // Zero out the part of memory where the TLM resides + memset(pThreadLocalModule, 0, size); + + return pThreadLocalModule; +} + +#endif + +PTR_ThreadLocalBlock ThreadStatics::GetTLBIfExists(PTR_Thread pThread, ADIndex index) //static +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + SUPPORTS_DAC; + SO_TOLERANT; + } + CONTRACTL_END; + + // Check to see if we have a ThreadLocalBlock for the this AppDomain, + if (index.m_dwIndex < pThread->m_TLBTableSize) + { + return pThread->m_pTLBTable[index.m_dwIndex]; + } + + return NULL; +} |