diff --git a/src/coreclr/debug/ee/controller.cpp b/src/coreclr/debug/ee/controller.cpp index 31feaf4a088f7..d48c8e2799dc5 100644 --- a/src/coreclr/debug/ee/controller.cpp +++ b/src/coreclr/debug/ee/controller.cpp @@ -1109,7 +1109,7 @@ void DebuggerController::DisableAll() // thus leaving the patchtable in an inconsistent state such that we may fail trying to walk it. // Since we're exiting anyways, leaving int3 in the code can't harm anybody. // - if (!g_fProcessDetach) + if (!IsAtProcessExit()) { HASHFIND f; for (DebuggerControllerPatch *patch = g_patches->GetFirstPatch(&f); diff --git a/src/coreclr/debug/ee/debugger.cpp b/src/coreclr/debug/ee/debugger.cpp index 32c4fba244f00..e3828e53029b1 100644 --- a/src/coreclr/debug/ee/debugger.cpp +++ b/src/coreclr/debug/ee/debugger.cpp @@ -328,7 +328,7 @@ void Debugger::DoNotCallDirectlyPrivateLock(void) // Lock becomes no-op in late shutdown. - if (g_fProcessDetach) + if (IsAtProcessExit()) { return; } @@ -430,7 +430,7 @@ void Debugger::DoNotCallDirectlyPrivateUnlock(void) // Controller lock is "smaller" than debugger lock. - if (!g_fProcessDetach) + if (!IsAtProcessExit()) { #ifdef _DEBUG if (m_mutexCount == 1) @@ -5035,7 +5035,7 @@ void Debugger::SendSyncCompleteIPCEvent(bool isEESuspendedForGC) PRECONDITION(ThreadHoldsLock()); // Anyone sending the synccomplete must hold the TSL. - PRECONDITION(ThreadStore::HoldingThreadStore() || g_fProcessDetach); + PRECONDITION(ThreadStore::HoldingThreadStore() || IsAtProcessExit()); // The sync complete is now only sent on a helper thread. if (!isEESuspendedForGC) @@ -5069,7 +5069,7 @@ void Debugger::SendSyncCompleteIPCEvent(bool isEESuspendedForGC) // We know we're not on the shutdown thread here. // And we also know we can't block the shutdown thread (b/c it has the TSL and will // get a free pass through the GC toggles that normally block threads for debugging). - if (g_fProcessDetach) + if (IsAtProcessExit()) { STRESS_LOG0(LF_CORDB, LL_INFO10000, "D::SSCIPCE: Skipping for shutdown.\n"); return; @@ -5304,7 +5304,7 @@ void Debugger::TrapAllRuntimeThreads() // If we're doing shutdown, then don't bother trying to communicate w/ the RS. // If we're not the thread doing shutdown, then we may be asynchronously killed by the OS. // If we are the thread in shutdown, don't TART b/c that may block and do complicated stuff. - if (g_fProcessDetach) + if (IsAtProcessExit()) { STRESS_LOG0(LF_CORDB, LL_INFO10000, "D::TART: Skipping for shutdown.\n"); return; @@ -5355,7 +5355,7 @@ void Debugger::TrapAllRuntimeThreads() // That means that our helper is not blocked on starting up, thus we can wait infinite on it. // Thus we don't need to do helper duty if the suspend fails. bool fShouldDoHelperDuty = !m_pRCThread->IsRCThreadReady() && fSuspended; - if (fShouldDoHelperDuty && !g_fProcessDetach) + if (fShouldDoHelperDuty && !IsAtProcessExit()) { // In V1.0, we had the assumption that if the helper thread isn't ready yet, then we're in // a state that SuspendForDebug will succeed on the first try, and thus we'll @@ -9139,7 +9139,7 @@ BOOL Debugger::SuspendComplete(bool isEESuspendedForGC) // If happen on managed thread, it must be doing the helper thread duty. // - _ASSERTE(ThreadStore::HoldingThreadStore() || g_fProcessDetach); + _ASSERTE(ThreadStore::HoldingThreadStore() || IsAtProcessExit()); // We should be holding debugger lock m_mutex. _ASSERTE(ThreadHoldsLock()); @@ -10739,7 +10739,7 @@ bool Debugger::HandleIPCEvent(DebuggerIPCEvent * pEvent) (pThread != NULL) ? GetThreadIdHelper(pThread) : 0, debugState)); - if (!g_fProcessDetach) + if (!IsAtProcessExit()) { g_pEEInterface->SetAllDebugState(pThread, debugState); } @@ -10868,7 +10868,7 @@ bool Debugger::HandleIPCEvent(DebuggerIPCEvent * pEvent) { pIPCResult->hr = CORDBG_E_NOTREADY; } - else if (!g_fProcessDetach) + else if (!IsAtProcessExit()) { // // Since this pointer is coming from the RS, it may be NULL or something @@ -15056,7 +15056,7 @@ HRESULT Debugger::FuncEvalSetup(DebuggerIPCE_FuncEvalInfo *pEvalInfo, if (pThread->m_State & Thread::TS_AbortRequested) return CORDBG_E_FUNC_EVAL_BAD_START_POINT; - if (g_fProcessDetach) + if (IsAtProcessExit()) return CORDBG_E_FUNC_EVAL_BAD_START_POINT; // If there is no guard page on this thread, then we've taken a stack overflow exception and can't run managed @@ -15251,7 +15251,7 @@ Debugger::FuncEvalAbort( "D::FEA: performing UserAbort on thread %#x, id=0x%x\n", pDE->m_thread, GetThreadIdHelper(pDE->m_thread))); - if (!g_fProcessDetach && !pDE->m_completed) + if (!IsAtProcessExit() && !pDE->m_completed) { // // Perform a stop on the thread that the eval is running on. @@ -15317,7 +15317,7 @@ Debugger::FuncEvalRudeAbort( "D::FEA: performing RudeAbort on thread %#x, id=0x%x\n", pDE->m_thread, Debugger::GetThreadIdHelper(pDE->m_thread))); - if (!g_fProcessDetach && !pDE->m_completed) + if (!IsAtProcessExit() && !pDE->m_completed) { // // Perform a stop on the thread that the eval is running on. @@ -15608,7 +15608,7 @@ void Debugger::DisableDebugger(void) * This is called in the case that the loader lock is held and so no new * threads can be spun up to be the helper thread, so the existing thread * must be the helper thread until a new one can spin up. - * This is also called in the shutdown case (g_fProcessDetach==true) and our + * This is also called in the shutdown case (IsAtProcessExit()==true) and our * helper may have already been blown away. ***************************************************************************/ void Debugger::DoHelperThreadDuty() @@ -15628,7 +15628,7 @@ void Debugger::DoHelperThreadDuty() // We'll get killed randomly anyways, so not much we can do. // These assumptions are based off us being called from TART. - _ASSERTE(ThreadStore::HoldingThreadStore() || g_fProcessDetach); // got this from TART + _ASSERTE(ThreadStore::HoldingThreadStore() || IsAtProcessExit()); // got this from TART _ASSERTE(m_trappingRuntimeThreads); // We're only called from TART. _ASSERTE(!m_stopped); // we haven't sent the sync-complete yet. @@ -16179,7 +16179,7 @@ void Debugger::AcquireDebuggerDataLock(Debugger *pDebugger) { WRAPPER_NO_CONTRACT; - if (!g_fProcessDetach) + if (!IsAtProcessExit()) { pDebugger->GetDebuggerDataLock()->Enter(); } @@ -16190,7 +16190,7 @@ void Debugger::ReleaseDebuggerDataLock(Debugger *pDebugger) { WRAPPER_NO_CONTRACT; - if (!g_fProcessDetach) + if (!IsAtProcessExit()) { pDebugger->GetDebuggerDataLock()->Leave(); } diff --git a/src/coreclr/debug/ee/debugger.h b/src/coreclr/debug/ee/debugger.h index fa538f0aed954..48f0e23e6228c 100644 --- a/src/coreclr/debug/ee/debugger.h +++ b/src/coreclr/debug/ee/debugger.h @@ -2492,7 +2492,7 @@ class Debugger : public DebugInterface } CONTRACTL_END; - if (g_fProcessDetach) + if (IsAtProcessExit()) return true; if (g_pEEInterface->GetThread()) diff --git a/src/coreclr/debug/ee/debuggermodule.cpp b/src/coreclr/debug/ee/debuggermodule.cpp index 633567f0dd85f..06b7c7b7f149e 100644 --- a/src/coreclr/debug/ee/debuggermodule.cpp +++ b/src/coreclr/debug/ee/debuggermodule.cpp @@ -113,8 +113,8 @@ DebuggerModuleTable::~DebuggerModuleTable() #ifdef _DEBUG bool DebuggerModuleTable::ThreadHoldsLock() { - // In shutdown (g_fProcessDetach), the shutdown thread implicitly holds all locks. - return g_fProcessDetach || g_pDebugger->HasDebuggerDataLock(); + // In shutdown (IsAtProcessExit()), the shutdown thread implicitly holds all locks. + return IsAtProcessExit() || g_pDebugger->HasDebuggerDataLock(); } #endif diff --git a/src/coreclr/debug/ee/frameinfo.cpp b/src/coreclr/debug/ee/frameinfo.cpp index 371e65c54a95f..f8b3026c2e49f 100644 --- a/src/coreclr/debug/ee/frameinfo.cpp +++ b/src/coreclr/debug/ee/frameinfo.cpp @@ -1899,7 +1899,7 @@ bool ShouldSendUMLeafChain(Thread * pThread) // If we're in shutodown, don't bother trying to sniff for an UM leaf chain. // @todo - we'd like to never even be trying to stack trace on shutdown, this // comes up when we do helper thread duty on shutdown. - if (g_fProcessDetach) + if (IsAtProcessExit()) { return false; } diff --git a/src/coreclr/debug/ee/rcthread.cpp b/src/coreclr/debug/ee/rcthread.cpp index 4d4c60e82a687..2ebc40130b4b3 100644 --- a/src/coreclr/debug/ee/rcthread.cpp +++ b/src/coreclr/debug/ee/rcthread.cpp @@ -858,7 +858,7 @@ void DebuggerRCThread::MainLoop() PRECONDITION(m_thread != NULL); PRECONDITION(ThisIsHelperThreadWorker()); PRECONDITION(IsDbgHelperSpecialThread()); // Can only be called on native debugger helper thread - PRECONDITION((!ThreadStore::HoldingThreadStore()) || g_fProcessDetach); + PRECONDITION((!ThreadStore::HoldingThreadStore()) || IsAtProcessExit()); } CONTRACTL_END; @@ -960,7 +960,7 @@ void DebuggerRCThread::MainLoop() { // If they called continue, then we must have released the TSL. - _ASSERTE(!ThreadStore::HoldingThreadStore() || g_fProcessDetach); + _ASSERTE(!ThreadStore::HoldingThreadStore() || IsAtProcessExit()); // Let's release the lock here since runtime is resumed. debugLockHolderSuspended.Release(); @@ -1062,7 +1062,7 @@ void DebuggerRCThread::MainLoop() // We also hold debugger lock the whole time that Runtime is stopped. We will release the debugger lock // when we receive the Continue event that resumes the runtime. - _ASSERTE(ThreadStore::HoldingThreadStore() || g_fProcessDetach); + _ASSERTE(ThreadStore::HoldingThreadStore() || IsAtProcessExit()); } else { @@ -1107,7 +1107,7 @@ void DebuggerRCThread::TemporaryHelperThreadMainLoop() // It should be holding the debugger lock!!! // PRECONDITION(m_debugger->ThreadHoldsLock()); - PRECONDITION((ThreadStore::HoldingThreadStore()) || g_fProcessDetach); + PRECONDITION((ThreadStore::HoldingThreadStore()) || IsAtProcessExit()); PRECONDITION(ThisIsTempHelperThread()); } CONTRACTL_END; @@ -1177,7 +1177,7 @@ void DebuggerRCThread::TemporaryHelperThreadMainLoop() if (fWasContinue) { // If they called continue, then we must have released the TSL. - _ASSERTE(!ThreadStore::HoldingThreadStore() || g_fProcessDetach); + _ASSERTE(!ThreadStore::HoldingThreadStore() || IsAtProcessExit()); #ifdef _DEBUG // Always reset the syncSpinCount to 0 on a continue so that we have the maximum number of possible @@ -1241,7 +1241,7 @@ void DebuggerRCThread::TemporaryHelperThreadMainLoop() dwWaitTimeout = INFINITE; // Note: we hold the thread store lock now and debugger lock... - _ASSERTE(ThreadStore::HoldingThreadStore() || g_fProcessDetach); + _ASSERTE(ThreadStore::HoldingThreadStore() || IsAtProcessExit()); } } diff --git a/src/coreclr/vm/cachelinealloc.cpp b/src/coreclr/vm/cachelinealloc.cpp index 8ea226ae93764..4c32903c6de39 100644 --- a/src/coreclr/vm/cachelinealloc.cpp +++ b/src/coreclr/vm/cachelinealloc.cpp @@ -67,7 +67,7 @@ CCacheLineAllocator::~CCacheLineAllocator() { if(tempPtr->m_pAddr[i] != NULL) { - if (!g_fProcessDetach) + if (!IsAtProcessExit()) VFree(tempPtr->m_pAddr[i]); } } diff --git a/src/coreclr/vm/ceemain.cpp b/src/coreclr/vm/ceemain.cpp index d7d5257ea1836..c7f19158bdbdb 100644 --- a/src/coreclr/vm/ceemain.cpp +++ b/src/coreclr/vm/ceemain.cpp @@ -396,6 +396,12 @@ BOOL g_singleVersionHosting; typedef BOOL(WINAPI* PINITIALIZECONTEXT2)(PVOID Buffer, DWORD ContextFlags, PCONTEXT* Context, PDWORD ContextLength, ULONG64 XStateCompactionMask); PINITIALIZECONTEXT2 g_pfnInitializeContext2 = NULL; +static BOOLEAN WINAPI RtlDllShutdownInProgressFallback() +{ + return g_fProcessDetach; +} +PRTLDLLSHUTDOWNINPROGRESS g_pfnRtlDllShutdownInProgress = &RtlDllShutdownInProgressFallback; + #ifdef TARGET_X86 typedef VOID(__cdecl* PRTLRESTORECONTEXT)(PCONTEXT ContextRecord, struct _EXCEPTION_RECORD* ExceptionRecord); PRTLRESTORECONTEXT g_pfnRtlRestoreContext = NULL; @@ -406,8 +412,12 @@ void InitializeOptionalWindowsAPIPointers() HMODULE hm = GetModuleHandleW(_T("kernel32.dll")); g_pfnInitializeContext2 = (PINITIALIZECONTEXT2)GetProcAddress(hm, "InitializeContext2"); -#ifdef TARGET_X86 hm = GetModuleHandleW(_T("ntdll.dll")); + PRTLDLLSHUTDOWNINPROGRESS pfn = (PRTLDLLSHUTDOWNINPROGRESS)GetProcAddress(hm, "RtlDllShutdownInProgress"); + if (pfn != NULL) + g_pfnRtlDllShutdownInProgress = pfn; + +#ifdef TARGET_X86 g_pfnRtlRestoreContext = (PRTLRESTORECONTEXT)GetProcAddress(hm, "RtlRestoreContext"); #endif //TARGET_X86 } @@ -1159,11 +1169,6 @@ void STDMETHODCALLTYPE EEShutDownHelper(BOOL fIsDllUnloading) Thread * pThisThread = GetThreadNULLOk(); #endif - // If the process is detaching then set the global state. - // This is used to get around FreeLibrary problems. - if(fIsDllUnloading) - g_fProcessDetach = true; - if (IsDbgHelperSpecialThread()) { // Our debugger helper thread does not allow Thread object to be set up. @@ -1187,7 +1192,7 @@ void STDMETHODCALLTYPE EEShutDownHelper(BOOL fIsDllUnloading) // ungracefully. This check is an attempt to recognize that case // and avoid the impending hang when attempting to get the helper // thread to do things for us. - if ((g_pDebugInterface != NULL) && g_fProcessDetach) + if ((g_pDebugInterface != NULL) && IsAtProcessExit()) g_pDebugInterface->EarlyHelperThreadDeath(); #endif // DEBUGGING_SUPPORTED @@ -1204,7 +1209,7 @@ void STDMETHODCALLTYPE EEShutDownHelper(BOOL fIsDllUnloading) // Indicate the EE is the shut down phase. g_fEEShutDown |= ShutDown_Start; - if (!g_fProcessDetach && !g_fFastExitProcess) + if (!IsAtProcessExit() && !g_fFastExitProcess) { g_fEEShutDown |= ShutDown_Finalize1; @@ -1214,7 +1219,7 @@ void STDMETHODCALLTYPE EEShutDownHelper(BOOL fIsDllUnloading) } // Ok. Let's stop the EE. - if (!g_fProcessDetach) + if (!IsAtProcessExit()) { // Convert key locks into "shutdown" mode. A lock in shutdown mode means: // - Only the finalizer/helper/shutdown threads will be able to take the lock. @@ -1316,7 +1321,7 @@ void STDMETHODCALLTYPE EEShutDownHelper(BOOL fIsDllUnloading) // are already in use, then skip phase 2. This will happen only when those locks // are orphaned. In Vista, the penalty for attempting to enter such locks is // instant process termination. - if (g_fProcessDetach) + if (IsAtProcessExit()) { // The assert below is a bit too aggressive and has generally brought cases that have been race conditions // and not easily reproed to validate a bug. A typical race scenario is when there are two threads, @@ -1382,7 +1387,7 @@ void STDMETHODCALLTYPE EEShutDownHelper(BOOL fIsDllUnloading) EX_END_CATCH(SwallowAllExceptions); ClrFlsClearThreadType(ThreadType_Shutdown); - if (!g_fProcessDetach) + if (!IsAtProcessExit()) { g_pEEShutDownEvent->Set(); } @@ -1478,17 +1483,6 @@ BOOL IsThreadInSTA() // Actual shut down logic is factored out to EEShutDownHelper which may be called // directly by EEShutDown, or indirectly on another thread (see code:#STAShutDown). // -// In order that callees may also know the value of fIsDllUnloading, EEShutDownHelper -// sets g_fProcessDetach = fIsDllUnloading, and g_fProcessDetach may then be retrieved -// via code:IsAtProcessExit. -// -// NOTE 1: Actually, g_fProcessDetach is set to TRUE if fIsDllUnloading is TRUE. But -// g_fProcessDetach doesn't appear to be explicitly set to FALSE. (Apparently -// g_fProcessDetach is implicitly initialized to FALSE as clr.dll is loaded.) -// -// NOTE 2: EEDllMain(DLL_PROCESS_DETACH) already sets g_fProcessDetach to TRUE, so it -// appears EEShutDownHelper doesn't have to. -// void STDMETHODCALLTYPE EEShutDown(BOOL fIsDllUnloading) { CONTRACTL { @@ -1729,25 +1723,13 @@ struct TlsDestructionMonitor thread->m_pFrame = FRAME_TOP; GCX_COOP_NO_DTOR_END(); } -#ifdef _DEBUG - BOOL oldGCOnTransitionsOK = thread->m_GCOnTransitionsOK; - thread->m_GCOnTransitionsOK = FALSE; -#endif - if (!IsAtProcessExit() && !g_fEEShutDown) - { - GCX_COOP_NO_DTOR(); - FreeThreadStaticData(&t_ThreadStatics, thread); - GCX_COOP_NO_DTOR_END(); - } -#ifdef _DEBUG - thread->m_GCOnTransitionsOK = oldGCOnTransitionsOK; -#endif + thread->DetachThread(TRUE); } else { // Since we don't actually cleanup the TLS data along this path, verify that it is already cleaned up - AssertThreadStaticDataFreed(&t_ThreadStatics); + AssertThreadStaticDataFreed(); } ThreadDetaching(); diff --git a/src/coreclr/vm/codeman.cpp b/src/coreclr/vm/codeman.cpp index fc8dfbb2778d1..d019affc33b33 100644 --- a/src/coreclr/vm/codeman.cpp +++ b/src/coreclr/vm/codeman.cpp @@ -3550,7 +3550,7 @@ void ExecutionManager::CleanupCodeHeaps() } CONTRACTL_END; - _ASSERTE (g_fProcessDetach || (GCHeapUtilities::IsGCInProgress() && ::IsGCThread())); + _ASSERTE (IsAtProcessExit() || (GCHeapUtilities::IsGCInProgress() && ::IsGCThread())); GetEEJitManager()->CleanupCodeHeaps(); } @@ -3564,7 +3564,7 @@ void EEJitManager::CleanupCodeHeaps() } CONTRACTL_END; - _ASSERTE (g_fProcessDetach || (GCHeapUtilities::IsGCInProgress() && ::IsGCThread())); + _ASSERTE (IsAtProcessExit() || (GCHeapUtilities::IsGCInProgress() && ::IsGCThread())); // Quick out, don't even take the lock if we have not cleanup to do. // This is important because ETW takes the CodeHeapLock when it is doing diff --git a/src/coreclr/vm/comcache.cpp b/src/coreclr/vm/comcache.cpp index 573ead1a96e35..938e1f57b0ef9 100644 --- a/src/coreclr/vm/comcache.cpp +++ b/src/coreclr/vm/comcache.cpp @@ -504,7 +504,7 @@ VOID IUnkEntry::ReleaseInterface(RCW *pRCW) } CONTRACTL_END; - if (g_fProcessDetach) + if (IsAtProcessExit()) { // The Release call is unsafe if the process is going away (calls into // DLLs we don't know are even mapped). @@ -533,14 +533,14 @@ VOID IUnkEntry::Free() NOTHROW; GC_TRIGGERS; MODE_PREEMPTIVE; - PRECONDITION(g_fProcessDetach || m_pUnknown == (IUnknown *)0xBADF00D); + PRECONDITION(IsAtProcessExit() || m_pUnknown == (IUnknown *)0xBADF00D); } CONTRACTL_END; // Log the de-allocation of the IUnknown entry. LOG((LF_INTEROP, LL_INFO10000, "IUnkEntry::Free called for context 0x%08X, to release entry with m_pUnknown %p, on thread %p\n", m_pCtxCookie, m_pUnknown, GetThreadNULLOk())); - if (g_fProcessDetach) + if (IsAtProcessExit()) { IStream *pOldStream = m_pStream; if (InterlockedExchangeT(&m_pStream, NULL) == pOldStream) @@ -846,7 +846,7 @@ HRESULT IUnkEntry::MarshalIUnknownToStreamCallback2(LPVOID pData) GC_TRIGGERS; MODE_ANY; PRECONDITION(CheckPointer(pData)); - PRECONDITION(g_fProcessDetach == FALSE); + PRECONDITION(IsAtProcessExit() == FALSE); } CONTRACTL_END; @@ -967,7 +967,7 @@ HRESULT IUnkEntry::MarshalIUnknownToStreamCallback(LPVOID pData) GC_TRIGGERS; MODE_ANY; PRECONDITION(CheckPointer(pData)); - PRECONDITION(g_fProcessDetach == FALSE); + PRECONDITION(IsAtProcessExit() == FALSE); } CONTRACTL_END; @@ -1199,7 +1199,7 @@ CtxEntry::~CtxEntry() CONTRACTL_END; // If the context is a valid context then release it. - if (m_pObjCtx && !g_fProcessDetach) + if (m_pObjCtx && !IsAtProcessExit()) { SafeRelease(m_pObjCtx); m_pObjCtx = NULL; @@ -1304,7 +1304,7 @@ HRESULT CtxEntry::EnterContext(PFNCTXCALLBACK pCallbackFunc, LPVOID pData) // If we are in process detach, we cannot safely try to enter another context // since we don't know if OLE32 is still loaded. - if (g_fProcessDetach) + if (IsAtProcessExit()) { LOG((LF_INTEROP, LL_INFO100, "Entering into context 0x08%x has failed since we are in process detach\n", m_pCtxCookie)); return RPC_E_DISCONNECTED; diff --git a/src/coreclr/vm/comcallablewrapper.cpp b/src/coreclr/vm/comcallablewrapper.cpp index b80c2442c9097..9cefbdfaa5921 100644 --- a/src/coreclr/vm/comcallablewrapper.cpp +++ b/src/coreclr/vm/comcallablewrapper.cpp @@ -3103,7 +3103,7 @@ void ComMethodTable::Cleanup() if (m_pDispatchInfo) delete m_pDispatchInfo; - if (m_pITypeInfo && !g_fProcessDetach) + if (m_pITypeInfo && !IsAtProcessExit()) SafeRelease(m_pITypeInfo); // The m_pMDescr and the current instance is allocated from the related LoaderAllocator diff --git a/src/coreclr/vm/crst.cpp b/src/coreclr/vm/crst.cpp index 3eacfc29f76a1..c313a710517e4 100644 --- a/src/coreclr/vm/crst.cpp +++ b/src/coreclr/vm/crst.cpp @@ -392,7 +392,7 @@ void CrstBase::PreEnter() STATIC_CONTRACT_GC_NOTRIGGER; // Are we in the shutdown sequence and in phase 2 of it? - if (g_fProcessDetach && (g_fEEShutDown & ShutDown_Phase2)) + if (IsAtProcessExit() && (g_fEEShutDown & ShutDown_Phase2)) { // Ensure that this lock has been flagged to be taken during shutdown _ASSERTE_MSG(CanBeTakenDuringShutdown(), "Attempting to take a lock at shutdown that is not CRST_TAKEN_DURING_SHUTDOWN"); @@ -569,7 +569,7 @@ void CrstBase::PreLeave() } // Are we in the shutdown sequence and in phase 2 of it? - if (g_fProcessDetach && (g_fEEShutDown & ShutDown_Phase2)) + if (IsAtProcessExit() && (g_fEEShutDown & ShutDown_Phase2)) { // Ensure that this lock has been flagged to be taken during shutdown _ASSERTE_MSG(CanBeTakenDuringShutdown(), "Attempting to leave a lock at shutdown that is not CRST_TAKEN_DURING_SHUTDOWN"); diff --git a/src/coreclr/vm/crst.h b/src/coreclr/vm/crst.h index 5928812dc49f5..65769569c4bea 100644 --- a/src/coreclr/vm/crst.h +++ b/src/coreclr/vm/crst.h @@ -97,10 +97,6 @@ #define ShutDown_IUnknown 0x00000040 #define ShutDown_Phase2 0x00000080 -#ifndef DACCESS_COMPILE -extern bool g_fProcessDetach; -extern DWORD g_fEEShutDown; -#endif // Total count of Crst lock of the type (Shutdown) that are currently in use extern Volatile g_ShutdownCrstUsageCount; diff --git a/src/coreclr/vm/eventing/eventpipe/ep-rt-coreclr.h b/src/coreclr/vm/eventing/eventpipe/ep-rt-coreclr.h index 95c568f50acbf..7f66adf7df631 100644 --- a/src/coreclr/vm/eventing/eventpipe/ep-rt-coreclr.h +++ b/src/coreclr/vm/eventing/eventpipe/ep-rt-coreclr.h @@ -725,7 +725,7 @@ bool ep_rt_process_detach (void) { STATIC_CONTRACT_NOTHROW; - return (bool)g_fProcessDetach; + return (bool)IsAtProcessExit(); } static diff --git a/src/coreclr/vm/interoputil.cpp b/src/coreclr/vm/interoputil.cpp index 587b5a0e5f36f..5b9224ce041f4 100644 --- a/src/coreclr/vm/interoputil.cpp +++ b/src/coreclr/vm/interoputil.cpp @@ -1196,7 +1196,7 @@ void MinorCleanupSyncBlockComData(InteropSyncBlockInfo* pInteropInfo) NOTHROW; GC_NOTRIGGER; MODE_ANY; - PRECONDITION( GCHeapUtilities::IsGCInProgress() || ( (g_fEEShutDown & ShutDown_SyncBlock) && g_fProcessDetach ) ); + PRECONDITION( GCHeapUtilities::IsGCInProgress() || ( (g_fEEShutDown & ShutDown_SyncBlock) && IsAtProcessExit() ) ); } CONTRACTL_END; @@ -1225,7 +1225,7 @@ void CleanupSyncBlockComData(InteropSyncBlockInfo* pInteropInfo) } CONTRACTL_END; - if ((g_fEEShutDown & ShutDown_SyncBlock) && g_fProcessDetach ) + if ((g_fEEShutDown & ShutDown_SyncBlock) && IsAtProcessExit() ) MinorCleanupSyncBlockComData(pInteropInfo); #ifdef FEATURE_COMINTEROP_UNMANAGED_ACTIVATION diff --git a/src/coreclr/vm/runtimecallablewrapper.cpp b/src/coreclr/vm/runtimecallablewrapper.cpp index aa28bfa6d8376..21fa3b8879f5b 100644 --- a/src/coreclr/vm/runtimecallablewrapper.cpp +++ b/src/coreclr/vm/runtimecallablewrapper.cpp @@ -1709,7 +1709,7 @@ void RCW::MinorCleanup() NOTHROW; GC_NOTRIGGER; MODE_ANY; - PRECONDITION(GCHeapUtilities::IsGCInProgress() || ( (g_fEEShutDown & ShutDown_SyncBlock) && g_fProcessDetach )); + PRECONDITION(GCHeapUtilities::IsGCInProgress() || ( (g_fEEShutDown & ShutDown_SyncBlock) && IsAtProcessExit() )); } CONTRACTL_END; diff --git a/src/coreclr/vm/stdinterfaces.cpp b/src/coreclr/vm/stdinterfaces.cpp index 3131f33d2892b..64a337e829ee7 100644 --- a/src/coreclr/vm/stdinterfaces.cpp +++ b/src/coreclr/vm/stdinterfaces.cpp @@ -222,7 +222,7 @@ Unknown_AddRef_Internal(IUnknown* pUnk) if (pSimpleWrap && (pOuter = pSimpleWrap->GetOuter()) != NULL) { // If we are in process detach, we cannot safely call release on our outer. - if (g_fProcessDetach) + if (IsAtProcessExit()) return 1; ULONG cbRef = pOuter->AddRef(); @@ -284,7 +284,7 @@ Unknown_Release_Internal(IUnknown* pUnk) if (pSimpleWrap && (pOuter = pSimpleWrap->GetOuter()) != NULL) { // If we are in process detach, we cannot safely call release on our outer. - if (g_fProcessDetach) + if (IsAtProcessExit()) cbRef = 1; cbRef = SafeReleasePreemp(pOuter); diff --git a/src/coreclr/vm/stringliteralmap.cpp b/src/coreclr/vm/stringliteralmap.cpp index 34f4eecea5015..168ab5503f4ca 100644 --- a/src/coreclr/vm/stringliteralmap.cpp +++ b/src/coreclr/vm/stringliteralmap.cpp @@ -341,7 +341,7 @@ GlobalStringLiteralMap::~GlobalStringLiteralMap() { // We are shutting down, the OS will reclaim the memory from the StringLiteralEntries, // m_MemoryPool and m_StringToEntryHashTable. - _ASSERTE(g_fProcessDetach); + _ASSERTE(IsAtProcessExit()); } } diff --git a/src/coreclr/vm/syncclean.cpp b/src/coreclr/vm/syncclean.cpp index 142417ce1d12c..7247529da56cd 100644 --- a/src/coreclr/vm/syncclean.cpp +++ b/src/coreclr/vm/syncclean.cpp @@ -66,7 +66,7 @@ void SyncClean::CleanUp () LIMITED_METHOD_CONTRACT; // Only GC thread can call this. - _ASSERTE (g_fProcessDetach || + _ASSERTE (IsAtProcessExit() || IsGCSpecialThread() || (GCHeapUtilities::IsGCInProgress() && GetThreadNULLOk() == ThreadSuspend::GetSuspensionThread())); if (m_HashMap) diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index abd6bac8050f9..2c8e9668a63d7 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -854,22 +854,6 @@ void DestroyThread(Thread *th) th->UnmarkThreadForAbort(); } - // Clear any outstanding stale EH state that maybe still active on the thread. -#ifdef FEATURE_EH_FUNCLETS - ExceptionTracker::PopTrackers((void*)-1); -#else // !FEATURE_EH_FUNCLETS -#ifdef TARGET_X86 - PTR_ThreadExceptionState pExState = th->GetExceptionState(); - if (pExState->IsExceptionInProgress()) - { - GCX_COOP(); - pExState->GetCurrentExceptionTracker()->UnwindExInfo((void *)-1); - } -#else // !TARGET_X86 -#error Unsupported platform -#endif // TARGET_X86 -#endif // FEATURE_EH_FUNCLETS - if (g_fEEShutDown == 0) { th->SetThreadState(Thread::TS_ReportDead); @@ -890,22 +874,6 @@ HRESULT Thread::DetachThread(BOOL fDLLThreadDetach) STATIC_CONTRACT_NOTHROW; STATIC_CONTRACT_GC_NOTRIGGER; - // Clear any outstanding stale EH state that maybe still active on the thread. -#ifdef FEATURE_EH_FUNCLETS - ExceptionTracker::PopTrackers((void*)-1); -#else // !FEATURE_EH_FUNCLETS -#ifdef TARGET_X86 - PTR_ThreadExceptionState pExState = GetExceptionState(); - if (pExState->IsExceptionInProgress()) - { - GCX_COOP(); - pExState->GetCurrentExceptionTracker()->UnwindExInfo((void *)-1); - } -#else // !TARGET_X86 -#error Unsupported platform -#endif // TARGET_X86 -#endif // FEATURE_EH_FUNCLETS - #ifdef FEATURE_COMINTEROP IErrorInfo *pErrorInfo; // Avoid calling GetErrorInfo() if ole32 has already executed the DLL_THREAD_DETACH, @@ -962,19 +930,7 @@ HRESULT Thread::DetachThread(BOOL fDLLThreadDetach) m_ThreadHandleForClose = hThread; } - if (GCHeapUtilities::IsGCHeapInitialized()) - { - // If the GC heap is initialized, we need to fix the alloc context for this detaching thread. - GCX_COOP(); - // GetTotalAllocatedBytes reads dead_threads_non_alloc_bytes, but will suspend EE, being in COOP mode we cannot race with that - // however, there could be other threads terminating and doing the same Add. - InterlockedExchangeAdd64((LONG64*)&dead_threads_non_alloc_bytes, t_runtime_thread_locals.alloc_context.alloc_limit - t_runtime_thread_locals.alloc_context.alloc_ptr); - GCHeapUtilities::GetGCHeap()->FixAllocContext(&t_runtime_thread_locals.alloc_context, NULL, NULL); - t_runtime_thread_locals.alloc_context.init(); // re-initialize the context. - - // Clear out the alloc context pointer for this thread. When TLS is gone, this pointer will point into freed memory. - m_pRuntimeThreadLocals = nullptr; - } + CooperativeCleanup(); // We need to make sure that TLS are touched last here. SetThread(NULL); @@ -2774,6 +2730,52 @@ void Thread::CleanupCOMState() } #endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT +// Thread cleanup that must be run in cooperative mode before the thread is destroyed. +void Thread::CooperativeCleanup() +{ + CONTRACTL { + NOTHROW; + GC_TRIGGERS; + } + CONTRACTL_END; + + // Skip the cleanup during process exit. Switch to cooperative mode can deadlock during process exit on Windows. + if (IsAtProcessExit()) + return; + + GCX_COOP(); + + // Clear any outstanding stale EH state that maybe still active on the thread. +#ifdef FEATURE_EH_FUNCLETS + ExceptionTracker::PopTrackers((void*)-1); +#else // !FEATURE_EH_FUNCLETS + PTR_ThreadExceptionState pExState = GetExceptionState(); + if (pExState->IsExceptionInProgress()) + { + pExState->GetCurrentExceptionTracker()->UnwindExInfo((void *)-1); + } +#endif // FEATURE_EH_FUNCLETS + + if (m_ThreadLocalDataPtr != NULL) + { + FreeThreadStaticData(this); + m_ThreadLocalDataPtr = NULL; + } + + if (GCHeapUtilities::IsGCHeapInitialized()) + { + // If the GC heap is initialized, we need to fix the alloc context for this detaching thread. + // GetTotalAllocatedBytes reads dead_threads_non_alloc_bytes, but will suspend EE, being in COOP mode we cannot race with that + // however, there could be other threads terminating and doing the same Add. + InterlockedExchangeAdd64((LONG64*)&dead_threads_non_alloc_bytes, t_runtime_thread_locals.alloc_context.alloc_limit - t_runtime_thread_locals.alloc_context.alloc_ptr); + GCHeapUtilities::GetGCHeap()->FixAllocContext(&t_runtime_thread_locals.alloc_context, NULL, NULL); + t_runtime_thread_locals.alloc_context.init(); // re-initialize the context. + + // Clear out the alloc context pointer for this thread. When TLS is gone, this pointer will point into freed memory. + m_pRuntimeThreadLocals = nullptr; + } +} + // See general comments on thread destruction (code:#threadDestruction) above. void Thread::OnThreadTerminate(BOOL holdingLock) { @@ -2809,6 +2811,11 @@ void Thread::OnThreadTerminate(BOOL holdingLock) } #endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT + if (this == GetThreadNULLOk()) + { + CooperativeCleanup(); + } + if (g_fEEShutDown != 0) { // We have started shutdown. Not safe to touch CLR state. @@ -2836,24 +2843,8 @@ void Thread::OnThreadTerminate(BOOL holdingLock) // Destroy the LastThrown handle (and anything that violates the above assert). SafeSetThrowables(NULL); - // Free all structures related to thread statics for this thread - DeleteThreadStaticData(); - - } - - if (GCHeapUtilities::IsGCHeapInitialized()) - { - // Guaranteed to NOT be a shutdown case, because we tear down the heap before - // we tear down any threads during shutdown. - if (ThisThreadID == CurrentThreadID && GetAllocContext() != nullptr) - { - GCX_COOP(); - // GetTotalAllocatedBytes reads dead_threads_non_alloc_bytes, but will suspend EE, being in COOP mode we cannot race with that - // however, there could be other threads terminating and doing the same Add. - InterlockedExchangeAdd64((LONG64*)&dead_threads_non_alloc_bytes, GetAllocContext()->alloc_limit - GetAllocContext()->alloc_ptr); - GCHeapUtilities::GetGCHeap()->FixAllocContext(GetAllocContext(), NULL, NULL); - GetAllocContext()->init(); // re-initialize the context. - } + // Free loader allocator structures related to this thread + FreeLoaderAllocatorHandlesForTLSData(this); } // We switch a thread to dead when it has finished doing useful work. But it @@ -2950,14 +2941,6 @@ void Thread::OnThreadTerminate(BOOL holdingLock) // If nobody else is holding onto the thread, we may destruct it here: ULONG oldCount = DecExternalCount(TRUE); - // If we are shutting down the process, we only have one thread active in the - // system. So we can disregard all the reasons that hold this thread alive -- - // TLS is about to be reclaimed anyway. - if (IsAtProcessExit()) - while (oldCount > 0) - { - oldCount = DecExternalCount(TRUE); - } // ASSUME THAT THE THREAD IS DELETED, FROM HERE ON @@ -7590,32 +7573,6 @@ Frame * Thread::NotifyFrameChainOfExceptionUnwind(Frame* pStartFrame, LPVOID pvL return pFrame; } -//+---------------------------------------------------------------------------- -// -// Method: Thread::DeleteThreadStaticData private -// -// Synopsis: Delete the static data for each appdomain that this thread -// visited. -// -// -//+---------------------------------------------------------------------------- - -void Thread::DeleteThreadStaticData() -{ - CONTRACTL { - NOTHROW; - GC_NOTRIGGER; - MODE_COOPERATIVE; - } - CONTRACTL_END; - - FreeLoaderAllocatorHandlesForTLSData(this); - if (!IsAtProcessExit() && !g_fEEShutDown) - { - FreeThreadStaticData(m_ThreadLocalDataPtr, this); - } -} - OBJECTREF Thread::GetCulture(BOOL bUICulture) { CONTRACTL { diff --git a/src/coreclr/vm/threads.h b/src/coreclr/vm/threads.h index c1ee8e7585bbf..8e381da04d534 100644 --- a/src/coreclr/vm/threads.h +++ b/src/coreclr/vm/threads.h @@ -1180,6 +1180,8 @@ class Thread void BaseWinRTUninitialize(); #endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT + void CooperativeCleanup(); + void OnThreadTerminate(BOOL holdingLock); static void CleanupDetachedThreads(); @@ -3339,12 +3341,6 @@ class Thread SpinLock m_TlsSpinLock; PTR_ThreadLocalData GetThreadLocalDataPtr() { LIMITED_METHOD_DAC_CONTRACT; return m_ThreadLocalDataPtr; } -public: - - // Called during Thread death to clean up all structures - // associated with thread statics - void DeleteThreadStaticData(); - private: TailCallTls m_tailCallTls; diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp index 23c38f6581863..0fd8f389f1a7d 100644 --- a/src/coreclr/vm/threadstatics.cpp +++ b/src/coreclr/vm/threadstatics.cpp @@ -408,22 +408,21 @@ void FreeLoaderAllocatorHandlesForTLSData(Thread *pThread) } } -void AssertThreadStaticDataFreed(ThreadLocalData *pThreadLocalData) +void AssertThreadStaticDataFreed() { LIMITED_METHOD_CONTRACT; - if (!IsAtProcessExit() && !g_fEEShutDown) - { - _ASSERTE(pThreadLocalData->pThread == NULL); - _ASSERTE(pThreadLocalData->pCollectibleTlsArrayData == NULL); - _ASSERTE(pThreadLocalData->cCollectibleTlsData == 0); - _ASSERTE(pThreadLocalData->pNonCollectibleTlsArrayData == NULL); - _ASSERTE(pThreadLocalData->cNonCollectibleTlsData == 0); - _ASSERTE(pThreadLocalData->pInFlightData == NULL); - } + ThreadLocalData *pThreadLocalData = &t_ThreadStatics; + + _ASSERTE(pThreadLocalData->pThread == NULL); + _ASSERTE(pThreadLocalData->pCollectibleTlsArrayData == NULL); + _ASSERTE(pThreadLocalData->cCollectibleTlsData == 0); + _ASSERTE(pThreadLocalData->pNonCollectibleTlsArrayData == NULL); + _ASSERTE(pThreadLocalData->cNonCollectibleTlsData == 0); + _ASSERTE(pThreadLocalData->pInFlightData == NULL); } -void FreeThreadStaticData(ThreadLocalData *pThreadLocalData, Thread* pThread) +void FreeThreadStaticData(Thread* pThread) { CONTRACTL { NOTHROW; @@ -432,47 +431,34 @@ void FreeThreadStaticData(ThreadLocalData *pThreadLocalData, Thread* pThread) } CONTRACTL_END; - if (pThreadLocalData == NULL) - return; - - if (!IsAtProcessExit() && !g_fEEShutDown) - { - SpinLockHolder spinLock(&pThread->m_TlsSpinLock); - - if (pThreadLocalData->pThread == NULL) - { - return; - } - - pThreadLocalData = pThread->m_ThreadLocalDataPtr; + SpinLockHolder spinLock(&pThread->m_TlsSpinLock); - if (pThreadLocalData == NULL) - return; + ThreadLocalData *pThreadLocalData = &t_ThreadStatics; - for (int32_t iTlsSlot = 0; iTlsSlot < pThreadLocalData->cCollectibleTlsData; ++iTlsSlot) + for (int32_t iTlsSlot = 0; iTlsSlot < pThreadLocalData->cCollectibleTlsData; ++iTlsSlot) + { + if (!IsHandleNullUnchecked(pThreadLocalData->pCollectibleTlsArrayData[iTlsSlot])) { - if (!IsHandleNullUnchecked(pThreadLocalData->pCollectibleTlsArrayData[iTlsSlot])) - { - DestroyLongWeakHandle(pThreadLocalData->pCollectibleTlsArrayData[iTlsSlot]); - } + DestroyLongWeakHandle(pThreadLocalData->pCollectibleTlsArrayData[iTlsSlot]); } + } - delete[] (uint8_t*)pThreadLocalData->pCollectibleTlsArrayData; + delete[] (uint8_t*)pThreadLocalData->pCollectibleTlsArrayData; - pThreadLocalData->pCollectibleTlsArrayData = 0; - pThreadLocalData->cCollectibleTlsData = 0; - pThreadLocalData->pNonCollectibleTlsArrayData = 0; - pThreadLocalData->cNonCollectibleTlsData = 0; + pThreadLocalData->pCollectibleTlsArrayData = 0; + pThreadLocalData->cCollectibleTlsData = 0; + pThreadLocalData->pNonCollectibleTlsArrayData = 0; + pThreadLocalData->cNonCollectibleTlsData = 0; - while (pThreadLocalData->pInFlightData != NULL) - { - InFlightTLSData* pInFlightData = pThreadLocalData->pInFlightData; - pThreadLocalData->pInFlightData = pInFlightData->pNext; - delete pInFlightData; - } - pThreadLocalData->pThread->m_ThreadLocalDataPtr = NULL; - VolatileStoreWithoutBarrier(&pThreadLocalData->pThread, (Thread*)NULL); + while (pThreadLocalData->pInFlightData != NULL) + { + InFlightTLSData* pInFlightData = pThreadLocalData->pInFlightData; + pThreadLocalData->pInFlightData = pInFlightData->pNext; + delete pInFlightData; } + + _ASSERTE(pThreadLocalData->pThread == pThread); + pThreadLocalData->pThread = NULL; } void SetTLSBaseValue(TADDR *ppTLSBaseAddress, TADDR pTLSBaseAddress, bool useGCBarrierInsteadOfHandleStore) diff --git a/src/coreclr/vm/threadstatics.h b/src/coreclr/vm/threadstatics.h index 0422629e05f9c..3ab3806cbf952 100644 --- a/src/coreclr/vm/threadstatics.h +++ b/src/coreclr/vm/threadstatics.h @@ -329,8 +329,8 @@ PTR_MethodTable LookupMethodTableForThreadStaticKnownToBeAllocated(TLSIndex inde void InitializeThreadStaticData(); void InitializeCurrentThreadsStaticData(Thread* pThread); void FreeLoaderAllocatorHandlesForTLSData(Thread* pThread); -void FreeThreadStaticData(ThreadLocalData *pThreadLocalData, Thread* pThread); -void AssertThreadStaticDataFreed(ThreadLocalData *pThreadLocalData); +void FreeThreadStaticData(Thread* pThread); +void AssertThreadStaticDataFreed(); void GetTLSIndexForThreadStatic(MethodTable* pMT, bool gcStatic, TLSIndex* pIndex, uint32_t bytesNeeded); void FreeTLSIndicesForLoaderAllocator(LoaderAllocator *pLoaderAllocator); void* GetThreadLocalStaticBase(TLSIndex index); diff --git a/src/coreclr/vm/threadsuspend.cpp b/src/coreclr/vm/threadsuspend.cpp index 4fd77e488a249..9cdb868998433 100644 --- a/src/coreclr/vm/threadsuspend.cpp +++ b/src/coreclr/vm/threadsuspend.cpp @@ -5543,7 +5543,7 @@ void ThreadSuspend::SuspendEE(SUSPEND_REASON reason) // set tls flags for compat with SOS ClrFlsSetThreadType(ThreadType_DynamicSuspendEE); - _ASSERTE(ThreadStore::HoldingThreadStore() || g_fProcessDetach); + _ASSERTE(ThreadStore::HoldingThreadStore() || IsAtProcessExit()); #ifdef PROFILING_SUPPORTED // If the profiler desires information about GCs, then let it know that one diff --git a/src/coreclr/vm/vars.hpp b/src/coreclr/vm/vars.hpp index 30e9e60d69155..5aa48135ae30e 100644 --- a/src/coreclr/vm/vars.hpp +++ b/src/coreclr/vm/vars.hpp @@ -459,12 +459,24 @@ GVAL_DECL(bool, g_metadataUpdatesApplied); #endif EXTERN bool g_fManagedAttach; +#ifdef HOST_WINDOWS +typedef BOOLEAN (WINAPI* PRTLDLLSHUTDOWNINPROGRESS)(); +EXTERN PRTLDLLSHUTDOWNINPROGRESS g_pfnRtlDllShutdownInProgress; +#endif + // Indicates whether we're executing shut down as a result of DllMain // (DLL_PROCESS_DETACH). See comments at code:EEShutDown for details. -inline BOOL IsAtProcessExit() +inline bool IsAtProcessExit() { SUPPORTS_DAC; +#if defined(DACCESS_COMPILE) || !defined(HOST_WINDOWS) return g_fProcessDetach; +#else + // RtlDllShutdownInProgress provides more accurace information about whether the process is shutting down. + // Use it if it is available to avoid shutdown deadlocks. + // https://learn.microsoft.com/windows/win32/devnotes/rtldllshutdowninprogress + return g_pfnRtlDllShutdownInProgress(); +#endif } enum FWStatus