From 8913aaa1388e263d537b19e78caaed6b124fc0ea Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Sat, 13 Apr 2019 08:09:31 -0700 Subject: [PATCH] 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. --- .../shared/System/Gen2GcCallback.cs | 5 +- .../CompilerServices/ConditionalWeakTable.cs | 6 +- .../shared/System/Threading/ThreadPool.cs | 17 +- .../shared/System/Threading/Timer.cs | 11 - .../src/System/Environment.CoreCLR.cs | 7 +- .../System/Reflection/Emit/DynamicILGenerator.cs | 19 +- .../src/System/Reflection/LoaderAllocator.cs | 17 +- .../ClrThreadPoolPreAllocatedOverlapped.cs | 7 +- src/classlibnative/bcltype/system.cpp | 12 - src/classlibnative/bcltype/system.h | 3 +- src/inc/MSCOREE.IDL | 1 - src/inc/clrconfigvalues.h | 7 - src/pal/prebuilt/inc/mscoree.h | 3 +- src/utilcode/posterror.cpp | 10 - 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 +- 27 files changed, 43 insertions(+), 825 deletions(-) diff --git a/src/System.Private.CoreLib/shared/System/Gen2GcCallback.cs b/src/System.Private.CoreLib/shared/System/Gen2GcCallback.cs index acc415b..1f8de96 100644 --- a/src/System.Private.CoreLib/shared/System/Gen2GcCallback.cs +++ b/src/System.Private.CoreLib/shared/System/Gen2GcCallback.cs @@ -71,10 +71,7 @@ namespace System } // Resurrect ourselves by re-registering for finalization. - if (!Environment.HasShutdownStarted) - { - GC.ReRegisterForFinalize(this); - } + GC.ReRegisterForFinalize(this); } } } diff --git a/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ConditionalWeakTable.cs b/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ConditionalWeakTable.cs index a7ea972..505134a 100644 --- a/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ConditionalWeakTable.cs +++ b/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ConditionalWeakTable.cs @@ -750,11 +750,9 @@ namespace System.Runtime.CompilerServices ~Container() { - // We're just freeing per-appdomain unmanaged handles here. If we're already shutting down the AD, - // don't bother. (Despite its name, Environment.HasShutdownStart also returns true if the current - // AD is finalizing.) We also skip doing anything if the container is invalid, including if someone + // Skip doing anything if the container is invalid, including if somehow // the container object was allocated but its associated table never set. - if (Environment.HasShutdownStarted || _invalid || _parent is null) + if (_invalid || _parent is null) { return; } diff --git a/src/System.Private.CoreLib/shared/System/Threading/ThreadPool.cs b/src/System.Private.CoreLib/shared/System/Threading/ThreadPool.cs index 4e6ff27..0625a97 100644 --- a/src/System.Private.CoreLib/shared/System/Threading/ThreadPool.cs +++ b/src/System.Private.CoreLib/shared/System/Threading/ThreadPool.cs @@ -709,8 +709,9 @@ namespace System.Threading currentThread = Thread.CurrentThread; } - private void CleanUp() + ~ThreadPoolWorkQueueThreadLocals() { + // Transfer any pending workitems into the global queue so that they will be executed by another thread if (null != workStealingQueue) { if (null != workQueue) @@ -726,17 +727,6 @@ namespace System.Threading ThreadPoolWorkQueue.WorkStealingQueueList.Remove(workStealingQueue); } } - - ~ThreadPoolWorkQueueThreadLocals() - { - // Since the purpose of calling CleanUp is to transfer any pending workitems into the global - // queue so that they will be executed by another thread, there's no point in doing this cleanup - // if we're in the process of shutting down or unloading the AD. In those cases, the work won't - // execute anyway. And there are subtle race conditions involved there that would lead us to do the wrong - // thing anyway. So we'll only clean up if this is a "normal" finalization. - if (!Environment.HasShutdownStarted) - CleanUp(); - } } public delegate void WaitCallback(object? state); @@ -752,8 +742,7 @@ namespace System.Threading ~QueueUserWorkItemCallbackBase() { Debug.Assert( - executed != 0 || Environment.HasShutdownStarted, - "A QueueUserWorkItemCallback was never called!"); + executed != 0, "A QueueUserWorkItemCallback was never called!"); } #endif diff --git a/src/System.Private.CoreLib/shared/System/Threading/Timer.cs b/src/System.Private.CoreLib/shared/System/Threading/Timer.cs index 66d01ac..19bdac6 100644 --- a/src/System.Private.CoreLib/shared/System/Threading/Timer.cs +++ b/src/System.Private.CoreLib/shared/System/Threading/Timer.cs @@ -678,17 +678,6 @@ namespace System.Threading ~TimerHolder() { - // If shutdown has started, another thread may be suspended while holding the timer lock. - // So we can't safely close the timer. - // - // Similarly, we should not close the timer during AD-unload's live-object finalization phase. - // A rude abort may have prevented us from releasing the lock. - // - // Note that in either case, the Timer still won't fire, because ThreadPool threads won't be - // allowed to run anymore. - if (Environment.HasShutdownStarted) - return; - _timer.Close(); } diff --git a/src/System.Private.CoreLib/src/System/Environment.CoreCLR.cs b/src/System.Private.CoreLib/src/System/Environment.CoreCLR.cs index 4514043..b09fa91 100644 --- a/src/System.Private.CoreLib/src/System/Environment.CoreCLR.cs +++ b/src/System.Private.CoreLib/src/System/Environment.CoreCLR.cs @@ -77,11 +77,8 @@ namespace System GetCommandLineArgsNative(); } - public static extern bool HasShutdownStarted - { - [MethodImpl(MethodImplOptions.InternalCall)] - get; - } + // Unconditionally return false since .NET Core does not support object finalization during shutdown. + public static bool HasShutdownStarted => false; public static int ProcessorCount => GetProcessorCount(); diff --git a/src/System.Private.CoreLib/src/System/Reflection/Emit/DynamicILGenerator.cs b/src/System.Private.CoreLib/src/System/Reflection/Emit/DynamicILGenerator.cs index b47b305..aee47df 100644 --- a/src/System.Private.CoreLib/src/System/Reflection/Emit/DynamicILGenerator.cs +++ b/src/System.Private.CoreLib/src/System/Reflection/Emit/DynamicILGenerator.cs @@ -638,14 +638,8 @@ namespace System.Reflection.Emit } catch { - // We go over all DynamicMethodDesc during AppDomain shutdown and make sure - // that everything associated with them is released. So it is ok to skip reregistration - // for finalization during appdomain shutdown - if (!Environment.HasShutdownStarted) - { - // Try again later. - GC.ReRegisterForFinalize(this); - } + // Try again later. + GC.ReRegisterForFinalize(this); return; } @@ -666,12 +660,9 @@ namespace System.Reflection.Emit // It is not safe to destroy the method if the managed resolver is alive. if (RuntimeMethodHandle.GetResolver(m_methodHandle) != null) { - if (!Environment.HasShutdownStarted) - { - // Somebody might have been holding a reference on us via weak handle. - // We will keep trying. It will be hopefully released eventually. - GC.ReRegisterForFinalize(this); - } + // Somebody might have been holding a reference on us via weak handle. + // We will keep trying. It will be hopefully released eventually. + GC.ReRegisterForFinalize(this); return; } diff --git a/src/System.Private.CoreLib/src/System/Reflection/LoaderAllocator.cs b/src/System.Private.CoreLib/src/System/Reflection/LoaderAllocator.cs index 1119f07..7d7d16b 100644 --- a/src/System.Private.CoreLib/src/System/Reflection/LoaderAllocator.cs +++ b/src/System.Private.CoreLib/src/System/Reflection/LoaderAllocator.cs @@ -41,19 +41,12 @@ namespace System.Reflection if (m_nativeLoaderAllocator == IntPtr.Zero) return; - // Assemblies and LoaderAllocators will be cleaned up during AppDomain shutdown in - // unmanaged code - // So it is ok to skip reregistration and cleanup for finalization during shutdown. - // We also avoid early finalization of LoaderAllocatorScout due to AD unload when the object was inside DelayedFinalizationList. - if (!Environment.HasShutdownStarted) + // Destroy returns false if the managed LoaderAllocator is still alive. + if (!Destroy(m_nativeLoaderAllocator)) { - // Destroy returns false if the managed LoaderAllocator is still alive. - if (!Destroy(m_nativeLoaderAllocator)) - { - // Somebody might have been holding a reference on us via weak handle. - // We will keep trying. It will be hopefully released eventually. - GC.ReRegisterForFinalize(this); - } + // Somebody might have been holding a reference on us via weak handle. + // We will keep trying. It will be hopefully released eventually. + GC.ReRegisterForFinalize(this); } } } diff --git a/src/System.Private.CoreLib/src/System/Threading/ClrThreadPoolPreAllocatedOverlapped.cs b/src/System.Private.CoreLib/src/System/Threading/ClrThreadPoolPreAllocatedOverlapped.cs index 26e1bd6..fd7f1dd 100644 --- a/src/System.Private.CoreLib/src/System/Threading/ClrThreadPoolPreAllocatedOverlapped.cs +++ b/src/System.Private.CoreLib/src/System/Threading/ClrThreadPoolPreAllocatedOverlapped.cs @@ -78,12 +78,7 @@ namespace System.Threading ~PreAllocatedOverlapped() { - // - // During shutdown, don't automatically clean up, because this instance may still be - // reachable/usable by other code. - // - if (!Environment.HasShutdownStarted) - Dispose(); + Dispose(); } unsafe void IDeferredDisposable.OnFinalRelease(bool disposed) diff --git a/src/classlibnative/bcltype/system.cpp b/src/classlibnative/bcltype/system.cpp index 38e5bba..ce47c14 100644 --- a/src/classlibnative/bcltype/system.cpp +++ b/src/classlibnative/bcltype/system.cpp @@ -356,18 +356,6 @@ INT32 QCALLTYPE SystemNative::GetProcessorCount() return processorCount; } -FCIMPL0(FC_BOOL_RET, SystemNative::HasShutdownStarted) -{ - FCALL_CONTRACT; - - // Return true if the EE has started to shutdown and is now going to - // aggressively finalize objects referred to by static variables OR - // if someone is unloading the current AppDomain AND we have started - // finalizing objects referred to by static variables. - FC_RETURN_BOOL(g_fEEShutDown & ShutDown_Finalize2); -} -FCIMPLEND - // FailFast is supported in BCL.small as internal to support failing fast in places where EEE used to be thrown. // // Static message buffer used by SystemNative::FailFast to avoid reliance on a diff --git a/src/classlibnative/bcltype/system.h b/src/classlibnative/bcltype/system.h index 803f1df..95670a3 100644 --- a/src/classlibnative/bcltype/system.h +++ b/src/classlibnative/bcltype/system.h @@ -69,9 +69,8 @@ public: static FCDECL3(VOID, FailFastWithExceptionAndSource, StringObject* refMessageUNSAFE, ExceptionObject* refExceptionUNSAFE, StringObject* errorSourceUNSAFE); // Returns the number of logical processors that can be used by managed code - static INT32 QCALLTYPE GetProcessorCount(); + static INT32 QCALLTYPE GetProcessorCount(); - static FCDECL0(FC_BOOL_RET, HasShutdownStarted); static FCDECL0(FC_BOOL_RET, IsServerGC); #ifdef FEATURE_COMINTEROP diff --git a/src/inc/MSCOREE.IDL b/src/inc/MSCOREE.IDL index 93e4042..ce368f3 100644 --- a/src/inc/MSCOREE.IDL +++ b/src/inc/MSCOREE.IDL @@ -204,7 +204,6 @@ typedef enum // Stop bypasses finalizer run. eFastExitProcess, eRudeExitProcess, - eDisableRuntime, MaxPolicyAction } EPolicyAction; diff --git a/src/inc/clrconfigvalues.h b/src/inc/clrconfigvalues.h index 441ae8e..61258ae 100644 --- a/src/inc/clrconfigvalues.h +++ b/src/inc/clrconfigvalues.h @@ -110,11 +110,6 @@ CONFIG_DWORD_INFO(INTERNAL_ADTakeDHSnapShot, W("ADTakeDHSnapShot"), 0, "Supersed CONFIG_DWORD_INFO(INTERNAL_ADTakeSnapShot, W("ADTakeSnapShot"), 0, "Superseded by test hooks") CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_EnableFullDebug, W("EnableFullDebug"), "Heavy-weight checking for AD boundary violations (AD leaks)") -// For the proposal and discussion on why finalizers are not run on shutdown by default anymore in CoreCLR, see the API review: -// https://github.com/dotnet/corefx/issues/5205 -#define DEFAULT_FinalizeOnShutdown (0) -RETAIL_CONFIG_DWORD_INFO(EXTERNAL_FinalizeOnShutdown, W("FinalizeOnShutdown"), DEFAULT_FinalizeOnShutdown, "When enabled, on shutdown, blocks all user threads and calls finalizers for all finalizable objects, including live objects") - /// /// ARM /// @@ -150,7 +145,6 @@ CONFIG_DWORD_INFO_EX(INTERNAL_BreakOnDumpToken, W("BreakOnDumpToken"), 0xfffffff RETAIL_CONFIG_DWORD_INFO_EX(UNSUPPORTED_BreakOnEELoad, W("BreakOnEELoad"), 0, "", CLRConfig::REGUTIL_default) CONFIG_DWORD_INFO(INTERNAL_BreakOnEEShutdown, W("BreakOnEEShutdown"), 0, "") CONFIG_DWORD_INFO_EX(INTERNAL_BreakOnExceptionInGetThrowable, W("BreakOnExceptionInGetThrowable"), 0, "", CLRConfig::REGUTIL_default) -RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_BreakOnFinalizeTimeOut, W("BreakOnFinalizeTimeOut"), 0, "Triggers a debug break on the finalizer thread when it has exceeded the maximum wait time") CONFIG_DWORD_INFO(INTERNAL_BreakOnFindMethod, W("BreakOnFindMethod"), 0, "Breaks in findMethodInternal when it searches for the specified token.") CONFIG_DWORD_INFO_EX(INTERNAL_BreakOnFirstPass, W("BreakOnFirstPass"), 0, "", CLRConfig::REGUTIL_default) CONFIG_DWORD_INFO_EX(INTERNAL_BreakOnHR, W("BreakOnHR"), 0, "Debug.cpp, IfFailxxx use this macro to stop if hr matches ", CLRConfig::REGUTIL_default) @@ -158,7 +152,6 @@ CONFIG_STRING_INFO(INTERNAL_BreakOnInstantiation, W("BreakOnInstantiation"), "Ve CONFIG_STRING_INFO(INTERNAL_BreakOnInteropStubSetup, W("BreakOnInteropStubSetup"), "Throws an assert when marshaling stub for the given method is about to be built.") CONFIG_STRING_INFO_EX(INTERNAL_BreakOnInteropVTableBuild, W("BreakOnInteropVTableBuild"), "Specifies a type name for which an assert should be thrown when building interop v-table.", CLRConfig::REGUTIL_default) CONFIG_STRING_INFO(INTERNAL_BreakOnMethodName, W("BreakOnMethodName"), "Very useful for debugging method override placement code.") -RETAIL_CONFIG_DWORD_INFO_EX(UNSUPPORTED_BreakOnNGenRegistryAccessCount, W("BreakOnNGenRegistryAccessCount"), 0, "Breaks on the Nth' root store write", CLRConfig::REGUTIL_default) CONFIG_DWORD_INFO_EX(INTERNAL_BreakOnNotify, W("BreakOnNotify"), 0, "", CLRConfig::REGUTIL_default) RETAIL_CONFIG_DWORD_INFO_EX(INTERNAL_BreakOnRetailAssert, W("BreakOnRetailAssert"), 0, "Used for debugging \"retail\" asserts (fatal errors)", CLRConfig::REGUTIL_default) CONFIG_DWORD_INFO_EX(INTERNAL_BreakOnSecondPass, W("BreakOnSecondPass"), 0, "", CLRConfig::REGUTIL_default) diff --git a/src/pal/prebuilt/inc/mscoree.h b/src/pal/prebuilt/inc/mscoree.h index 42a97c0..ab7bbb0 100644 --- a/src/pal/prebuilt/inc/mscoree.h +++ b/src/pal/prebuilt/inc/mscoree.h @@ -229,8 +229,7 @@ enum __MIDL___MIDL_itf_mscoree_0000_0000_0009 eExitProcess = ( eRudeUnloadAppDomain + 1 ) , eFastExitProcess = ( eExitProcess + 1 ) , eRudeExitProcess = ( eFastExitProcess + 1 ) , - eDisableRuntime = ( eRudeExitProcess + 1 ) , - MaxPolicyAction = ( eDisableRuntime + 1 ) + MaxPolicyAction = (eRudeExitProcess + 1 ) } EPolicyAction; diff --git a/src/utilcode/posterror.cpp b/src/utilcode/posterror.cpp index 3c97db7..a15128a 100644 --- a/src/utilcode/posterror.cpp +++ b/src/utilcode/posterror.cpp @@ -28,16 +28,6 @@ // Local prototypes. HRESULT FillErrorInfo(LPCWSTR szMsg, DWORD dwHelpContext); -//***************************************************************************** -// Function that we'll expose to the outside world to fire off the shutdown method -//***************************************************************************** -#ifdef SHOULD_WE_CLEANUP -void ShutdownCompRC() -{ - CCompRC::ShutdownDefaultResourceDll(); -} -#endif /* SHOULD_WE_CLEANUP */ - void GetResourceCultureCallbacks( FPGETTHREADUICULTURENAMES* fpGetThreadUICultureNames, FPGETTHREADUICULTUREID* fpGetThreadUICultureId) diff --git a/src/vm/appdomain.cpp b/src/vm/appdomain.cpp index abb9ac1..9ac4478 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 9ac0cc6..3ca3356 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 ab2da65..50071ed 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 a6ecda0..2da1c70 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 4d24b21..52cab11 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 15e9787..ad3386e 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 f11dd15..d5063b2 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 af41def..f44f32a 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 5cc5e37..d219ac3 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 43976a9..70c2611 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 1fee3b7..9760fe4 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 68473b6..e4b7908 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 b501496..f340542 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; } -- 2.7.4