From 5bc0f836763c8bcf3fe92274810b99726d32b649 Mon Sep 17 00:00:00 2001 From: Neale Ferguson Date: Fri, 12 Jul 2024 06:05:28 +1000 Subject: [PATCH] Provide fix for #81093 - "Mono does not emit ProcessExit event on SIGTERM" (#100056) * src/mono/mono/mini/mini-posix.c - Add signal handler for SIGTERM * src/mono/mono/mini/mini-windows.c - Add signal handler for SIGTERM - Use the correct signal for handler * src/mono/mono/mini/mini-runtime.c - Add mono_sigterm_signal_handler to process SIGTERM that will set a global variable to be monitored by the GC finalizer thread - Set a default exit code before setting the term_signaled variable that gets checked in gc * src/mono/mono/mini/mini-runtime.h - Define prototype for mono_sigterm_signal_handler() * src/mono/mono/metadata/gc.c - Monitor for sigterm and kick off the shutdown process when encountered by calling mono_runtime_try_shutdown(). - Exit with either the user set exitcode (System.Environment.ExitCode) or SIGTERM + 128. - Simplify use of exit code now that a default is being set - Rename term_signaled to match mono style - Remove volatile attribute - Move testing of shutdown until after the sem wait * src/libraries/System.Runtime/tests/System/ExitCodeTests.Unix.cs - Re-enable ExitCodeTests for mono * src/mono/mono/mini/exceptions-amd64.c src/mono/mono/mini/exceptions-x86.c - Add control event handling for windows Co-authored-by: Jo Shields --- .../System/ExitCodeTests.Unix.cs | 1 - src/mono/mono/metadata/gc.c | 10 ++++++ src/mono/mono/mini/exceptions-amd64.c | 34 +++++++++++++++++++ src/mono/mono/mini/exceptions-x86.c | 31 +++++++++++++++++ src/mono/mono/mini/mini-posix.c | 2 ++ src/mono/mono/mini/mini-runtime.c | 12 +++++++ src/mono/mono/mini/mini-runtime.h | 1 + src/mono/mono/mini/mini-windows.c | 1 + 8 files changed, 91 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ExitCodeTests.Unix.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ExitCodeTests.Unix.cs index 9fd7ab4f461a98..bd7cf68e87c366 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ExitCodeTests.Unix.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ExitCodeTests.Unix.cs @@ -16,7 +16,6 @@ public class ExitCodeTests private static extern int kill(int pid, int sig); [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/31656", TestRuntimes.Mono)] [InlineData(null)] [InlineData(0)] [InlineData(42)] diff --git a/src/mono/mono/metadata/gc.c b/src/mono/mono/metadata/gc.c index 49fd0c2553dfef..b46971f1a9a7e0 100644 --- a/src/mono/mono/metadata/gc.c +++ b/src/mono/mono/metadata/gc.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -63,6 +64,8 @@ static gboolean gc_disabled; static gboolean finalizing_root_domain; +extern gboolean mono_term_signaled; + gboolean mono_log_finalizers; gboolean mono_do_not_finalize; static volatile gboolean suspend_finalizers; @@ -852,6 +855,7 @@ finalizer_thread (gpointer unused) mono_hazard_pointer_install_free_queue_size_callback (hazard_free_queue_is_too_big); while (!finished) { + /* Wait to be notified that there's at least one * finaliser to run */ @@ -865,6 +869,12 @@ finalizer_thread (gpointer unused) } wait = TRUE; + /* Just in case we've received a SIGTERM */ + if (mono_term_signaled) { + mono_runtime_try_shutdown(); + exit(mono_environment_exitcode_get()); + } + mono_thread_info_set_flags (MONO_THREAD_INFO_FLAGS_NONE); /* The Finalizer thread doesn't initialize during creation because base managed diff --git a/src/mono/mono/mini/exceptions-amd64.c b/src/mono/mono/mini/exceptions-amd64.c index 5049bc799700ff..617a551dc80875 100644 --- a/src/mono/mono/mini/exceptions-amd64.c +++ b/src/mono/mono/mini/exceptions-amd64.c @@ -45,10 +45,14 @@ #include "mono/utils/mono-tls-inline.h" #ifdef TARGET_WIN32 +#include static void (*restore_stack) (void); static MonoW32ExceptionHandler fpe_handler; static MonoW32ExceptionHandler ill_handler; static MonoW32ExceptionHandler segv_handler; +static MonoW32ExceptionHandler term_handler = NULL; + +extern gboolean mono_term_signaled; LPTOP_LEVEL_EXCEPTION_FILTER mono_old_win_toplevel_exception_filter; void *mono_win_vectored_exception_handle; @@ -214,6 +218,31 @@ void win32_seh_cleanup(void) g_assert (ret); } +BOOL WINAPI mono_win_ctrl_handler(DWORD fdwCtrlType) +{ + switch (fdwCtrlType) { + case CTRL_C_EVENT: + if (term_handler != NULL) + term_handler(0, NULL, NULL); + return TRUE; + break; + case CTRL_CLOSE_EVENT: + return TRUE; + break; + case CTRL_BREAK_EVENT: + return FALSE; + break; + case CTRL_LOGOFF_EVENT: + return FALSE; + break; + case CTRL_SHUTDOWN_EVENT: + return FALSE; + break; + default: + return FALSE; + } +} + void win32_seh_set_handler(int type, MonoW32ExceptionHandler handler) { switch (type) { @@ -226,6 +255,11 @@ void win32_seh_set_handler(int type, MonoW32ExceptionHandler handler) case SIGSEGV: segv_handler = handler; break; + case SIGTERM: + term_handler = handler; + if (!SetConsoleCtrlHandler(mono_win_ctrl_handler, TRUE)) + fprintf(stderr,"Cannot set control handler\n"); + break; default: break; } diff --git a/src/mono/mono/mini/exceptions-x86.c b/src/mono/mono/mini/exceptions-x86.c index 47ba88dcf92923..366228f75c1b52 100644 --- a/src/mono/mono/mini/exceptions-x86.c +++ b/src/mono/mono/mini/exceptions-x86.c @@ -47,6 +47,7 @@ static void (*restore_stack) (void *); static MonoW32ExceptionHandler fpe_handler; static MonoW32ExceptionHandler ill_handler; static MonoW32ExceptionHandler segv_handler; +static MonoW32ExceptionHandler term_handler = NULL; LPTOP_LEVEL_EXCEPTION_FILTER mono_old_win_toplevel_exception_filter; gpointer mono_win_vectored_exception_handle; @@ -260,6 +261,31 @@ void win32_seh_cleanup(void) RemoveVectoredExceptionHandler (mono_win_vectored_exception_handle); } +BOOL WINAPI mono_win_ctrl_handler(DWORD fdwCtrlType) +{ + switch (fdwCtrlType) { + case CTRL_C_EVENT: + if (term_handler != NULL) + term_handler(0, NULL, NULL); + return TRUE; + break; + case CTRL_CLOSE_EVENT: + return TRUE; + break; + case CTRL_BREAK_EVENT: + return FALSE; + break; + case CTRL_LOGOFF_EVENT: + return FALSE; + break; + case CTRL_SHUTDOWN_EVENT: + return FALSE; + break; + default: + return FALSE; + } +} + void win32_seh_set_handler(int type, MonoW32ExceptionHandler handler) { switch (type) { @@ -272,6 +298,11 @@ void win32_seh_set_handler(int type, MonoW32ExceptionHandler handler) case SIGSEGV: segv_handler = handler; break; + case SIGTERM: + term_handler = handler; + if (!SetConsoleCtrlHandler(mono_win_ctrl_handler, TRUE)) + fprintf(stderr,"Cannot set control handler\n"); + break; default: break; } diff --git a/src/mono/mono/mini/mini-posix.c b/src/mono/mono/mini/mini-posix.c index 6b1621bdaa0582..075cdd07081a50 100644 --- a/src/mono/mono/mini/mini-posix.c +++ b/src/mono/mono/mini/mini-posix.c @@ -390,6 +390,8 @@ mono_runtime_posix_install_handlers (void) sigaddset (&signal_set, SIGFPE); add_signal_handler (SIGQUIT, sigquit_signal_handler, SA_RESTART); sigaddset (&signal_set, SIGQUIT); + add_signal_handler (SIGTERM, mono_sigterm_signal_handler, SA_RESTART); + sigaddset (&signal_set, SIGTERM); add_signal_handler (SIGILL, mono_crashing_signal_handler, 0); sigaddset (&signal_set, SIGILL); add_signal_handler (SIGBUS, mono_sigsegv_signal_handler, 0); diff --git a/src/mono/mono/mini/mini-runtime.c b/src/mono/mono/mini/mini-runtime.c index 240aa1f8a9b8bc..4609de0510518e 100644 --- a/src/mono/mono/mini/mini-runtime.c +++ b/src/mono/mono/mini/mini-runtime.c @@ -119,6 +119,7 @@ const char *mono_build_date; gboolean mono_do_signal_chaining; gboolean mono_do_crash_chaining; int mini_verbose = 0; +gboolean mono_term_signaled = FALSE; /* * This flag controls whenever the runtime uses LLVM for JIT compilation, and whenever @@ -3758,6 +3759,17 @@ MONO_SIG_HANDLER_FUNC (, mono_crashing_signal_handler) } } +MONO_SIG_HANDLER_FUNC (, mono_sigterm_signal_handler) +{ + mono_environment_exitcode_set(128+SIGTERM); /* Set default exit code */ + + mono_term_signaled = TRUE; + + mono_gc_finalize_notify (); + + mono_chain_signal (MONO_SIG_HANDLER_PARAMS); +} + #if defined(MONO_ARCH_USE_SIGACTION) || defined(HOST_WIN32) #define HAVE_SIG_INFO diff --git a/src/mono/mono/mini/mini-runtime.h b/src/mono/mono/mini/mini-runtime.h index afdfbc78ba6d8a..5104a053b30c77 100644 --- a/src/mono/mono/mini/mini-runtime.h +++ b/src/mono/mono/mini/mini-runtime.h @@ -676,6 +676,7 @@ void MONO_SIG_HANDLER_SIGNATURE (mono_sigfpe_signal_handler) ; void MONO_SIG_HANDLER_SIGNATURE (mono_crashing_signal_handler) ; void MONO_SIG_HANDLER_SIGNATURE (mono_sigsegv_signal_handler); void MONO_SIG_HANDLER_SIGNATURE (mono_sigint_signal_handler) ; +void MONO_SIG_HANDLER_SIGNATURE (mono_sigterm_signal_handler) ; gboolean MONO_SIG_HANDLER_SIGNATURE (mono_chain_signal); #if defined (HOST_WASM) diff --git a/src/mono/mono/mini/mini-windows.c b/src/mono/mono/mini/mini-windows.c index ba47da233f3f15..322488abdaab76 100644 --- a/src/mono/mono/mini/mini-windows.c +++ b/src/mono/mono/mini/mini-windows.c @@ -188,6 +188,7 @@ mono_runtime_install_handlers (void) win32_seh_set_handler(SIGFPE, mono_sigfpe_signal_handler); win32_seh_set_handler(SIGILL, mono_crashing_signal_handler); win32_seh_set_handler(SIGSEGV, mono_sigsegv_signal_handler); + win32_seh_set_handler(SIGTERM, mono_sigterm_signal_handler); if (mini_debug_options.handle_sigint) win32_seh_set_handler(SIGINT, mono_sigint_signal_handler); #endif