From b900d678b481d0e53f788396d4494bc607dfa0fd Mon Sep 17 00:00:00 2001 From: Jonathan Conder Date: Tue, 18 Oct 2022 17:25:56 +1300 Subject: [PATCH] Add option to only install essential signal handlers When embedding Julia, it is useful to opt out of some of the signal handlers (especially SIGINT). But opting out of SIGSEGV makes it very difficult to use multiple Julia threads, since the GC safepoint implementation relies on segv_handler(). This patch provides a third option to embedders, which installs the essential signal handlers but skips the optional ones. It might make sense for this to be the default when embedding, but I've kept the other two options for backwards compatibility. --- src/init.c | 4 +- src/julia.h | 1 + src/signals-unix.c | 91 +++++++++++++++++++++++++++++----------------- src/task.c | 2 +- 4 files changed, 61 insertions(+), 37 deletions(-) diff --git a/src/init.c b/src/init.c index 02769e03c668e5..783a4969f4df1c 100644 --- a/src/init.c +++ b/src/init.c @@ -766,7 +766,7 @@ JL_DLLEXPORT void julia_init(JL_IMAGE_SEARCH rel) jl_init_uv(); init_stdio(); restore_fp_env(); - if (jl_options.handle_signals == JL_OPTIONS_HANDLE_SIGNALS_ON) + if (jl_options.handle_signals != JL_OPTIONS_HANDLE_SIGNALS_OFF) restore_signals(); jl_init_intrinsic_properties(); @@ -811,7 +811,7 @@ JL_DLLEXPORT void julia_init(JL_IMAGE_SEARCH rel) jl_init_tasks(); jl_init_threading(); jl_init_threadinginfra(); - if (jl_options.handle_signals == JL_OPTIONS_HANDLE_SIGNALS_ON) + if (jl_options.handle_signals != JL_OPTIONS_HANDLE_SIGNALS_OFF) jl_install_default_signal_handlers(); jl_gc_init(); diff --git a/src/julia.h b/src/julia.h index d214509c7d0b6a..38a0c024be9bf2 100644 --- a/src/julia.h +++ b/src/julia.h @@ -2287,6 +2287,7 @@ JL_DLLEXPORT int jl_generating_output(void) JL_NOTSAFEPOINT; #define JL_OPTIONS_HANDLE_SIGNALS_ON 1 #define JL_OPTIONS_HANDLE_SIGNALS_OFF 0 +#define JL_OPTIONS_HANDLE_SIGNALS_ESSENTIAL 2 #define JL_OPTIONS_USE_SYSIMAGE_NATIVE_CODE_YES 1 #define JL_OPTIONS_USE_SYSIMAGE_NATIVE_CODE_NO 0 diff --git a/src/signals-unix.c b/src/signals-unix.c index 4c21d25d3622c3..b5cd46d0a9a54e 100644 --- a/src/signals-unix.c +++ b/src/signals-unix.c @@ -645,7 +645,10 @@ void jl_install_thread_signal_handler(jl_ptls_t ptls) } const static int sigwait_sigs[] = { - SIGINT, SIGTERM, SIGQUIT, + SIGINT, SIGTERM, SIGQUIT, 0, +}; + +const static int sigwait_sigs_essential[] = { #ifdef SIGINFO SIGINFO, #else @@ -660,7 +663,11 @@ const static int sigwait_sigs[] = { static void jl_sigsetset(sigset_t *sset) { sigemptyset(sset); - for (const int *sig = sigwait_sigs; *sig; sig++) + if (jl_options.handle_signals == JL_OPTIONS_HANDLE_SIGNALS_ON) { + for (const int *sig = sigwait_sigs; *sig; sig++) + sigaddset(sset, *sig); + } + for (const int *sig = sigwait_sigs_essential; *sig; sig++) sigaddset(sset, *sig); } @@ -719,11 +726,19 @@ static void *signal_listener(void *arg) perror("signal kqueue"); } else { - for (const int *sig = sigwait_sigs; *sig; sig++) + if (jl_options.handle_signals == JL_OPTIONS_HANDLE_SIGNALS_ON) { + for (const int *sig = sigwait_sigs; *sig; sig++) + kqueue_signal(&sigqueue, &ev, *sig); + } + for (const int *sig = sigwait_sigs_essential; *sig; sig++) kqueue_signal(&sigqueue, &ev, *sig); if (sigqueue == -1) { // re-enable sigwait for these - for (const int *sig = sigwait_sigs; *sig; sig++) + if (jl_options.handle_signals == JL_OPTIONS_HANDLE_SIGNALS_ON) { + for (const int *sig = sigwait_sigs; *sig; sig++) + signal(*sig, SIG_DFL); + } + for (const int *sig = sigwait_sigs_essential; *sig; sig++) signal(*sig, SIG_DFL); } } @@ -742,7 +757,11 @@ static void *signal_listener(void *arg) if (nevents != 1) { close(sigqueue); sigqueue = -1; - for (const int *sig = sigwait_sigs; *sig; sig++) + if (jl_options.handle_signals == JL_OPTIONS_HANDLE_SIGNALS_ON) { + for (const int *sig = sigwait_sigs; *sig; sig++) + signal(*sig, SIG_DFL); + } + for (const int *sig = sigwait_sigs_essential; *sig; sig++) signal(*sig, SIG_DFL); continue; } @@ -996,27 +1015,29 @@ static void sigint_handler(int sig) void jl_install_default_signal_handlers(void) { - struct sigaction actf; - memset(&actf, 0, sizeof(struct sigaction)); - sigemptyset(&actf.sa_mask); - actf.sa_sigaction = fpe_handler; - actf.sa_flags = SA_ONSTACK | SA_SIGINFO; - if (sigaction(SIGFPE, &actf, NULL) < 0) { - jl_errorf("fatal error: sigaction: %s", strerror(errno)); - } - struct sigaction actint; - memset(&actint, 0, sizeof(struct sigaction)); - sigemptyset(&actint.sa_mask); - actint.sa_handler = sigint_handler; - actint.sa_flags = 0; - if (sigaction(SIGINT, &actint, NULL) < 0) { - jl_errorf("fatal error: sigaction: %s", strerror(errno)); - } - if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) { - jl_error("fatal error: Couldn't set SIGPIPE"); - } - if (signal(SIGTRAP, SIG_IGN) == SIG_ERR) { - jl_error("fatal error: Couldn't set SIGTRAP"); + if (jl_options.handle_signals == JL_OPTIONS_HANDLE_SIGNALS_ON) { + struct sigaction actf; + memset(&actf, 0, sizeof(struct sigaction)); + sigemptyset(&actf.sa_mask); + actf.sa_sigaction = fpe_handler; + actf.sa_flags = SA_ONSTACK | SA_SIGINFO; + if (sigaction(SIGFPE, &actf, NULL) < 0) { + jl_errorf("fatal error: sigaction: %s", strerror(errno)); + } + struct sigaction actint; + memset(&actint, 0, sizeof(struct sigaction)); + sigemptyset(&actint.sa_mask); + actint.sa_handler = sigint_handler; + actint.sa_flags = 0; + if (sigaction(SIGINT, &actint, NULL) < 0) { + jl_errorf("fatal error: sigaction: %s", strerror(errno)); + } + if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) { + jl_error("fatal error: Couldn't set SIGPIPE"); + } + if (signal(SIGTRAP, SIG_IGN) == SIG_ERR) { + jl_error("fatal error: Couldn't set SIGTRAP"); + } } #if defined(HAVE_MACH) @@ -1039,14 +1060,16 @@ void jl_install_default_signal_handlers(void) sigemptyset(&act_die.sa_mask); act_die.sa_sigaction = sigdie_handler; act_die.sa_flags = SA_SIGINFO | SA_RESETHAND; - if (sigaction(SIGILL, &act_die, NULL) < 0) { - jl_errorf("fatal error: sigaction: %s", strerror(errno)); - } - if (sigaction(SIGABRT, &act_die, NULL) < 0) { - jl_errorf("fatal error: sigaction: %s", strerror(errno)); - } - if (sigaction(SIGSYS, &act_die, NULL) < 0) { - jl_errorf("fatal error: sigaction: %s", strerror(errno)); + if (jl_options.handle_signals == JL_OPTIONS_HANDLE_SIGNALS_ON) { + if (sigaction(SIGILL, &act_die, NULL) < 0) { + jl_errorf("fatal error: sigaction: %s", strerror(errno)); + } + if (sigaction(SIGABRT, &act_die, NULL) < 0) { + jl_errorf("fatal error: sigaction: %s", strerror(errno)); + } + if (sigaction(SIGSYS, &act_die, NULL) < 0) { + jl_errorf("fatal error: sigaction: %s", strerror(errno)); + } } // need to ensure the following signals are not SIG_IGN, even though they will be blocked act_die.sa_flags = SA_SIGINFO | SA_RESTART | SA_RESETHAND; diff --git a/src/task.c b/src/task.c index 477ae481071a0e..a95361c1375cb0 100644 --- a/src/task.c +++ b/src/task.c @@ -1719,7 +1719,7 @@ jl_task_t *jl_init_root_task(jl_ptls_t ptls, void *stack_lo, void *stack_hi) } #endif - if (jl_options.handle_signals == JL_OPTIONS_HANDLE_SIGNALS_ON) + if (jl_options.handle_signals != JL_OPTIONS_HANDLE_SIGNALS_OFF) jl_install_thread_signal_handler(ptls); return ct;