Skip to content

Commit

Permalink
[NativeAOT] WindowsUse RtlDllShutdownInProgress to detect process
Browse files Browse the repository at this point in the history
shutdown

Port dotnet#103877 to Native AOT. This is fixing intermittent shutdown hangs
that can observed by running the tests attached to dotnet#103877.
  • Loading branch information
jkotas committed Jul 2, 2024
1 parent a674338 commit 5b1c5b3
Show file tree
Hide file tree
Showing 5 changed files with 34 additions and 34 deletions.
2 changes: 1 addition & 1 deletion src/coreclr/nativeaot/Runtime/EHHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
6 changes: 0 additions & 6 deletions src/coreclr/nativeaot/Runtime/PalRedhawk.h
Original file line number Diff line number Diff line change
Expand Up @@ -475,10 +475,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)
Expand Down Expand Up @@ -679,8 +675,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);
Expand Down
53 changes: 32 additions & 21 deletions src/coreclr/nativeaot/Runtime/startup.cpp
Original file line number Diff line number Diff line change
@@ -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 <windows.h>
#endif
#include "CommonTypes.h"
#include "CommonMacros.h"
#include "daccess.h"
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -284,23 +287,30 @@ 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.
//
// On POSIX OSes a process typically lives as long as any of its threads are alive or until
// 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
Expand All @@ -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.
Expand All @@ -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.
Expand All @@ -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

Expand Down
5 changes: 0 additions & 5 deletions src/coreclr/nativeaot/Runtime/windows/PalRedhawkMinWin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -815,11 +815,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
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/vm/vars.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down

0 comments on commit 5b1c5b3

Please sign in to comment.