summary refs log tree commit diff stats
diff options
context:
space:
mode:
authoralaviss <leorize+oss@disroot.org>2021-02-19 08:29:21 +0000
committerGitHub <noreply@github.com>2021-02-19 00:29:21 -0800
commitccc0667c29256ec1303e1d4280f013b380d85828 (patch)
tree88fda1a9341491f8afdff001a034e1a537ee0ddd
parent95664e15247d678476d64a1c81f7c19b163e823c (diff)
downloadNim-ccc0667c29256ec1303e1d4280f013b380d85828.tar.gz
system/excpt: let the OS handle termination on signal (#16712)
-rw-r--r--changelog.md8
-rw-r--r--lib/system/ansi_c.nim9
-rw-r--r--lib/system/excpt.nim12
-rw-r--r--tests/system/tsigexitcode.nim20
4 files changed, 46 insertions, 3 deletions
diff --git a/changelog.md b/changelog.md
index d016d2604..3f8ee3c3d 100644
--- a/changelog.md
+++ b/changelog.md
@@ -159,6 +159,14 @@ provided by the operating system.
   `ValueError` when the real command line is not available. `parseopt` was
   previously excluded from `prelude` for JS, as it could not be imported.
 
+- On POSIX systems, the default signal handlers used for Nim programs (it's
+  used for printing the stacktrace on fatal signals) will now re-raise the
+  signal for the OS default handlers to handle.
+
+  This lets the OS perform its default actions, which might include core
+  dumping (on select signals) and notifying the parent process about the cause
+  of termination.
+
 ## Language changes
 
 - `nimscript` now handles `except Exception as e`.
diff --git a/lib/system/ansi_c.nim b/lib/system/ansi_c.nim
index 68d3eb9b5..7e156eaab 100644
--- a/lib/system/ansi_c.nim
+++ b/lib/system/ansi_c.nim
@@ -40,6 +40,7 @@ else:
     C_JmpBuf* {.importc: "jmp_buf", header: "<setjmp.h>".} = object
 
 
+type CSighandlerT = proc (a: cint) {.noconv.}
 when defined(windows):
   const
     SIGABRT* = cint(22)
@@ -48,6 +49,7 @@ when defined(windows):
     SIGINT* = cint(2)
     SIGSEGV* = cint(11)
     SIGTERM = cint(15)
+    SIG_DFL* = cast[CSighandlerT](0)
 elif defined(macosx) or defined(linux) or defined(freebsd) or
      defined(openbsd) or defined(netbsd) or defined(solaris) or
      defined(dragonfly) or defined(nintendoswitch) or defined(genode) or
@@ -60,6 +62,7 @@ elif defined(macosx) or defined(linux) or defined(freebsd) or
     SIGSEGV* = cint(11)
     SIGTERM* = cint(15)
     SIGPIPE* = cint(13)
+    SIG_DFL* = cast[CSighandlerT](0)
 elif defined(haiku):
   const
     SIGABRT* = cint(6)
@@ -69,6 +72,7 @@ elif defined(haiku):
     SIGSEGV* = cint(11)
     SIGTERM* = cint(15)
     SIGPIPE* = cint(7)
+    SIG_DFL* = cast[CSighandlerT](0)
 else:
   when NoFakeVars:
     {.error: "SIGABRT not ported to your platform".}
@@ -79,6 +83,7 @@ else:
       SIGABRT* {.importc: "SIGABRT", nodecl.}: cint
       SIGFPE* {.importc: "SIGFPE", nodecl.}: cint
       SIGILL* {.importc: "SIGILL", nodecl.}: cint
+      SIG_DFL* {.importc: "SIG_DFL", nodecl.}: CSighandlerT
     when defined(macosx) or defined(linux):
       var SIGPIPE* {.importc: "SIGPIPE", nodecl.}: cint
 
@@ -105,9 +110,9 @@ else:
   proc c_setjmp*(jmpb: C_JmpBuf): cint {.
     header: "<setjmp.h>", importc: "setjmp".}
 
-type CSighandlerT = proc (a: cint) {.noconv.}
-proc c_signal*(sign: cint, handler: proc (a: cint) {.noconv.}): CSighandlerT {.
+proc c_signal*(sign: cint, handler: CSighandlerT): CSighandlerT {.
   importc: "signal", header: "<signal.h>", discardable.}
+proc c_raise*(sign: cint): cint {.importc: "raise", header: "<signal.h>".}
 
 type
   CFile {.importc: "FILE", header: "<stdio.h>",
diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim
index dbb39f536..19bf8911d 100644
--- a/lib/system/excpt.nim
+++ b/lib/system/excpt.nim
@@ -643,7 +643,17 @@ when not defined(noSignalHandler) and not defined(useNimRtl):
       # unless there's a good reason to use cstring in signal handler to avoid
       # using gc?
       showErrorMessage(msg, msg.len)
-    quit(1) # always quit when SIGABRT
+
+    when defined(posix):
+      # reset the signal handler to OS default
+      c_signal(sign, SIG_DFL)
+
+      # re-raise the signal, which will arrive once this handler exit.
+      # this lets the OS perform actions like core dumping and will
+      # also return the correct exit code to the shell.
+      discard c_raise(sign)
+    else:
+      quit(1)
 
   proc registerSignalHandler() =
     c_signal(SIGINT, signalHandler)
diff --git a/tests/system/tsigexitcode.nim b/tests/system/tsigexitcode.nim
new file mode 100644
index 000000000..6922cb8eb
--- /dev/null
+++ b/tests/system/tsigexitcode.nim
@@ -0,0 +1,20 @@
+discard """
+  joinable: false
+  disabled: windows
+"""
+
+import os, osproc, posix, strutils
+
+proc main() =
+  if paramCount() > 0:
+    let signal = cint parseInt paramStr(1)
+    discard posix.raise(signal)
+  else:
+    # synchronize this list with lib/system/except.nim:registerSignalHandler()
+    let fatalSigs = [SIGINT, SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS,
+                     SIGPIPE]
+    for s in fatalSigs:
+      let (_, exitCode) = execCmdEx(quoteShellCommand [getAppFilename(), $s])
+      doAssert exitCode == 128 + s, "mismatched exit code for signal " & $s
+
+main()