From 8913aaa1388e263d537b19e78caaed6b124fc0ea Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Sat, 13 Apr 2019 08:09:31 -0700 Subject: Delete COMPlus_FinalizeOnShutdown (#23595) This compat quirk is increasingly more broken since the framework is generally not compatible with, and we have not heard anybody actually using it. Changed Environment.HasShutdownStarted to unconditionally return false. It does not make sense for it to ever return true with shutdown finalization disabled. --- src/vm/appdomain.cpp | 4 - src/vm/ceemain.cpp | 157 +----------------- src/vm/ecalllist.h | 1 - src/vm/eepolicy.cpp | 70 -------- src/vm/excep.cpp | 11 -- src/vm/finalizerthread.cpp | 405 +-------------------------------------------- src/vm/finalizerthread.h | 19 ++- src/vm/frames.cpp | 8 - src/vm/frames.h | 1 - src/vm/threads.cpp | 6 - src/vm/threadsuspend.cpp | 50 +----- src/vm/vars.hpp | 4 - src/vm/win32threadpool.cpp | 7 +- 13 files changed, 22 insertions(+), 721 deletions(-) (limited to 'src/vm') diff --git a/src/vm/appdomain.cpp b/src/vm/appdomain.cpp index abb9ac15d6..9ac4478a1d 100644 --- a/src/vm/appdomain.cpp +++ b/src/vm/appdomain.cpp @@ -3207,11 +3207,7 @@ void AppDomain::Stop() #ifdef DEBUGGING_SUPPORTED if (IsDebuggerAttached()) NotifyDebuggerUnload(); -#endif // DEBUGGING_SUPPORTED - - m_pRootAssembly = NULL; // This assembly is in the assembly list; -#ifdef DEBUGGING_SUPPORTED if (NULL != g_pDebugInterface) { // Call the publisher API to delete this appdomain entry from the list diff --git a/src/vm/ceemain.cpp b/src/vm/ceemain.cpp index 9ac0cc6a71..3ca3356717 100644 --- a/src/vm/ceemain.cpp +++ b/src/vm/ceemain.cpp @@ -257,12 +257,6 @@ HRESULT g_EEStartupStatus = S_OK; // checking this flag. Volatile g_fEEStarted = FALSE; -// Flag indicating if the EE should be suspended on shutdown. -BOOL g_fSuspendOnShutdown = FALSE; - -// Flag indicating if the finalizer thread should be suspended on shutdown. -BOOL g_fSuspendFinalizerOnShutdown = FALSE; - // Flag indicating if the EE was started up by COM. extern BOOL g_fEEComActivatedStartup; @@ -1457,7 +1451,7 @@ void STDMETHODCALLTYPE EEShutDownHelper(BOOL fIsDllUnloading) // Used later for a callback. CEEInfo ceeInf; - if(fIsDllUnloading) + if (fIsDllUnloading) { ETW::EnumerationLog::ProcessShutdown(); } @@ -1513,8 +1507,6 @@ void STDMETHODCALLTYPE EEShutDownHelper(BOOL fIsDllUnloading) g_pDebugInterface->EarlyHelperThreadDeath(); #endif // DEBUGGING_SUPPORTED - BOOL fFinalizeOK = FALSE; - EX_TRY { ClrFlsSetThreadType(ThreadType_Shutdown); @@ -1528,20 +1520,17 @@ void STDMETHODCALLTYPE EEShutDownHelper(BOOL fIsDllUnloading) // Indicate the EE is the shut down phase. g_fEEShutDown |= ShutDown_Start; - fFinalizeOK = TRUE; - // Terminate the BBSweep thread g_BBSweep.ShutdownBBSweepThread(); - // We perform the final GC only if the user has requested it through the GC class. - // We should never do the final GC for a process detach if (!g_fProcessDetach && !g_fFastExitProcess) { g_fEEShutDown |= ShutDown_Finalize1; - FinalizerThread::EnableFinalization(); - fFinalizeOK = FinalizerThread::FinalizerThreadWatchDog(); - } + // Wait for the finalizer thread to deliver process exit event + GCX_PREEMP(); + FinalizerThread::RaiseShutdownEvents(); + } // Ok. Let's stop the EE. if (!g_fProcessDetach) @@ -1566,21 +1555,6 @@ void STDMETHODCALLTYPE EEShutDownHelper(BOOL fIsDllUnloading) // This call will convert the ThreadStoreLock into "shutdown" mode, just like the debugger lock above. g_fEEShutDown |= ShutDown_Finalize2; - if (fFinalizeOK) - { - fFinalizeOK = FinalizerThread::FinalizerThreadWatchDog(); - } - - if (!fFinalizeOK) - { - // One of the calls to FinalizerThreadWatchDog failed due to timeout, so we need to prevent - // any thread from running managed code, including the finalizer. - ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_FOR_SHUTDOWN); - g_fSuspendOnShutdown = TRUE; - g_fSuspendFinalizerOnShutdown = TRUE; - ThreadStore::TrapReturningThreads(TRUE); - ThreadSuspend::RestartEE(FALSE, TRUE); - } } #ifdef FEATURE_EVENT_TRACE @@ -1631,18 +1605,6 @@ void STDMETHODCALLTYPE EEShutDownHelper(BOOL fIsDllUnloading) Interpreter::PrintPostMortemData(); #endif // FEATURE_INTERPRETER - FastInterlockExchange((LONG*)&g_fForbidEnterEE, TRUE); - - if (g_fProcessDetach) - { - ThreadStore::TrapReturningThreads(TRUE); - } - - if (!g_fProcessDetach && !fFinalizeOK) - { - goto lDone; - } - #ifdef PROFILING_SUPPORTED // If profiling is enabled, then notify of shutdown first so that the // profiler can make any last calls it needs to. Do this only if we @@ -1761,13 +1723,6 @@ part2: Interpreter::Terminate(); #endif // FEATURE_INTERPRETER -#ifdef SHOULD_WE_CLEANUP - if (!g_fFastExitProcess) - { - GCHandleUtilities::GetGCHandleManager()->Shutdown(); - } -#endif /* SHOULD_WE_CLEANUP */ - //@TODO: find the right place for this VirtualCallStubManager::UninitStatic(); @@ -1775,13 +1730,6 @@ part2: PerfLog::PerfLogDone(); #endif //ENABLE_PERF_LOG - Frame::Term(); - - if (!g_fFastExitProcess) - { - SystemDomain::DetachEnd(); - } - // Unregister our vectored exception and continue handlers from the OS. // This will ensure that if any other DLL unload (after ours) has an exception, // we wont attempt to process that exception (which could lead to various @@ -1822,9 +1770,6 @@ part2: StressLog::Terminate(TRUE); #endif - if (g_pConfig != NULL) - g_pConfig->Cleanup(); - #ifdef LOGGING ShutdownLogging(); #endif @@ -1910,46 +1855,6 @@ BOOL IsThreadInSTA() } #endif -static LONG s_ActiveShutdownThreadCount = 0; - -// --------------------------------------------------------------------------- -// Function: EEShutDownProcForSTAThread(LPVOID lpParameter) -// -// Parameters: -// LPVOID lpParameter: unused -// -// Description: -// When EEShutDown decides that the shut down logic must occur on another thread, -// EEShutDown creates a new thread, and this function acts as the thread proc. See -// code:#STAShutDown for details. -// -DWORD WINAPI EEShutDownProcForSTAThread(LPVOID lpParameter) -{ - ClrFlsSetThreadType(ThreadType_ShutdownHelper); - - EEShutDownHelper(FALSE); - for (int i = 0; i < 10; i ++) - { - if (s_ActiveShutdownThreadCount) - { - return 0; - } - __SwitchToThread(20, CALLER_LIMITS_SPINNING); - } - - EPolicyAction action = GetEEPolicy()->GetDefaultAction(OPR_ProcessExit, NULL); - if (action < eRudeExitProcess) - { - action = eRudeExitProcess; - } - - UINT exitCode = GetLatchedExitCode(); - EEPolicy::HandleExitProcessFromEscalation(action, exitCode); - - return 0; -} - -// --------------------------------------------------------------------------- // #EEShutDown // // Function: EEShutDown(BOOL fIsDllUnloading) @@ -2027,60 +1932,14 @@ void STDMETHODCALLTYPE EEShutDown(BOOL fIsDllUnloading) #endif } -#ifdef FEATURE_COMINTEROP - if (!fIsDllUnloading && CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_FinalizeOnShutdown) && IsThreadInSTA()) - { - // #STAShutDown - // - // During shutdown, we may need to release STA interface on the shutdown thread. - // It is possible that the shutdown thread may deadlock. During shutdown, all - // threads are blocked, except the shutdown thread and finalizer thread. If a - // lock is held by one of these suspended threads, it can deadlock the process if - // the shutdown thread tries to enter the lock. To mitigate this risk, create - // another thread (B) to do shutdown activities (i.e., EEShutDownHelper), while - // this thread (A) waits. If B deadlocks, A will time out and immediately return - // from EEShutDown. A will then eventually call the OS's ExitProcess, which will - // kill the deadlocked thread (and all other threads). - // - // Many Windows Forms-based apps will also execute the code below to shift shut - // down logic to a separate thread, even if they don't use COM objects. Reason - // being that they will typically use a main UI thread to pump all Windows - // messages (including messages that facilitate cross-thread COM calls to STA COM - // objects), and will set that thread up as an STA thread just in case there are - // such cross-thread COM calls to contend with. In fact, when you use VS's - // File.New.Project to make a new Windows Forms project, VS will mark Main() with - // [STAThread] - DWORD thread_id = 0; - if (CreateThread(NULL,0,EEShutDownProcForSTAThread,NULL,0,&thread_id)) - { - GCX_PREEMP_NO_DTOR(); - - ClrFlsSetThreadType(ThreadType_Shutdown); - WaitForEndOfShutdown(); - FastInterlockIncrement(&s_ActiveShutdownThreadCount); - ClrFlsClearThreadType(ThreadType_Shutdown); - } - } - else - // Otherwise, this thread calls EEShutDownHelper directly. First switch to - // cooperative mode if this is a managed thread -#endif if (GetThread()) { GCX_COOP(); EEShutDownHelper(fIsDllUnloading); - if (!fIsDllUnloading) - { - FastInterlockIncrement(&s_ActiveShutdownThreadCount); - } } else { EEShutDownHelper(fIsDllUnloading); - if (!fIsDllUnloading) - { - FastInterlockIncrement(&s_ActiveShutdownThreadCount); - } } } @@ -2406,11 +2265,7 @@ BOOL STDMETHODCALLTYPE EEDllMain( // TRUE on success, FALSE on error. if (g_fEEStarted) { - // GetThread() may be set to NULL for Win9x during shutdown. - Thread *pThread = GetThread(); - if (GCHeapUtilities::IsGCInProgress() && - ( (pThread && (pThread != ThreadSuspend::GetSuspensionThread() )) - || !g_fSuspendOnShutdown)) + if (GCHeapUtilities::IsGCInProgress()) { g_fEEShutDown |= ShutDown_Phase2; break; diff --git a/src/vm/ecalllist.h b/src/vm/ecalllist.h index ab2da6555d..50071ed410 100644 --- a/src/vm/ecalllist.h +++ b/src/vm/ecalllist.h @@ -164,7 +164,6 @@ FCFuncStart(gEnvironmentFuncs) QCFuncElement("_Exit", SystemNative::Exit) FCFuncElement("set_ExitCode", SystemNative::SetExitCode) FCFuncElement("get_ExitCode", SystemNative::GetExitCode) - FCFuncElement("get_HasShutdownStarted", SystemNative::HasShutdownStarted) QCFuncElement("GetProcessorCount", SystemNative::GetProcessorCount) FCFuncElement("GetCommandLineArgsNative", SystemNative::GetCommandLineArgs) diff --git a/src/vm/eepolicy.cpp b/src/vm/eepolicy.cpp index a6ecda0520..2da1c70f70 100644 --- a/src/vm/eepolicy.cpp +++ b/src/vm/eepolicy.cpp @@ -538,61 +538,6 @@ void EEPolicy::ExitProcessViaShim(UINT exitCode) ExitProcess(exitCode); } - -//--------------------------------------------------------------------------------------- -// DisableRuntime disables this runtime, suspending all managed execution and preventing -// threads from entering the runtime. This will cause the caller to block forever as well -// unless sca is SCA_ReturnWhenShutdownComplete. -//--------------------------------------------------------------------------------------- -void DisableRuntime(ShutdownCompleteAction sca) -{ - CONTRACTL - { - DISABLED(GC_TRIGGERS); - NOTHROW; - } - CONTRACTL_END; - - FastInterlockExchange((LONG*)&g_fForbidEnterEE, TRUE); - - if (!g_fSuspendOnShutdown) - { - if (!IsGCThread()) - { - if (ThreadStore::HoldingThreadStore(GetThread())) - { - ThreadSuspend::UnlockThreadStore(); - } - ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_FOR_SHUTDOWN); - } - - if (!g_fSuspendOnShutdown) - { - ThreadStore::TrapReturningThreads(TRUE); - g_fSuspendOnShutdown = TRUE; - ClrFlsSetThreadType(ThreadType_Shutdown); - } - - // Don't restart runtime. CLR is disabled. - } - - GCX_PREEMP_NO_DTOR(); - - ClrFlsClearThreadType(ThreadType_Shutdown); - - if (g_pDebugInterface != NULL) - { - g_pDebugInterface->DisableDebugger(); - } - - if (sca == SCA_ExitProcessWhenShutdownComplete) - { - __SwitchToThread(INFINITE, CALLER_LIMITS_SPINNING); - _ASSERTE (!"Should not reach here"); - SafeExitProcess(0); - } -} - //--------------------------------------------------------------------------------------- // HandleExitProcessHelper is used to shutdown the runtime as specified by the given // action, then to exit the process. Note, however, that the process will not exit if @@ -629,9 +574,6 @@ static void HandleExitProcessHelper(EPolicyAction action, UINT exitCode, Shutdow g_fFastExitProcess = 2; SafeExitProcess(exitCode, TRUE, sca); break; - case eDisableRuntime: - DisableRuntime(sca); - break; default: _ASSERTE (!"Invalid policy"); break; @@ -686,7 +628,6 @@ void EEPolicy::PerformResourceConstraintAction(Thread *pThread, EPolicyAction ac case eExitProcess: case eFastExitProcess: case eRudeExitProcess: - case eDisableRuntime: HandleExitProcessFromEscalation(action, exitCode); break; default: @@ -1040,13 +981,6 @@ void EEPolicy::LogFatalError(UINT exitCode, UINT_PTR address, LPCWSTR pszMessage } #endif // _DEBUG - // We're here logging a fatal error. If the policy is to then do anything other than - // disable the runtime (ie, if the policy is to terminate the runtime), we should give - // Watson an opportunity to capture an error report. - // Presumably, hosts that are sophisticated enough to disable the runtime are also cognizant - // of how they want to handle fatal errors in the runtime, including whether they want - // to capture Watson information (for which they are responsible). - if (GetEEPolicy()->GetActionOnFailureNoHostNotification(FAIL_FatalRuntime) != eDisableRuntime) { #ifdef DEBUGGING_SUPPORTED //Give a managed debugger a chance if this fatal error is on a managed thread. @@ -1279,10 +1213,6 @@ int NOINLINE EEPolicy::HandleFatalError(UINT exitCode, UINT_PTR address, LPCWSTR LogFatalError(exitCode, address, pszMessage, pExceptionInfo, errorSource, argExceptionString); SafeExitProcess(exitCode, TRUE); break; - case eDisableRuntime: - LogFatalError(exitCode, address, pszMessage, pExceptionInfo, errorSource, argExceptionString); - DisableRuntime(SCA_ExitProcessWhenShutdownComplete); - break; default: _ASSERTE(!"Invalid action for FAIL_FatalRuntime"); break; diff --git a/src/vm/excep.cpp b/src/vm/excep.cpp index 4d24b21d90..52cab11138 100644 --- a/src/vm/excep.cpp +++ b/src/vm/excep.cpp @@ -4738,17 +4738,6 @@ LONG InternalUnhandledExceptionFilter_Worker( return EXCEPTION_CONTINUE_SEARCH; } - - if (GetEEPolicy()->GetActionOnFailure(FAIL_FatalRuntime) == eDisableRuntime) - { - ETaskType type = ::GetCurrentTaskType(); - if (type != TT_UNKNOWN && type != TT_USER) - { - LOG((LF_EH, LL_INFO100, "InternalUnhandledExceptionFilter_Worker: calling EEPolicy::HandleFatalError\n")); - EEPolicy::HandleFatalError(COR_E_EXECUTIONENGINE, (UINT_PTR)GetIP(pExceptionInfo->ContextRecord), NULL, pExceptionInfo); - } - } - // We don't do anything when this is called from an unmanaged thread. Thread *pThread = GetThread(); diff --git a/src/vm/finalizerthread.cpp b/src/vm/finalizerthread.cpp index 15e97878c0..ad3386e5a5 100644 --- a/src/vm/finalizerthread.cpp +++ b/src/vm/finalizerthread.cpp @@ -17,7 +17,6 @@ #include "profattach.h" #endif // FEATURE_PROFAPI_ATTACH_DETACH -BOOL FinalizerThread::fRunFinalizersOnUnload = FALSE; BOOL FinalizerThread::fQuitFinalizer = FALSE; #if defined(__linux__) && defined(FEATURE_EVENT_TRACE) @@ -31,7 +30,6 @@ Volatile g_TriggerHeapDump = FALSE; CLREvent * FinalizerThread::hEventFinalizer = NULL; CLREvent * FinalizerThread::hEventFinalizerDone = NULL; -CLREvent * FinalizerThread::hEventShutDownToFinalizer = NULL; CLREvent * FinalizerThread::hEventFinalizerToShutDown = NULL; HANDLE FinalizerThread::MHandles[kHandleCount]; @@ -408,8 +406,6 @@ static BOOL s_FinalizerThreadOK = FALSE; VOID FinalizerThread::FinalizerThreadWorker(void *args) { - // TODO: The following line should be removed after contract violation is fixed. - // See bug 27409 SCAN_IGNORE_THROW; SCAN_IGNORE_TRIGGER; @@ -523,16 +519,6 @@ VOID FinalizerThread::FinalizerThreadWorker(void *args) } } - -// During shutdown, finalize all objects that haven't been run yet... whether reachable or not. -void FinalizerThread::FinalizeObjectsOnShutdown(LPVOID args) -{ - WRAPPER_NO_CONTRACT; - - FinalizeAllObjects(BIT_SBLK_FINALIZER_RUN); -} - - DWORD WINAPI FinalizerThread::FinalizerThreadStart(void *args) { ClrFlsSetThreadType (ThreadType_Finalizer); @@ -591,7 +577,7 @@ DWORD WINAPI FinalizerThread::FinalizerThreadStart(void *args) MHandles[kProfilingAPIAttach] = ::ProfilingAPIAttachDetach::GetAttachEvent(); GetFinalizerThread()->DisablePreemptiveGC(); #endif // FEATURE_PROFAPI_ATTACH_DETACH - + while (!fQuitFinalizer) { // This will apply any policy for swallowing exceptions during normal @@ -605,67 +591,12 @@ DWORD WINAPI FinalizerThread::FinalizerThreadStart(void *args) EnableFinalization(); } - // Tell shutdown thread we are done with finalizing dead objects. - hEventFinalizerToShutDown->Set(); - - // Wait for shutdown thread to signal us. - GetFinalizerThread()->EnablePreemptiveGC(); - hEventShutDownToFinalizer->Wait(INFINITE,FALSE); - GetFinalizerThread()->DisablePreemptiveGC(); - AppDomain::RaiseExitProcessEvent(); - hEventFinalizerToShutDown->Set(); - - // Phase 1 ends. - // Now wait for Phase 2 signal. - - // Wait for shutdown thread to signal us. - GetFinalizerThread()->EnablePreemptiveGC(); - hEventShutDownToFinalizer->Wait(INFINITE,FALSE); - GetFinalizerThread()->DisablePreemptiveGC(); - // We have been asked to quit, so must be shutting down _ASSERTE(g_fEEShutDown); _ASSERTE(GetFinalizerThread()->PreemptiveGCDisabled()); - if (CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_FinalizeOnShutdown) != 0) - { - // Finalize all registered objects during shutdown, even they are still reachable. - GCHeapUtilities::GetGCHeap()->SetFinalizeQueueForShutdown(FALSE); - - // This will apply any policy for swallowing exceptions during normal - // processing, without allowing the finalizer thread to disappear on us. - ManagedThreadBase::FinalizerBase(FinalizeObjectsOnShutdown); - } - - _ASSERTE(GetFinalizerThread()->GetDomain()->IsDefaultDomain()); - - // we might want to do some extra work on the finalizer thread - // check and do it - if (GetFinalizerThread()->HaveExtraWorkForFinalizer()) - { - GetFinalizerThread()->DoExtraWorkForFinalizer(); - } - - hEventFinalizerToShutDown->Set(); - - // Wait for shutdown thread to signal us. - GetFinalizerThread()->EnablePreemptiveGC(); - hEventShutDownToFinalizer->Wait(INFINITE,FALSE); - GetFinalizerThread()->DisablePreemptiveGC(); - -#ifdef FEATURE_COMINTEROP - // Do extra cleanup for part 1 of shutdown. - // If we hang here (bug 87809) shutdown thread will - // timeout on us and will proceed normally - // - // We cannot call CoEEShutDownCOM, since the BEGIN_EXTERNAL_ENTRYPOINT - // will turn our call into a NOP. We can no longer execute managed - // code for an external caller. - InnerCoEEShutDownCOM(); -#endif // FEATURE_COMINTEROP - hEventFinalizerToShutDown->Set(); #ifdef _DEBUG // The only purpose of this try/finally is to trigger an assertion @@ -729,8 +660,6 @@ void FinalizerThread::FinalizerThreadCreate() hEventFinalizer->CreateAutoEvent(FALSE); hEventFinalizerToShutDown = new CLREvent(); hEventFinalizerToShutDown->CreateAutoEvent(FALSE); - hEventShutDownToFinalizer = new CLREvent(); - hEventShutDownToFinalizer->CreateAutoEvent(FALSE); _ASSERTE(g_pFinalizerThread == 0); g_pFinalizerThread = SetupUnstartedThread(); @@ -833,335 +762,3 @@ void FinalizerThread::FinalizerThreadWait(DWORD timeout) } } } - - -#ifdef _DEBUG -#define FINALIZER_WAIT_TIMEOUT 250 -#else -#define FINALIZER_WAIT_TIMEOUT 200 -#endif -#define FINALIZER_TOTAL_WAIT 2000 - -static BOOL s_fRaiseExitProcessEvent = FALSE; -static DWORD dwBreakOnFinalizeTimeOut = (DWORD) -1; - -static ULONGLONG ShutdownEnd; - - -BOOL FinalizerThread::FinalizerThreadWatchDog() -{ - Thread *pThread = GetThread(); - - if (dwBreakOnFinalizeTimeOut == (DWORD) -1) { - dwBreakOnFinalizeTimeOut = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_BreakOnFinalizeTimeOut); - } - - // Do not wait for FinalizerThread if the current one is FinalizerThread. - if (pThread == GetFinalizerThread()) - return TRUE; - - // If finalizer thread is gone, just return. - if (GetFinalizerThread()->Join (0, FALSE) != WAIT_TIMEOUT) - return TRUE; - - // *** This is the first call ShutDown -> Finalizer to Finilize dead objects *** - if ((g_fEEShutDown & ShutDown_Finalize1) && - !(g_fEEShutDown & ShutDown_Finalize2)) { - ShutdownEnd = CLRGetTickCount64() + GetEEPolicy()->GetTimeout(OPR_ProcessExit); - // Wait for the finalizer... - LOG((LF_GC, LL_INFO10, "Signalling finalizer to quit...")); - - fQuitFinalizer = TRUE; - hEventFinalizerDone->Reset(); - EnableFinalization(); - - LOG((LF_GC, LL_INFO10, "Waiting for finalizer to quit...")); - - if (pThread) - { - pThread->EnablePreemptiveGC(); - } - - BOOL fTimeOut = FinalizerThreadWatchDogHelper(); - - if (!fTimeOut) { - hEventShutDownToFinalizer->Set(); - - // Wait for finalizer thread to finish raising ExitProcess Event. - s_fRaiseExitProcessEvent = TRUE; - fTimeOut = FinalizerThreadWatchDogHelper(); - s_fRaiseExitProcessEvent = FALSE; - } - - if (pThread) - { - pThread->DisablePreemptiveGC(); - } - - // Can not call ExitProcess here if we are in a hosting environment. - // The host does not expect that we terminate the process. - //if (fTimeOut) - //{ - //::ExitProcess (GetLatchedExitCode()); - //} - - return !fTimeOut; - } - - // *** This is the second call ShutDown -> Finalizer to *** - // suspend the Runtime and Finilize live objects - if ( g_fEEShutDown & ShutDown_Finalize2 && - !(g_fEEShutDown & ShutDown_COM) ) { - -#ifdef BACKGROUND_GC - gc_heap::gc_can_use_concurrent = FALSE; - - if (pGenGCHeap->settings.concurrent) - pGenGCHeap->background_gc_wait(); -#endif //BACKGROUND_GC - - _ASSERTE((g_fEEShutDown & ShutDown_Finalize1) || g_fFastExitProcess); - - if (CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_FinalizeOnShutdown) != 0) - { - // When running finalizers on shutdown (including for reachable objects), suspend threads for shutdown before - // running finalizers, so that the reachable objects will not be used after they are finalized. - - ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_FOR_SHUTDOWN); - - g_fSuspendOnShutdown = TRUE; - - // Do not balance the trap returning threads. - // We are shutting down CLR. Only Finalizer/Shutdown threads can - // return from DisablePreemptiveGC. - ThreadStore::TrapReturningThreads(TRUE); - - ThreadSuspend::RestartEE(FALSE, TRUE); - } - - if (g_fFastExitProcess) - { - return TRUE; - } - - // !!! Before we wake up Finalizer thread, we need to enable preemptive gc on the - // !!! shutdown thread. Otherwise we may see a deadlock during debug test. - if (pThread) - { - pThread->EnablePreemptiveGC(); - } - - GCHeapUtilities::GetGCHeap()->SetFinalizeRunOnShutdown(true); - - // Wait for finalizer thread to finish finalizing all objects. - hEventShutDownToFinalizer->Set(); - BOOL fTimeOut = FinalizerThreadWatchDogHelper(); - - if (!fTimeOut) { - GCHeapUtilities::GetGCHeap()->SetFinalizeRunOnShutdown(false); - } - - // Can not call ExitProcess here if we are in a hosting environment. - // The host does not expect that we terminate the process. - //if (fTimeOut) { - // ::ExitProcess (GetLatchedExitCode()); - //} - - if (pThread) - { - pThread->DisablePreemptiveGC(); - } - return !fTimeOut; - } - - // *** This is the third call ShutDown -> Finalizer *** - // to do additional cleanup - if (g_fEEShutDown & ShutDown_COM) { - _ASSERTE (g_fEEShutDown & (ShutDown_Finalize2 | ShutDown_Finalize1)); - - if (pThread) - { - pThread->EnablePreemptiveGC(); - } - - GCHeapUtilities::GetGCHeap()->SetFinalizeRunOnShutdown(true); - - hEventShutDownToFinalizer->Set(); - DWORD status = WAIT_OBJECT_0; - while (CLREventWaitWithTry(hEventFinalizerToShutDown, FINALIZER_WAIT_TIMEOUT, TRUE, &status)) - { - } - - BOOL fTimeOut = (status == WAIT_TIMEOUT) ? TRUE : FALSE; - - if (fTimeOut) - { - if (dwBreakOnFinalizeTimeOut) { - LOG((LF_GC, LL_INFO10, "Finalizer took too long to clean up COM IP's.\n")); - DebugBreak(); - } - } - - if (pThread) - { - pThread->DisablePreemptiveGC(); - } - - return !fTimeOut; - } - - _ASSERTE(!"Should never reach this point"); - return FALSE; -} - -BOOL FinalizerThread::FinalizerThreadWatchDogHelper() -{ - // Since our thread is blocking waiting for the finalizer thread, we must be in preemptive GC - // so that we don't in turn block the finalizer on us in a GC. - Thread *pCurrentThread; - pCurrentThread = GetThread(); - _ASSERTE (pCurrentThread == NULL || !pCurrentThread->PreemptiveGCDisabled()); - - // We're monitoring the finalizer thread. - Thread *pThread = GetFinalizerThread(); - _ASSERTE(pThread != pCurrentThread); - - ULONGLONG dwBeginTickCount = CLRGetTickCount64(); - - size_t prevCount; - size_t curCount; - BOOL fTimeOut = FALSE; - DWORD nTry = 0; - DWORD maxTotalWait = (DWORD)(ShutdownEnd - dwBeginTickCount); - DWORD totalWaitTimeout; - totalWaitTimeout = GetEEPolicy()->GetTimeout(OPR_FinalizerRun); - if (totalWaitTimeout == (DWORD)-1) - { - totalWaitTimeout = FINALIZER_TOTAL_WAIT; - } - - if (s_fRaiseExitProcessEvent) - { - DWORD tmp = maxTotalWait/20; // Normally we assume 2 seconds timeout if total timeout is 40 seconds. - if (tmp > totalWaitTimeout) - { - totalWaitTimeout = tmp; - } - prevCount = MAXLONG; - } - else - { - prevCount = GCHeapUtilities::GetGCHeap()->GetNumberOfFinalizable(); - } - - DWORD maxTry = (DWORD)(totalWaitTimeout*1.0/FINALIZER_WAIT_TIMEOUT + 0.5); - BOOL bAlertable = TRUE; //(g_fEEShutDown & ShutDown_Finalize2) ? FALSE:TRUE; - - if (dwBreakOnFinalizeTimeOut == (DWORD) -1) { - dwBreakOnFinalizeTimeOut = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_BreakOnFinalizeTimeOut); - } - - DWORD dwTimeout = FINALIZER_WAIT_TIMEOUT; - - // This used to set the dwTimeout to infinite, but this can cause a hang when shutting down - // if a finalizer tries to take a lock that another suspended managed thread already has. - // This results in the hang because the other managed thread is never going to be resumed - // because we're in shutdown. So we make a compromise here - make the timeout for every - // iteration 10 times longer and make the total wait infinite - so if things hang we will - // eventually shutdown but we also give things a chance to finish if they're running slower - // because of the profiler. -#ifdef PROFILING_SUPPORTED - if (CORProfilerPresent()) - { - dwTimeout *= 10; - maxTotalWait = INFINITE; - } -#endif // PROFILING_SUPPORTED - - // This change was added late in Windows Phone 8, so we want to keep it minimal. - // We should consider refactoring this later, as we've got a lot of dead code here now on CoreCLR. - dwTimeout = INFINITE; - maxTotalWait = INFINITE; - - while (1) { - struct Param - { - DWORD status; - DWORD dwTimeout; - BOOL bAlertable; - } param; - param.status = 0; - param.dwTimeout = dwTimeout; - param.bAlertable = bAlertable; - - PAL_TRY(Param *, pParam, ¶m) - { - pParam->status = hEventFinalizerToShutDown->Wait(pParam->dwTimeout, pParam->bAlertable); - } - PAL_EXCEPT (EXCEPTION_EXECUTE_HANDLER) - { - param.status = WAIT_TIMEOUT; - } - PAL_ENDTRY - - if (param.status != WAIT_TIMEOUT) { - break; - } - nTry ++; - // ExitProcessEventCount is incremental - // FinalizableObjects is decremental - if (s_fRaiseExitProcessEvent) - { - curCount = MAXLONG - GetProcessedExitProcessEventCount(); - } - else - { - curCount = GCHeapUtilities::GetGCHeap()->GetNumberOfFinalizable(); - } - - if ((prevCount <= curCount) - && !GCHeapUtilities::GetGCHeap()->ShouldRestartFinalizerWatchDog() - && (pThread == NULL || !(pThread->m_State & (Thread::TS_UserSuspendPending | Thread::TS_DebugSuspendPending)))){ - if (nTry == maxTry) { - if (!s_fRaiseExitProcessEvent) { - LOG((LF_GC, LL_INFO10, "Finalizer took too long on one object.\n")); - } - else - LOG((LF_GC, LL_INFO10, "Finalizer took too long to process ExitProcess event.\n")); - - fTimeOut = TRUE; - if (dwBreakOnFinalizeTimeOut != 2) { - break; - } - } - } - else - { - nTry = 0; - prevCount = curCount; - } - ULONGLONG dwCurTickCount = CLRGetTickCount64(); - if (pThread && pThread->m_State & (Thread::TS_UserSuspendPending | Thread::TS_DebugSuspendPending)) { - // CoreCLR does not support user-requested thread suspension - _ASSERTE(!(pThread->m_State & Thread::TS_UserSuspendPending)); - dwBeginTickCount = dwCurTickCount; - } - if (dwCurTickCount - dwBeginTickCount >= maxTotalWait) - { - LOG((LF_GC, LL_INFO10, "Finalizer took too long on shutdown.\n")); - fTimeOut = TRUE; - if (dwBreakOnFinalizeTimeOut != 2) { - break; - } - } - } - - if (fTimeOut) - { - if (dwBreakOnFinalizeTimeOut){ - DebugBreak(); - } - } - - return fTimeOut; -} diff --git a/src/vm/finalizerthread.h b/src/vm/finalizerthread.h index f11dd15209..d5063b2166 100644 --- a/src/vm/finalizerthread.h +++ b/src/vm/finalizerthread.h @@ -8,16 +8,14 @@ class FinalizerThread { - static BOOL fRunFinalizersOnUnload; static BOOL fQuitFinalizer; - + #if defined(__linux__) && defined(FEATURE_EVENT_TRACE) static ULONGLONG LastHeapDumpTime; #endif static CLREvent *hEventFinalizer; static CLREvent *hEventFinalizerDone; - static CLREvent *hEventShutDownToFinalizer; static CLREvent *hEventFinalizerToShutDown; // Note: This enum makes it easier to read much of the code that deals with the @@ -40,8 +38,6 @@ class FinalizerThread static void WaitForFinalizerEvent (CLREvent *event); - static BOOL FinalizerThreadWatchDogHelper(); - #ifdef FEATURE_PROFAPI_ATTACH_DETACH static void ProcessProfilerAttachIfNecessary(ULONGLONG * pui64TimestampLastCheckedEventMs); #endif // FEATURE_PROFAPI_ATTACH_DETACH @@ -64,6 +60,17 @@ public: static BOOL HaveExtraWorkForFinalizer(); + static void RaiseShutdownEvents() + { + WRAPPER_NO_CONTRACT; + fQuitFinalizer = TRUE; + EnableFinalization(); + + // Do not wait for FinalizerThread if the current one is FinalizerThread. + if (GetThread() != GetFinalizerThread()) + hEventFinalizerToShutDown->Wait(INFINITE,FALSE); + } + static void FinalizerThreadWait(DWORD timeout = INFINITE); // We wake up a wait for finaliation for two reasons: @@ -72,11 +79,9 @@ public: static void SignalFinalizationDone(BOOL fFinalizer); static VOID FinalizerThreadWorker(void *args); - static void FinalizeObjectsOnShutdown(LPVOID args); static DWORD WINAPI FinalizerThreadStart(void *args); static void FinalizerThreadCreate(); - static BOOL FinalizerThreadWatchDog(); }; #endif // _FINALIZER_THREAD_H_ diff --git a/src/vm/frames.cpp b/src/vm/frames.cpp index af41def0c1..f44f32a4c2 100644 --- a/src/vm/frames.cpp +++ b/src/vm/frames.cpp @@ -311,14 +311,6 @@ void Frame::Init() } // void Frame::Init() -// static -void Frame::Term() -{ - LIMITED_METHOD_CONTRACT; - delete s_pFrameVTables; - s_pFrameVTables = NULL; -} - #endif // DACCESS_COMPILE // Returns true if the Frame's VTablePtr is valid diff --git a/src/vm/frames.h b/src/vm/frames.h index 5cc5e37d5e..d219ac30a5 100644 --- a/src/vm/frames.h +++ b/src/vm/frames.h @@ -543,7 +543,6 @@ public: static bool HasValidVTablePtr(Frame * pFrame); static PTR_GSCookie SafeGetGSCookiePtr(Frame * pFrame); static void Init(); - static void Term(); // Callers, note that the REGDISPLAY parameter is actually in/out. While // UpdateRegDisplay is generally used to fill out the REGDISPLAY parameter, some diff --git a/src/vm/threads.cpp b/src/vm/threads.cpp index 43976a91ff..70c26118ea 100644 --- a/src/vm/threads.cpp +++ b/src/vm/threads.cpp @@ -5213,12 +5213,6 @@ DEBUG_NOINLINE void ThreadStore::Enter() ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT; CHECK_ONE_STORE(); m_Crst.Enter(); - - // Threadstore needs special shutdown handling. - if (g_fSuspendOnShutdown) - { - m_Crst.ReleaseAndBlockForShutdownIfNotSpecialThread(); - } } DEBUG_NOINLINE void ThreadStore::Leave() diff --git a/src/vm/threadsuspend.cpp b/src/vm/threadsuspend.cpp index 1fee3b760c..9760fe4293 100644 --- a/src/vm/threadsuspend.cpp +++ b/src/vm/threadsuspend.cpp @@ -1356,7 +1356,6 @@ Thread::UserAbort(ThreadAbortRequester requester, case eExitProcess: case eFastExitProcess: case eRudeExitProcess: - case eDisableRuntime: GetEEPolicy()->NotifyHostOnDefaultAction(operation,action); EEPolicy::HandleExitProcessFromEscalation(action, HOST_E_EXITPROCESS_THREADABORT); _ASSERTE (!"Should not reach here"); @@ -1986,7 +1985,6 @@ LPrepareRetry: case eExitProcess: case eFastExitProcess: case eRudeExitProcess: - case eDisableRuntime: GetEEPolicy()->NotifyHostOnTimeout(operation1, action1); EEPolicy::HandleExitProcessFromEscalation(action1, HOST_E_EXITPROCESS_TIMEOUT); _ASSERTE (!"Should not reach here"); @@ -2543,9 +2541,8 @@ void Thread::RareDisablePreemptiveGC() // Note IsGCInProgress is also true for say Pause (anywhere SuspendEE happens) and GCThread is the // thread that did the Pause. While in Pause if another thread attempts Rev/Pinvoke it should get inside the following and // block until resume - if (((GCHeapUtilities::IsGCInProgress() && (this != ThreadSuspend::GetSuspensionThread())) || - (m_State & (TS_UserSuspendPending | TS_DebugSuspendPending | TS_StackCrawlNeeded))) && - (!g_fSuspendOnShutdown || IsFinalizerThread() || IsShutdownSpecialThread())) + if ((GCHeapUtilities::IsGCInProgress() && (this != ThreadSuspend::GetSuspensionThread())) || + (m_State & (TS_UserSuspendPending | TS_DebugSuspendPending | TS_StackCrawlNeeded))) { if (!ThreadStore::HoldingThreadStore(this)) { @@ -2653,47 +2650,6 @@ void Thread::RareDisablePreemptiveGC() STRESS_LOG0(LF_SYNC, LL_INFO1000, "RareDisablePreemptiveGC: leaving\n"); } - // Block all threads except finalizer and shutdown thread during shutdown. - // If g_fSuspendFinalizerOnShutdown is set, block the finalizer too. - if ((g_fSuspendOnShutdown && !IsFinalizerThread() && !IsShutdownSpecialThread()) || - (g_fSuspendFinalizerOnShutdown && IsFinalizerThread())) - { - STRESS_LOG1(LF_SYNC, LL_INFO1000, "RareDisablePreemptiveGC: entering. Thread state = %x\n", m_State.Load()); - - EnablePreemptiveGC(); - - // Cannot use GCX_PREEMP_NO_DTOR here because we're inside of the thread - // PREEMP->COOP switch mechanism and GCX_PREEMP's assert's will fire. - // Instead we use BEGIN_GCX_ASSERT_PREEMP to inform Scan of the mode - // change here. - BEGIN_GCX_ASSERT_PREEMP; - -#ifdef PROFILING_SUPPORTED - // If profiler desires GC events, notify it that this thread is waiting until the GC is over - // Do not send suspend notifications for debugger suspensions - { - BEGIN_PIN_PROFILER(CORProfilerTrackSuspends()); - if (!(m_State & TS_DebugSuspendPending)) - { - g_profControlBlock.pProfInterface->RuntimeThreadSuspended((ThreadID)this); - } - END_PIN_PROFILER(); - } -#endif // PROFILING_SUPPORTED - - - - // The thread is blocked for shutdown. We do not concern for GC violation. - CONTRACT_VIOLATION(GCViolation); - - WaitForEndOfShutdown(); - - END_GCX_ASSERT_PREEMP; - - __SwitchToThread(INFINITE, CALLER_LIMITS_SPINNING); - _ASSERTE(!"Cannot reach here"); - } - Exit: ; END_PRESERVE_LAST_ERROR; } @@ -2740,7 +2696,6 @@ void Thread::HandleThreadAbortTimeout() case eExitProcess: case eFastExitProcess: case eRudeExitProcess: - case eDisableRuntime: GetEEPolicy()->NotifyHostOnTimeout(operation,action); EEPolicy::HandleExitProcessFromEscalation(action, HOST_E_EXITPROCESS_THREADABORT); _ASSERTE (!"Should not reach here"); @@ -2844,7 +2799,6 @@ void Thread::PreWorkForThreadAbort() case eExitProcess: case eFastExitProcess: case eRudeExitProcess: - case eDisableRuntime: { GetEEPolicy()->NotifyHostOnDefaultAction(OPR_ThreadRudeAbortInCriticalRegion,action); GetEEPolicy()->HandleExitProcessFromEscalation(action,HOST_E_EXITPROCESS_ADUNLOAD); diff --git a/src/vm/vars.hpp b/src/vm/vars.hpp index 68473b6c1a..e4b790836e 100644 --- a/src/vm/vars.hpp +++ b/src/vm/vars.hpp @@ -529,10 +529,6 @@ EXTERN BOOL g_fComStarted; GVAL_DECL(DWORD, g_fEEShutDown); EXTERN DWORD g_fFastExitProcess; EXTERN BOOL g_fFatalErrorOccurredOnGCThread; -#ifndef DACCESS_COMPILE -EXTERN BOOL g_fSuspendOnShutdown; -EXTERN BOOL g_fSuspendFinalizerOnShutdown; -#endif // DACCESS_COMPILE EXTERN Volatile g_fForbidEnterEE; GVAL_DECL(bool, g_fProcessDetach); EXTERN bool g_fManagedAttach; diff --git a/src/vm/win32threadpool.cpp b/src/vm/win32threadpool.cpp index b5014962d2..f340542b0c 100644 --- a/src/vm/win32threadpool.cpp +++ b/src/vm/win32threadpool.cpp @@ -3094,12 +3094,7 @@ void ThreadpoolMgr::DeregisterWait(WaitInfo* pArgs) if (InterlockedDecrement(&waitInfo->refCount) == 0) { - // After we suspend EE during shutdown, a thread may be blocked in WaitForEndOfShutdown in alertable state. - // We don't allow a thread reenter runtime while processing APC or pumping message. - if (!g_fSuspendOnShutdown ) - { - DeleteWait(waitInfo); - } + DeleteWait(waitInfo); } return; } -- cgit v1.2.3