diff --git a/src/coreclr/vm/appdomain.cpp b/src/coreclr/vm/appdomain.cpp index e36a2b57a0390..6c9863c3f5fd4 100644 --- a/src/coreclr/vm/appdomain.cpp +++ b/src/coreclr/vm/appdomain.cpp @@ -672,7 +672,7 @@ void AppDomain::SetNativeDllSearchDirectories(LPCWSTR wszNativeDllSearchDirector } } -OBJECTREF* BaseDomain::AllocateObjRefPtrsInLargeTable(int nRequested, DynamicStaticsInfo* pStaticsInfo, MethodTable *pMTToFillWithStaticBoxes) +OBJECTREF* BaseDomain::AllocateObjRefPtrsInLargeTable(int nRequested, DynamicStaticsInfo* pStaticsInfo, MethodTable *pMTToFillWithStaticBoxes, bool isClassInitdeByUpdatingStaticPointer) { CONTRACTL { @@ -707,7 +707,7 @@ OBJECTREF* BaseDomain::AllocateObjRefPtrsInLargeTable(int nRequested, DynamicSta if (pStaticsInfo) { // race with other threads that might be doing the same concurrent allocation - if (!pStaticsInfo->InterlockedUpdateStaticsPointer(/*isGCPointer*/ true, (TADDR)result)) + if (!pStaticsInfo->InterlockedUpdateStaticsPointer(/*isGCPointer*/ true, (TADDR)result, isClassInitdeByUpdatingStaticPointer)) { // we lost the race, release our handles and use the handles from the // winning thread @@ -2930,7 +2930,7 @@ void AppDomain::SetupSharedStatics() FieldDesc * pEmptyStringFD = CoreLibBinder::GetField(FIELD__STRING__EMPTY); OBJECTREF* pEmptyStringHandle = (OBJECTREF*) - ((TADDR)g_pStringClass->GetDynamicStaticsInfo()->m_pGCStatics+pEmptyStringFD->GetOffset()); + ((TADDR)g_pStringClass->GetDynamicStaticsInfo()->GetGCStaticsPointer()+pEmptyStringFD->GetOffset()); SetObjectReference( pEmptyStringHandle, StringObject::GetEmptyString()); } diff --git a/src/coreclr/vm/appdomain.hpp b/src/coreclr/vm/appdomain.hpp index 83d28792d556a..5957aad24655e 100644 --- a/src/coreclr/vm/appdomain.hpp +++ b/src/coreclr/vm/appdomain.hpp @@ -522,7 +522,7 @@ class BaseDomain // Statics and reflection info (Types, MemberInfo,..) are stored this way // If pStaticsInfo != 0, allocation will only take place if GC statics in the DynamicStaticsInfo are NULL (and the allocation // will be properly serialized) - OBJECTREF *AllocateObjRefPtrsInLargeTable(int nRequested, DynamicStaticsInfo* pStaticsInfo = NULL, MethodTable *pMTToFillWithStaticBoxes = NULL); + OBJECTREF *AllocateObjRefPtrsInLargeTable(int nRequested, DynamicStaticsInfo* pStaticsInfo = NULL, MethodTable *pMTToFillWithStaticBoxes = NULL, bool isClassInitdeByUpdatingStaticPointer = false); //**************************************************************************************** // Handles diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 9bcedace5d240..426c872d9d6ca 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -1210,10 +1210,9 @@ CorInfoHelpFunc CEEInfo::getSharedStaticsHelper(FieldDesc * pField, MethodTable { STANDARD_VM_CONTRACT; - pFieldMT->AttemptToPreinit(); bool GCStatic = (pField->GetFieldType() == ELEMENT_TYPE_CLASS || pField->GetFieldType() == ELEMENT_TYPE_VALUETYPE); - bool noCtor = pFieldMT->IsClassInited(); + bool noCtor = pFieldMT->IsClassInitedOrPreinited(); bool threadStatic = pField->IsThreadStatic(); bool isInexactMT = pFieldMT->IsSharedByGenericInstantiations(); bool isCollectible = pFieldMT->Collectible(); @@ -1451,7 +1450,7 @@ void CEEInfo::getFieldInfo (CORINFO_RESOLVED_TOKEN * pResolvedToken, } // We are not going through a helper. The constructor has to be triggered explicitly. - if (!pFieldMT->IsClassInited()) + if (!pFieldMT->IsClassInitedOrPreinited()) fieldFlags |= CORINFO_FLG_FIELD_INITCLASS; } else @@ -1536,10 +1535,9 @@ void CEEInfo::getFieldInfo (CORINFO_RESOLVED_TOKEN * pResolvedToken, // Allocate space for the local class if necessary, but don't trigger // class construction. pFieldMT->EnsureStaticDataAllocated(); - pFieldMT->AttemptToPreinit(); // We are not going through a helper. The constructor has to be triggered explicitly. - if (!pFieldMT->IsClassInited()) + if (!pFieldMT->IsClassInitedOrPreinited()) fieldFlags |= CORINFO_FLG_FIELD_INITCLASS; GCX_COOP(); @@ -3884,8 +3882,7 @@ CorInfoInitClassResult CEEInfo::initClass( MethodTable *pTypeToInitMT = typeToInitTH.AsMethodTable(); - pTypeToInitMT->AttemptToPreinit(); - if (pTypeToInitMT->IsClassInited()) + if (pTypeToInitMT->IsClassInitedOrPreinited()) { // If the type is initialized there really is nothing to do. result = CORINFO_INITCLASS_INITIALIZED; @@ -11725,7 +11722,7 @@ bool CEEInfo::getStaticFieldContent(CORINFO_FIELD_HANDLE fieldHnd, uint8_t* buff // class construction. pEnclosingMT->EnsureStaticDataAllocated(); - if (!field->IsThreadStatic() && pEnclosingMT->IsClassInited() && IsFdInitOnly(field->GetAttributes())) + if (!field->IsThreadStatic() && pEnclosingMT->IsClassInitedOrPreinited() && IsFdInitOnly(field->GetAttributes())) { if (field->IsObjRef()) { @@ -11913,7 +11910,7 @@ CORINFO_CLASS_HANDLE CEEJitInfo::getStaticFieldCurrentClass(CORINFO_FIELD_HANDLE VALIDATEOBJECTREF(fieldObj); // Check for initialization before looking at the value - isClassInitialized = !!pEnclosingMT->IsClassInited(); + isClassInitialized = !!pEnclosingMT->IsClassInitedOrPreinited(); if (fieldObj != NULL) { diff --git a/src/coreclr/vm/loaderallocator.cpp b/src/coreclr/vm/loaderallocator.cpp index c186a3d1b9e1b..6cc8b62480a00 100644 --- a/src/coreclr/vm/loaderallocator.cpp +++ b/src/coreclr/vm/loaderallocator.cpp @@ -2254,7 +2254,7 @@ PTR_OnStackReplacementManager LoaderAllocator::GetOnStackReplacementManager() #endif // FEATURE_ON_STACK_REPLACEMENT #ifndef DACCESS_COMPILE -void LoaderAllocator::AllocateBytesForStaticVariables(DynamicStaticsInfo* pStaticsInfo, uint32_t cbMem) +void LoaderAllocator::AllocateBytesForStaticVariables(DynamicStaticsInfo* pStaticsInfo, uint32_t cbMem, bool isClassInitedByUpdatingStaticPointer) { CONTRACTL { @@ -2294,7 +2294,7 @@ void LoaderAllocator::AllocateBytesForStaticVariables(DynamicStaticsInfo* pStati WeakInteriorHandleHolder weakHandleHolder = GetAppDomain()->CreateWeakInteriorHandle(ptrArray, &pStaticsInfo->m_pNonGCStatics); RegisterHandleForCleanupLocked(weakHandleHolder.GetValue()); weakHandleHolder.SuppressRelease(); - bool didUpdateStaticsPointer = pStaticsInfo->InterlockedUpdateStaticsPointer(/* isGCPointer */false, (TADDR)ptrArray->GetDataPtr()); + bool didUpdateStaticsPointer = pStaticsInfo->InterlockedUpdateStaticsPointer(/* isGCPointer */false, (TADDR)ptrArray->GetDataPtr(), isClassInitedByUpdatingStaticPointer); _ASSERTE(didUpdateStaticsPointer); } } @@ -2324,11 +2324,11 @@ void LoaderAllocator::AllocateBytesForStaticVariables(DynamicStaticsInfo* pStati pbMem = (uint8_t*)ALIGN_UP(pbMem, 8); } #endif - pStaticsInfo->InterlockedUpdateStaticsPointer(/* isGCPointer */false, (TADDR)pbMem); + pStaticsInfo->InterlockedUpdateStaticsPointer(/* isGCPointer */false, (TADDR)pbMem, isClassInitedByUpdatingStaticPointer); } } -void LoaderAllocator::AllocateGCHandlesBytesForStaticVariables(DynamicStaticsInfo* pStaticsInfo, uint32_t cSlots, MethodTable* pMTToFillWithStaticBoxes) +void LoaderAllocator::AllocateGCHandlesBytesForStaticVariables(DynamicStaticsInfo* pStaticsInfo, uint32_t cSlots, MethodTable* pMTToFillWithStaticBoxes, bool isClassInitedByUpdatingStaticPointer) { CONTRACTL { @@ -2374,7 +2374,7 @@ void LoaderAllocator::AllocateGCHandlesBytesForStaticVariables(DynamicStaticsInf WeakInteriorHandleHolder weakHandleHolder = GetAppDomain()->CreateWeakInteriorHandle(ptrArray, &pStaticsInfo->m_pGCStatics); RegisterHandleForCleanupLocked(weakHandleHolder.GetValue()); weakHandleHolder.SuppressRelease(); - bool didUpdateStaticsPointer = pStaticsInfo->InterlockedUpdateStaticsPointer(/* isGCPointer */true, (TADDR)ptrArray->GetDataPtr()); + bool didUpdateStaticsPointer = pStaticsInfo->InterlockedUpdateStaticsPointer(/* isGCPointer */true, (TADDR)ptrArray->GetDataPtr(), isClassInitedByUpdatingStaticPointer); _ASSERTE(didUpdateStaticsPointer); } } @@ -2382,7 +2382,7 @@ void LoaderAllocator::AllocateGCHandlesBytesForStaticVariables(DynamicStaticsInf } else { - GetDomain()->AllocateObjRefPtrsInLargeTable(cSlots, pStaticsInfo, pMTToFillWithStaticBoxes); + GetDomain()->AllocateObjRefPtrsInLargeTable(cSlots, pStaticsInfo, pMTToFillWithStaticBoxes, isClassInitedByUpdatingStaticPointer); } } #endif // !DACCESS_COMPILE diff --git a/src/coreclr/vm/loaderallocator.hpp b/src/coreclr/vm/loaderallocator.hpp index b6f5ad1445572..0bf475e33d5fa 100644 --- a/src/coreclr/vm/loaderallocator.hpp +++ b/src/coreclr/vm/loaderallocator.hpp @@ -761,8 +761,8 @@ class LoaderAllocator LIMITED_METHOD_CONTRACT; return m_nGCCount; } - void AllocateBytesForStaticVariables(DynamicStaticsInfo* pStaticsInfo, uint32_t cbMem); - void AllocateGCHandlesBytesForStaticVariables(DynamicStaticsInfo* pStaticsInfo, uint32_t cSlots, MethodTable* pMTWithStaticBoxes); + void AllocateBytesForStaticVariables(DynamicStaticsInfo* pStaticsInfo, uint32_t cbMem, bool isClassInitedByUpdatingStaticPointer); + void AllocateGCHandlesBytesForStaticVariables(DynamicStaticsInfo* pStaticsInfo, uint32_t cSlots, MethodTable* pMTWithStaticBoxes, bool isClassInitedByUpdatingStaticPointer); static BOOL Destroy(QCall::LoaderAllocatorHandle pLoaderAllocator); diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index a0cb4ee4914e1..bb8a1646e511b 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -3782,20 +3782,42 @@ void MethodTable::EnsureStaticDataAllocated() CONTRACTL_END; PTR_MethodTableAuxiliaryData pAuxiliaryData = GetAuxiliaryDataForWrite(); - if (!pAuxiliaryData->IsStaticDataAllocated() && IsDynamicStatics()) + if (!pAuxiliaryData->IsStaticDataAllocated()) { - DynamicStaticsInfo *pDynamicStaticsInfo = GetDynamicStaticsInfo(); - // Allocate space for normal statics if we might have them - if (pDynamicStaticsInfo->GetNonGCStaticsPointer() == NULL) - GetLoaderAllocator()->AllocateBytesForStaticVariables(pDynamicStaticsInfo, GetClass()->GetNonGCRegularStaticFieldBytes()); + bool isInitedIfStaticDataAllocated = IsInitedIfStaticDataAllocated(); + if (IsDynamicStatics() && !IsSharedByGenericInstantiations()) + { + DynamicStaticsInfo *pDynamicStaticsInfo = GetDynamicStaticsInfo(); + // Allocate space for normal statics if we might have them + if (pDynamicStaticsInfo->GetNonGCStaticsPointer() == NULL) + GetLoaderAllocator()->AllocateBytesForStaticVariables(pDynamicStaticsInfo, GetClass()->GetNonGCRegularStaticFieldBytes(), isInitedIfStaticDataAllocated); - if (pDynamicStaticsInfo->GetGCStaticsPointer() == NULL) - GetLoaderAllocator()->AllocateGCHandlesBytesForStaticVariables(pDynamicStaticsInfo, GetClass()->GetNumHandleRegularStatics(), this->HasBoxedRegularStatics() ? this : NULL); + if (pDynamicStaticsInfo->GetGCStaticsPointer() == NULL) + GetLoaderAllocator()->AllocateGCHandlesBytesForStaticVariables(pDynamicStaticsInfo, GetClass()->GetNumHandleRegularStatics(), this->HasBoxedRegularStatics() ? this : NULL, isInitedIfStaticDataAllocated); + } + pAuxiliaryData->SetIsStaticDataAllocated(isInitedIfStaticDataAllocated); } - pAuxiliaryData->SetIsStaticDataAllocated(); } -void MethodTable::AttemptToPreinit() +bool MethodTable::IsClassInitedOrPreinited() +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + INJECT_FAULT(COMPlusThrowOM()); + } + CONTRACTL_END; + + bool initResult; + if (GetAuxiliaryData()->IsClassInitedOrPreinitedDecided(&initResult)) + return initResult; + + EnsureStaticDataAllocated(); + return IsClassInited(); +} + +bool MethodTable::IsInitedIfStaticDataAllocated() { CONTRACTL { @@ -3806,33 +3828,32 @@ void MethodTable::AttemptToPreinit() CONTRACTL_END; if (IsClassInited()) - return; + { + return true; + } if (HasClassConstructor()) { // If there is a class constructor, then the class cannot be preinitted. - return; + return false; } if (GetClass()->GetNonGCRegularStaticFieldBytes() == 0 && GetClass()->GetNumHandleRegularStatics() == 0) { - // If there are static fields that are not thread statics, then the class is preinitted. - SetClassInited(); - return; + // If there aren't static fields that are not thread statics, then the class is preinitted. + return true; } // At this point, we are looking at a class that has no class constructor, but does have static fields if (IsSharedByGenericInstantiations()) { - // If we don't know the exact type, we can't pre-allocate the fields - return; + // If we don't know the exact type, we can't pre-init the the fields + return false; } // All this class needs to be initialized is to allocate the memory for the static fields. Do so, and mark the type as initialized - EnsureStaticDataAllocated(); - SetClassInited(); - return; + return true; } void MethodTable::EnsureTlsIndexAllocated() diff --git a/src/coreclr/vm/methodtable.h b/src/coreclr/vm/methodtable.h index be663a43067ff..23fcb92e5291a 100644 --- a/src/coreclr/vm/methodtable.h +++ b/src/coreclr/vm/methodtable.h @@ -337,7 +337,7 @@ struct MethodTableAuxiliaryData enum_flag_DependenciesLoaded = 0x0080, // class and all dependencies loaded up to CLASS_LOADED_BUT_NOT_VERIFIED enum_flag_IsInitError = 0x0100, - enum_flag_IsStaticDataAllocated = 0x0200, + enum_flag_IsStaticDataAllocated = 0x0200, // When this is set, if the class can be marked as initialized without any further code execution it will be. // unum_unused = 0x0400, enum_flag_IsTlsIndexAllocated = 0x0800, enum_flag_MayHaveOpenInterfaceInInterfaceMap = 0x1000, @@ -447,9 +447,19 @@ struct MethodTableAuxiliaryData inline BOOL IsClassInited() const { + LIMITED_METHOD_DAC_CONTRACT; return VolatileLoad(&m_dwFlags) & enum_flag_Initialized; } + inline bool IsClassInitedOrPreinitedDecided(bool *initResult) const + { + LIMITED_METHOD_DAC_CONTRACT; + + DWORD dwFlags = VolatileLoad(&m_dwFlags); + *initResult = m_dwFlags & enum_flag_Initialized; + return (dwFlags & (enum_flag_IsStaticDataAllocated|enum_flag_Initialized)) != 0; + } + #ifndef DACCESS_COMPILE inline void SetClassInited() { @@ -465,10 +475,10 @@ struct MethodTableAuxiliaryData } #ifndef DACCESS_COMPILE - inline void SetIsStaticDataAllocated() + inline void SetIsStaticDataAllocated(bool markAsInitedToo) { LIMITED_METHOD_CONTRACT; - InterlockedOr((LONG*)&m_dwFlags, (LONG)enum_flag_IsStaticDataAllocated); + InterlockedOr((LONG*)&m_dwFlags, markAsInitedToo ? (LONG)(enum_flag_IsStaticDataAllocated|enum_flag_Initialized) : (LONG)enum_flag_IsStaticDataAllocated); } #endif @@ -567,7 +577,7 @@ struct DynamicStaticsInfo bool GetIsInitedAndNonGCStaticsPointerIfInited(PTR_BYTE *ptrResult) { TADDR staticsVal = VolatileLoadWithoutBarrier(&m_pNonGCStatics); *ptrResult = dac_cast(staticsVal); return !(staticsVal & ISCLASSNOTINITED); } // This function sets the pointer portion of a statics pointer. It returns false if the statics value was already set. - bool InterlockedUpdateStaticsPointer(bool isGC, TADDR newVal) + bool InterlockedUpdateStaticsPointer(bool isGC, TADDR newVal, bool isClassInitedByUpdatingStaticPointer) { TADDR oldVal; TADDR oldValFromInterlockedOp; @@ -583,7 +593,14 @@ struct DynamicStaticsInfo return false; } - oldValFromInterlockedOp = InterlockedCompareExchangeT(pAddr, newVal | oldVal, oldVal); + if (isClassInitedByUpdatingStaticPointer) + { + oldValFromInterlockedOp = InterlockedCompareExchangeT(pAddr, newVal, oldVal); + } + else + { + oldValFromInterlockedOp = InterlockedCompareExchangeT(pAddr, newVal | oldVal, oldVal); + } } while(oldValFromInterlockedOp != oldVal); return true; } @@ -1042,18 +1059,31 @@ class MethodTable #ifndef DACCESS_COMPILE void SetClassInited() { - GetAuxiliaryDataForWrite()->SetClassInited(); + // This must be before setting the MethodTable level flag, as otherwise there is a race condition where + // the MethodTable flag is set, which would allows the JIT to generate a call to a helper which assumes + // the DynamicStaticInfo level flag is set. + // The other race in the other direction is not a concern, as it can only cause allows reads/write from the static + // fields, which are effectively inited in any case once we reach this point. if (IsDynamicStatics()) { GetDynamicStaticsInfo()->SetClassInited(); } + GetAuxiliaryDataForWrite()->SetClassInited(); } - void AttemptToPreinit(); +private: + bool IsInitedIfStaticDataAllocated(); +public: + // Is the MethodTable current initialized, and/or can the runtime initialize the MethodTable + // without running any user code. (This function may allocate memory, and may throw OutOfMemory) + bool IsClassInitedOrPreinited(); #endif + // Is the MethodTable current known to be initialized + // If you want to know if it is initialized and allocation/throwing is permitted, call IsClassInitedOrPreinited instead BOOL IsClassInited() { + LIMITED_METHOD_DAC_CONTRACT; return GetAuxiliaryDataForWrite()->IsClassInited(); } diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index 85931bff5636e..ad00f7c5b1bbf 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -3545,9 +3545,8 @@ static PCODE getHelperForStaticBase(Module * pModule, CORCOMPILE_FIXUP_BLOB_KIND { STANDARD_VM_CONTRACT; - pMT->AttemptToPreinit(); bool GCStatic = (kind == ENCODE_STATIC_BASE_GC_HELPER || kind == ENCODE_THREAD_STATIC_BASE_GC_HELPER); - bool noCtor = pMT->IsClassInited(); + bool noCtor = pMT->IsClassInitedOrPreinited(); bool threadStatic = (kind == ENCODE_THREAD_STATIC_BASE_NONGC_HELPER || kind == ENCODE_THREAD_STATIC_BASE_GC_HELPER); CorInfoHelpFunc helper; @@ -3916,7 +3915,7 @@ PCODE DynamicHelperFixup(TransitionBlock * pTransitionBlock, TADDR * pCell, DWOR else { // Delay the creation of the helper until the type is initialized - if (pMT->IsClassInited()) + if (pMT->IsClassInitedOrPreinited()) pHelper = getHelperForInitializedStatic(pModule, (CORCOMPILE_FIXUP_BLOB_KIND)kind, pMT, pFD); } }