From 752558f4be7d19bbd3df1f94e8d4f5e655f8c28a Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Tue, 2 Jul 2024 14:50:30 -0700 Subject: [PATCH] [NativeAOT] WindowsUse RtlDllShutdownInProgress to detect process (#104318) shutdown Port #103877 to Native AOT. This is fixing intermittent shutdown hangs that can observed by running the tests attached to #103877. --- src/coreclr/nativeaot/Runtime/EHHelpers.cpp | 2 +- src/coreclr/nativeaot/Runtime/PalRedhawk.h | 6 --- src/coreclr/nativeaot/Runtime/startup.cpp | 53 +++++++++++-------- .../Runtime/windows/PalRedhawkMinWin.cpp | 5 -- src/coreclr/vm/vars.hpp | 2 +- 5 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/coreclr/nativeaot/Runtime/EHHelpers.cpp b/src/coreclr/nativeaot/Runtime/EHHelpers.cpp index dc0e6fe1b75be..2cb6c6bad1ddf 100644 --- a/src/coreclr/nativeaot/Runtime/EHHelpers.cpp +++ b/src/coreclr/nativeaot/Runtime/EHHelpers.cpp @@ -214,7 +214,7 @@ EXTERN_C void QCALLTYPE RhpFailFastForPInvokeExceptionPreemp(intptr_t PInvokeCal void* pExceptionRecord, void* pContextRecord); FCDECL3(void, RhpFailFastForPInvokeExceptionCoop, intptr_t PInvokeCallsiteReturnAddr, void* pExceptionRecord, void* pContextRecord); -int32_t __stdcall RhpVectoredExceptionHandler(PEXCEPTION_POINTERS pExPtrs); +EXTERN_C int32_t __stdcall RhpVectoredExceptionHandler(PEXCEPTION_POINTERS pExPtrs); EXTERN_C int32_t __stdcall RhpPInvokeExceptionGuard(PEXCEPTION_RECORD pExceptionRecord, uintptr_t EstablisherFrame, diff --git a/src/coreclr/nativeaot/Runtime/PalRedhawk.h b/src/coreclr/nativeaot/Runtime/PalRedhawk.h index b0a1606a11537..f2a73b7f06f44 100644 --- a/src/coreclr/nativeaot/Runtime/PalRedhawk.h +++ b/src/coreclr/nativeaot/Runtime/PalRedhawk.h @@ -476,10 +476,6 @@ typedef struct _EXCEPTION_POINTERS { PCONTEXT ContextRecord; } EXCEPTION_POINTERS, *PEXCEPTION_POINTERS; -typedef int32_t (__stdcall *PVECTORED_EXCEPTION_HANDLER)( - PEXCEPTION_POINTERS ExceptionInfo - ); - #define EXCEPTION_CONTINUE_EXECUTION (-1) #define EXCEPTION_CONTINUE_SEARCH (0) #define EXCEPTION_EXECUTE_HANDLER (1) @@ -681,8 +677,6 @@ struct UNIX_CONTEXT; #ifdef TARGET_UNIX REDHAWK_PALIMPORT uint32_t REDHAWK_PALAPI PalGetOsPageSize(); REDHAWK_PALIMPORT void REDHAWK_PALAPI PalSetHardwareExceptionHandler(PHARDWARE_EXCEPTION_HANDLER handler); -#else -REDHAWK_PALIMPORT void* REDHAWK_PALAPI PalAddVectoredExceptionHandler(uint32_t firstHandler, _In_ PVECTORED_EXCEPTION_HANDLER vectoredHandler); #endif typedef uint32_t (__stdcall *BackgroundCallback)(_In_opt_ void* pCallbackContext); diff --git a/src/coreclr/nativeaot/Runtime/startup.cpp b/src/coreclr/nativeaot/Runtime/startup.cpp index f87bc947d970a..db2802dcb115e 100644 --- a/src/coreclr/nativeaot/Runtime/startup.cpp +++ b/src/coreclr/nativeaot/Runtime/startup.cpp @@ -1,6 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. #include "common.h" +#ifdef HOST_WINDOWS +#include +#endif #include "CommonTypes.h" #include "CommonMacros.h" #include "daccess.h" @@ -36,10 +39,10 @@ uint64_t g_startupTimelineEvents[NUM_STARTUP_TIMELINE_EVENTS] = { 0 }; #endif // PROFILE_STARTUP -#ifdef TARGET_UNIX -int32_t RhpHardwareExceptionHandler(uintptr_t faultCode, uintptr_t faultAddress, PAL_LIMITED_CONTEXT* palContext, uintptr_t* arg0Reg, uintptr_t* arg1Reg); +#ifdef HOST_WINDOWS +EXTERN_C LONG WINAPI RhpVectoredExceptionHandler(PEXCEPTION_POINTERS pExPtrs); #else -int32_t __stdcall RhpVectoredExceptionHandler(PEXCEPTION_POINTERS pExPtrs); +int32_t RhpHardwareExceptionHandler(uintptr_t faultCode, uintptr_t faultAddress, PAL_LIMITED_CONTEXT* palContext, uintptr_t* arg0Reg, uintptr_t* arg1Reg); #endif extern "C" void PopulateDebugHeaders(); @@ -123,8 +126,8 @@ static bool InitDLL(HANDLE hPalInstance) // Note: The global exception handler uses RuntimeInstance #if !defined(USE_PORTABLE_HELPERS) -#ifndef TARGET_UNIX - PalAddVectoredExceptionHandler(1, RhpVectoredExceptionHandler); +#ifdef HOST_WINDOWS + AddVectoredExceptionHandler(1, RhpVectoredExceptionHandler); #else PalSetHardwareExceptionHandler(RhpHardwareExceptionHandler); #endif @@ -284,7 +287,7 @@ static void UninitDLL() #endif // PROFILE_STARTUP } -#ifdef _WIN32 +#ifdef HOST_WINDOWS // This is set to the thread that initiates and performs the shutdown and may run // after other threads are rudely terminated. So far this is a Windows-specific concern. // @@ -292,15 +295,22 @@ static void UninitDLL() // the process is terminated via `exit()` or a signal. Thus there is no such distinction // between threads. Thread* g_threadPerformingShutdown = NULL; + +static BOOLEAN WINAPI RtlDllShutdownInProgressFallback() +{ + return g_threadPerformingShutdown == ThreadStore::GetCurrentThread(); +} +typedef BOOLEAN (WINAPI* PRTLDLLSHUTDOWNINPROGRESS)(); +PRTLDLLSHUTDOWNINPROGRESS g_pfnRtlDllShutdownInProgress = &RtlDllShutdownInProgressFallback; #endif -#if defined(_WIN32) && defined(FEATURE_PERFTRACING) +#if defined(HOST_WINDOWS) && defined(FEATURE_PERFTRACING) bool g_safeToShutdownTracing; #endif static void __cdecl OnProcessExit() { -#ifdef _WIN32 +#ifdef HOST_WINDOWS // The process is exiting and the current thread is performing the shutdown. // When this thread exits some threads may be already rudely terminated. // It would not be a good idea for this thread to wait on any locks @@ -310,7 +320,7 @@ static void __cdecl OnProcessExit() #endif #ifdef FEATURE_PERFTRACING -#ifdef _WIN32 +#ifdef HOST_WINDOWS // We forgo shutting down event pipe if it wouldn't be safe and could lead to a hang. // If there was an active trace session, the trace will likely be corrupted without // orderly shutdown. See https://github.com/dotnet/runtime/issues/89346. @@ -325,18 +335,11 @@ static void __cdecl OnProcessExit() void RuntimeThreadShutdown(void* thread) { - // Note: loader lock is normally *not* held here! - // The one exception is that the loader lock may be held during the thread shutdown callback - // that is made for the single thread that runs the final stages of orderly process - // shutdown (i.e., the thread that delivers the DLL_PROCESS_DETACH notifications when the - // process is being torn down via an ExitProcess call). - // In such case we do not detach. - -#ifdef _WIN32 +#ifdef HOST_WINDOWS ASSERT((Thread*)thread == ThreadStore::GetCurrentThread()); - // Do not try detaching the thread that performs the shutdown. - if (g_threadPerformingShutdown == thread) + // Do not try detaching the thread during process shutdown. + if (g_pfnRtlDllShutdownInProgress()) { // At this point other threads could be terminated rudely while leaving runtime // in inconsistent state, so we would be risking blocking the process from exiting. @@ -362,11 +365,19 @@ extern "C" bool RhInitialize(bool isDll) if (!PalInit()) return false; -#if defined(_WIN32) || defined(FEATURE_PERFTRACING) +#if defined(HOST_WINDOWS) + // RtlDllShutdownInProgress provides more accurate information about whether the process is shutting down. + // Use it if it is available to avoid shutdown deadlocks. + PRTLDLLSHUTDOWNINPROGRESS pfn = (PRTLDLLSHUTDOWNINPROGRESS)GetProcAddress(GetModuleHandleW(W("ntdll.dll")), "RtlDllShutdownInProgress"); + if (pfn != NULL) + g_pfnRtlDllShutdownInProgress = pfn; +#endif + +#if defined(HOST_WINDOWS) || defined(FEATURE_PERFTRACING) atexit(&OnProcessExit); #endif -#if defined(_WIN32) && defined(FEATURE_PERFTRACING) +#if defined(HOST_WINDOWS) && defined(FEATURE_PERFTRACING) g_safeToShutdownTracing = !isDll; #endif diff --git a/src/coreclr/nativeaot/Runtime/windows/PalRedhawkMinWin.cpp b/src/coreclr/nativeaot/Runtime/windows/PalRedhawkMinWin.cpp index 2b52cfd78032f..216b017054836 100644 --- a/src/coreclr/nativeaot/Runtime/windows/PalRedhawkMinWin.cpp +++ b/src/coreclr/nativeaot/Runtime/windows/PalRedhawkMinWin.cpp @@ -868,11 +868,6 @@ REDHAWK_PALEXPORT HANDLE REDHAWK_PALAPI PalGetModuleHandleFromPointer(_In_ void* return (HANDLE)module; } -REDHAWK_PALEXPORT void* REDHAWK_PALAPI PalAddVectoredExceptionHandler(uint32_t firstHandler, _In_ PVECTORED_EXCEPTION_HANDLER vectoredHandler) -{ - return AddVectoredExceptionHandler(firstHandler, vectoredHandler); -} - REDHAWK_PALEXPORT void PalPrintFatalError(const char* message) { // Write the message using lowest-level OS API available. This is used to print the stack overflow diff --git a/src/coreclr/vm/vars.hpp b/src/coreclr/vm/vars.hpp index aa92365f0896a..85069abdad9d9 100644 --- a/src/coreclr/vm/vars.hpp +++ b/src/coreclr/vm/vars.hpp @@ -472,7 +472,7 @@ inline bool IsAtProcessExit() #if defined(DACCESS_COMPILE) || !defined(HOST_WINDOWS) return g_fProcessDetach; #else - // RtlDllShutdownInProgress provides more accurace information about whether the process is shutting down. + // RtlDllShutdownInProgress provides more accurate information about whether the process is shutting down. // Use it if it is available to avoid shutdown deadlocks. // https://learn.microsoft.com/windows/win32/devnotes/rtldllshutdowninprogress return g_pfnRtlDllShutdownInProgress();