diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index 69cb6c2c668cb..a9242a804555e 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -49310,7 +49310,29 @@ bool GCHeap::StressHeap(gc_alloc_context * context) } Interlocked::Decrement(&OneAtATime); #endif // !MULTIPLE_HEAPS - if (IsConcurrentGCEnabled()) + + if (g_pConfig->GetGCStressLevel() & EEConfig::GCSTRESS_INSTR_JIT) + { + // When GCSTRESS_INSTR_JIT is set we see lots of GCs - on every GC-eligible instruction. + // We do not want all these GC to be gen2 because: + // - doing only or mostly gen2 is very expensive in this mode + // - doing only or mostly gen2 prevents coverage of generation-aware behaviors + // - the main value of this stress mode is to catch stack scanning issues at various/rare locations + // in the code and gen2 is not needed for that. + + int rgen = StressRNG(100); + + // gen0:gen1:gen2 distribution: 90:8:2 + if (rgen >= 98) + rgen = 2; + else if (rgen >= 90) + rgen = 1; + else + rgen = 0; + + GarbageCollectTry (rgen, FALSE, collection_gcstress); + } + else if (IsConcurrentGCEnabled()) { int rgen = StressRNG(10); @@ -49319,7 +49341,7 @@ bool GCHeap::StressHeap(gc_alloc_context * context) rgen = 2; else if (rgen >= 4) rgen = 1; - else + else rgen = 0; GarbageCollectTry (rgen, FALSE, collection_gcstress); diff --git a/src/coreclr/vm/eeconfig.cpp b/src/coreclr/vm/eeconfig.cpp index 29017c18d3c74..7280a9d441cec 100644 --- a/src/coreclr/vm/eeconfig.cpp +++ b/src/coreclr/vm/eeconfig.cpp @@ -27,7 +27,7 @@ using namespace clr; // is relied on by the EH code and the JIT code (for handling patched // managed code, and GC stress exception) after GC stress is dynamically // turned off. -Volatile GCStressPolicy::InhibitHolder::s_nGcStressDisabled = 0; +int GCStressPolicy::InhibitHolder::s_nGcStressDisabled = 0; #endif // STRESS_HEAP /**************************************************************/ diff --git a/src/coreclr/vm/finalizerthread.cpp b/src/coreclr/vm/finalizerthread.cpp index b3d16dbf37dc3..e543a3c60c346 100644 --- a/src/coreclr/vm/finalizerthread.cpp +++ b/src/coreclr/vm/finalizerthread.cpp @@ -569,6 +569,17 @@ void FinalizerThread::FinalizerThreadWait() EnableFinalization(); - hEventFinalizerDone->Wait(INFINITE,TRUE); + // Under GC stress the finalizer queue may never go empty as frequent + // GCs will keep filling up the queue with items. + // We will disable GC stress to make sure the current thread is not permanently blocked on that. + GCStressPolicy::InhibitHolder iholder; + + //---------------------------------------------------- + // Do appropriate wait and pump messages if necessary + //---------------------------------------------------- + + DWORD status; + status = hEventFinalizerDone->Wait(INFINITE,TRUE); + _ASSERTE(status == WAIT_OBJECT_0); } } diff --git a/src/coreclr/vm/gccover.cpp b/src/coreclr/vm/gccover.cpp index a0a6c2f1576db..b7ae97613d507 100644 --- a/src/coreclr/vm/gccover.cpp +++ b/src/coreclr/vm/gccover.cpp @@ -1417,16 +1417,9 @@ BOOL OnGcCoverageInterrupt(PCONTEXT regs) RemoveGcCoverageInterrupt(instrPtr, savedInstrPtr, gcCover, offset); return TRUE; } - - // If the thread is in preemptive mode then we must be in a - // PInvoke stub, a method that has an inline PInvoke frame, - // or be in a reverse PInvoke stub that's about to return. - // - // The PInvoke cases should should properly report GC refs if we - // trigger GC here. But a reverse PInvoke stub may over-report - // leading to spurious failures, as we would not normally report - // anything for this method at this point. - if (!pThread->PreemptiveGCDisabled() && pMD->HasUnmanagedCallersOnlyAttribute()) + + // The thread is in preemptive mode. Normally, it should not be able to trigger GC. + if (!pThread->PreemptiveGCDisabled()) { RemoveGcCoverageInterrupt(instrPtr, savedInstrPtr, gcCover, offset); return TRUE; diff --git a/src/coreclr/vm/gcstress.h b/src/coreclr/vm/gcstress.h index 8fe0b962007fe..23b11d9989fcf 100644 --- a/src/coreclr/vm/gcstress.h +++ b/src/coreclr/vm/gcstress.h @@ -72,13 +72,22 @@ namespace GCStressPolicy private: // This static controls whether GC stress may induce GCs. EEConfig::GetGCStressLevel() still // controls when GCs may occur. - static Volatile s_nGcStressDisabled; - + static int s_nGcStressDisabled; bool m_bAcquired; + FORCEINLINE static void Disable() + { Interlocked::Increment(&InhibitHolder::s_nGcStressDisabled); } + + FORCEINLINE static void Enable() + { + int newVal; + newVal = Interlocked::Decrement(&InhibitHolder::s_nGcStressDisabled); + _ASSERTE(newVal >= 0); + } + public: InhibitHolder() - { LIMITED_METHOD_CONTRACT; ++s_nGcStressDisabled; m_bAcquired = true; } + { LIMITED_METHOD_CONTRACT; Disable(); m_bAcquired = true;} ~InhibitHolder() { LIMITED_METHOD_CONTRACT; Release(); } @@ -88,7 +97,7 @@ namespace GCStressPolicy LIMITED_METHOD_CONTRACT; if (m_bAcquired) { - --s_nGcStressDisabled; + Enable(); m_bAcquired = false; } } @@ -99,13 +108,13 @@ namespace GCStressPolicy } UNUSED_ATTR; FORCEINLINE bool IsEnabled() - { return InhibitHolder::s_nGcStressDisabled == 0U; } + { return VolatileLoadWithoutBarrier(&InhibitHolder::s_nGcStressDisabled) == 0; } FORCEINLINE void GlobalDisable() - { ++InhibitHolder::s_nGcStressDisabled; } + { InhibitHolder::Disable(); } FORCEINLINE void GlobalEnable() - { --InhibitHolder::s_nGcStressDisabled; } + { InhibitHolder::Enable(); } #else // STRESS_HEAP diff --git a/src/tests/baseservices/RuntimeConfiguration/TestConfigTester.csproj b/src/tests/baseservices/RuntimeConfiguration/TestConfigTester.csproj index cd6cb09525e45..48615f4ec48a9 100644 --- a/src/tests/baseservices/RuntimeConfiguration/TestConfigTester.csproj +++ b/src/tests/baseservices/RuntimeConfiguration/TestConfigTester.csproj @@ -1,5 +1,7 @@ + + true true true