//========================================================================= // 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(). } } }
WinWrapperContract(const char *szFunction, const char *szFile, int lineNum) { CANNOT_HAVE_CONTRACT; m_pClrDebugState = NULL; if (gWinWrapperContractRecursionBreak) { return; } m_pClrDebugState = GetClrDebugState(); // Save old debug state m_IncomingClrDebugState = *m_pClrDebugState; m_pClrDebugState->ViolationMaskReset( ThrowsViolation ); if (m_pClrDebugState->IsFaultForbid() && !(m_pClrDebugState->ViolationMask() & (FaultViolation|FaultNotFatal|BadDebugState))) { gWinWrapperContractRecursionBreak = TRUE; CONTRACT_ASSERT("INJECT_FAULT called in a FAULTFORBID region.", Contract::FAULT_Forbid, Contract::FAULT_Mask, szFunction, szFile, lineNum ); } };
void EEContract::DoChecks(UINT testmask, __in_z const char *szFunction, __in_z char *szFile, int lineNum) { SCAN_IGNORE_THROW; // Tell the static contract analyzer to ignore contract violations SCAN_IGNORE_FAULT; // due to the contract checking logic itself. SCAN_IGNORE_TRIGGER; SCAN_IGNORE_LOCK; SCAN_IGNORE_SO; // Many of the checks below result in calls to GetThread() // that work just fine if GetThread() returns NULL, so temporarily // allow such calls. BEGIN_GETTHREAD_ALLOWED_IN_NO_THROW_REGION; m_pThread = GetThread(); if (m_pThread != NULL) { m_pClrDebugState = m_pThread->GetClrDebugState(); } // Call our base DoChecks. BaseContract::DoChecks(testmask, szFunction, szFile, lineNum); m_testmask = testmask; m_contractStackRecord.m_testmask = testmask; // GC mode check switch (testmask & MODE_Mask) { case MODE_Coop: if (m_pThread == NULL || !m_pThread->PreemptiveGCDisabled()) { // // Check if this is the debugger helper thread and has the runtime // stoppped. If both of these things are true, then we do not care // whether we are in COOP mode or not. // if ((g_pDebugInterface != NULL) && g_pDebugInterface->ThisIsHelperThread() && g_pDebugInterface->IsStopped()) { break; } // Pretend that the threads doing GC are in cooperative mode so that code with // MODE_COOPERATIVE contract works fine on them. if (IsGCThread()) { break; } if (!( (ModeViolation|BadDebugState) & m_pClrDebugState->ViolationMask())) { if (m_pThread == NULL) { CONTRACT_ASSERT("You must have called SetupThread in order to be in GC Cooperative mode.", Contract::MODE_Preempt, Contract::MODE_Mask, m_contractStackRecord.m_szFunction, m_contractStackRecord.m_szFile, m_contractStackRecord.m_lineNum ); } else { CONTRACT_ASSERT("MODE_COOPERATIVE encountered while thread is in preemptive state.", Contract::MODE_Preempt, Contract::MODE_Mask, m_contractStackRecord.m_szFunction, m_contractStackRecord.m_szFile, m_contractStackRecord.m_lineNum ); } } } break; case MODE_Preempt: // Unmanaged threads are considered permanently preemptive so a NULL thread amounts to a passing case here. if (m_pThread != NULL && m_pThread->PreemptiveGCDisabled()) { if (!( (ModeViolation|BadDebugState) & m_pClrDebugState->ViolationMask())) { CONTRACT_ASSERT("MODE_PREEMPTIVE encountered while thread is in cooperative state.", Contract::MODE_Coop, Contract::MODE_Mask, m_contractStackRecord.m_szFunction, m_contractStackRecord.m_szFile, m_contractStackRecord.m_lineNum ); } } break; case MODE_Disabled: // Nothing break; default: UNREACHABLE(); } // GC Trigger check switch (testmask & GC_Mask) { case GC_Triggers: // We don't want to do a full TRIGGERSGC here as this could corrupt // OBJECTREF-typed arguments to the function. { if (m_pClrDebugState->GetGCNoTriggerCount()) { if (!( (GCViolation|BadDebugState) & m_pClrDebugState->ViolationMask())) { CONTRACT_ASSERT("GC_TRIGGERS encountered in a GC_NOTRIGGER scope", Contract::GC_NoTrigger, Contract::GC_Mask, m_contractStackRecord.m_szFunction, m_contractStackRecord.m_szFile, m_contractStackRecord.m_lineNum ); } } } break; case GC_NoTrigger: m_pClrDebugState->ViolationMaskReset( GCViolation ); // Inlined BeginNoTriggerGC m_pClrDebugState->IncrementGCNoTriggerCount(); if (m_pThread && m_pThread->m_fPreemptiveGCDisabled) { m_pClrDebugState->IncrementGCForbidCount(); } break; case GC_Disabled: // Nothing break; default: UNREACHABLE(); } // Host Triggers check switch (testmask & HOST_Mask) { case HOST_Calls: { if (!m_pClrDebugState->IsHostCaller()) { if (!( (HostViolation|BadDebugState) & m_pClrDebugState->ViolationMask())) { // Avoid infinite recursion by temporarily allowing HOST_CALLS // violations so that we don't get contract asserts in anything // called downstream of CONTRACT_ASSERT. If we unwind out of // here, our dtor will reset our state to what it was on entry. CONTRACT_VIOLATION(HostViolation); CONTRACT_ASSERT("HOST_CALLS encountered in a HOST_NOCALLS scope", Contract::HOST_NoCalls, Contract::HOST_Mask, m_contractStackRecord.m_szFunction, m_contractStackRecord.m_szFile, m_contractStackRecord.m_lineNum ); } } } break; case HOST_NoCalls: // m_pClrDebugState->ViolationMaskReset( HostViolation ); m_pClrDebugState->ResetHostCaller(); break; case HOST_Disabled: // Nothing break; default: UNREACHABLE(); } END_GETTHREAD_ALLOWED_IN_NO_THROW_REGION; // EE Thread-required check // NOTE: The following must NOT be inside BEGIN/END_GETTHREAD_ALLOWED, // as the change to m_pClrDebugState->m_allowGetThread below would be // overwritten by END_GETTHREAD_ALLOWED. switch (testmask & EE_THREAD_Mask) { case EE_THREAD_Required: if (!((EEThreadViolation|BadDebugState) & m_pClrDebugState->ViolationMask())) { if (m_pThread == NULL) { CONTRACT_ASSERT("EE_THREAD_REQUIRED encountered with no current EE Thread object in TLS.", Contract::EE_THREAD_Required, Contract::EE_THREAD_Mask, m_contractStackRecord.m_szFunction, m_contractStackRecord.m_szFile, m_contractStackRecord.m_lineNum ); } else if (!m_pClrDebugState->IsGetThreadAllowed()) { // In general, it's unsafe for an EE_THREAD_NOT_REQUIRED function to // call an EE_THREAD_REQUIRED function. In cases where it is safe, // you may wrap the call to the EE_THREAD_REQUIRED function inside a // BEGIN/END_GETTHREAD_ALLOWED block, but you may only do so if the // case where GetThread() == NULL is clearly handled in a way that // prevents entry into the BEGIN/END_GETTHREAD_ALLOWED block. CONTRACT_ASSERT("EE_THREAD_REQUIRED encountered in an EE_THREAD_NOT_REQUIRED scope, without an intervening BEGIN/END_GETTHREAD_ALLOWED block.", Contract::EE_THREAD_Required, Contract::EE_THREAD_Mask, m_contractStackRecord.m_szFunction, m_contractStackRecord.m_szFile, m_contractStackRecord.m_lineNum ); } } m_pClrDebugState->SetGetThreadAllowed(); break; case EE_THREAD_Not_Required: m_pClrDebugState->ResetGetThreadAllowed(); break; case EE_THREAD_Disabled: break; default: UNREACHABLE(); } }