diff options
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 acc415b6d1..1f8de9628e 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 a7ea972b41..505134a5cc 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 4e6ff27e30..0625a97f13 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 66d01ac81a..19bdac6f7a 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 4514043e70..b09fa91a1b 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 b47b305057..aee47df59c 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 1119f07fa4..7d7d16b052 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 26e1bd6bee..fd7f1dd962 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 38e5bba9ad..ce47c14e1b 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 803f1dfca8..95670a382c 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 93e4042816..ce368f3f80 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 441ae8e44b..61258ae09c 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 42a97c0096..ab7bbb0d0c 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 3c97db7ea2..a15128add9 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 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<BOOL> 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<BOOL> 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<LONG> 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; } |