//========================================================================= // Asserts if lock mode is PREEMPTIVE and thread in a GC_NOTRIGGER contract //========================================================================= void SimpleRWLock::CheckGCNoTrigger() { STATIC_CONTRACT_NOTHROW; // On PREEMPTIVE locks we'll toggle the GC mode, so we better not be in a GC_NOTRIGGERS region if (m_gcMode == PREEMPTIVE) { ClrDebugState *pClrDebugState = CheckClrDebugState(); if (pClrDebugState) { if (pClrDebugState->GetGCNoTriggerCount()) { // If we have no thread object, we won't be toggling the GC. This is the case, // for example, on the debugger helper thread which is always GC_NOTRIGGERS. if (GetThreadNULLOk() != NULL) { if (!( (GCViolation|BadDebugState) & pClrDebugState->ViolationMask())) { CONTRACT_ASSERT("You cannot enter a lock in a GC_NOTRIGGER region.", Contract::GC_NoTrigger, Contract::GC_Mask, __FUNCTION__, __FILE__, __LINE__); } } } // The mode checks and enforcement of GC_NOTRIGGER during the lock are done in SimpleRWLock::PostEnter(). } } }
//============================================================================================= // Used to initialize the per-thread ClrDebugState. This is called once per thread (with // possible exceptions for OOM scenarios.) // // No matter what, this function will not return NULL. If it can't do its job because of OOM reasons, // it will return a pointer to &gBadClrDebugState which effectively disables contracts for // this thread. //============================================================================================= ClrDebugState *CLRInitDebugState() { // workaround! // // The existing Fls apis didn't provide the support we need and adding support cleanly is // messy because of the brittleness of IExecutionEngine. // // To understand this function, you need to know that the Fls routines have special semantics // for the TlsIdx_ClrDebugState slot: // // - FlsSetValue will never throw. If it fails due to OOM on creation of the slot storage, // it will silently bail. Thus, we must do a confirming FlsGetValue before we can conclude // that the SetValue succeeded. // // - FlsAssociateCallback will not complain about multiple sets of the callback. // // - The mscorwks implemention of FlsAssociateCallback will ignore the passed in value // and use the version of FreeClrDebugState compiled into mscorwks. This is needed to // avoid dangling pointer races on shutdown. // This is our global "bad" debug state that thread use when they OOM on CLRInitDebugState. // We really only need to initialize it once but initializing each time is convenient // and has low perf impact. static ClrDebugState gBadClrDebugState; gBadClrDebugState.ViolationMaskSet( AllViolation ); // SO_INFRASTRUCTURE_CODE() Macro to remove SO infrastructure code during build SO_INFRASTRUCTURE_CODE(gBadClrDebugState.BeginSOTolerant();)
// Fls callback to deallocate ClrDebugState when our FLS block goes away. void FreeClrDebugState(LPVOID pTlsData) { #ifdef _DEBUG ClrDebugState *pClrDebugState = (ClrDebugState*)pTlsData; // Make sure the ClrDebugState was initialized by a compatible version of // utilcode.lib. If it was initialized by an older version, we just let it leak. if (pClrDebugState && (pClrDebugState->ViolationMask() & CanFreeMe) && !(pClrDebugState->ViolationMask() & BadDebugState)) { #undef HeapFree #undef GetProcessHeap // Since "!(pClrDebugState->m_violationmask & BadDebugState)", we know we have // a valid m_pLockData _ASSERTE(pClrDebugState->GetDbgStateLockData() != NULL); ::HeapFree (GetProcessHeap(), 0, pClrDebugState->GetDbgStateLockData()); ::HeapFree (GetProcessHeap(), 0, pClrDebugState); #define HeapFree(hHeap, dwFlags, lpMem) Dont_Use_HeapFree(hHeap, dwFlags, lpMem) #define GetProcessHeap() Dont_Use_GetProcessHeap() } #endif //_DEBUG }
// // EnsureSOTolerant ASSERTS if we are not in an SO-tolerant mode // void EnsureSOTolerant() { ClrDebugState *pClrDebugState = GetClrDebugState(); _ASSERTE(! pClrDebugState || pClrDebugState->IsSOTolerant()); }