@@ -17,7 +17,7 @@ const noStacktraceAvailable = "No stack traceback available\n"
1717
1818var
1919 errorMessageWriter* : (proc (msg: string ) {.tags : [WriteIOEffect ], benign ,
20- nimcall .})
20+ nimcall , raises : [] .})
2121 # # Function that will be called
2222 # # instead of `stdmsg.write` when printing stacktrace.
2323 # # Unstable API.
@@ -58,6 +58,7 @@ proc showErrorMessage(data: cstring, length: int) {.gcsafe, raises: [].} =
5858 writeToStdErr (data, length)
5959
6060proc showErrorMessage2 (data: string ) {.inline .} =
61+ # TODO showErrorMessage will turn it back to a string when a hook is set (!)
6162 showErrorMessage (data.cstring , data.len)
6263
6364proc chckIndx (i, a, b: int ): int {.inline , compilerproc , benign .}
@@ -619,7 +620,7 @@ when not defined(noSignalHandler) and not defined(useNimRtl):
619620 type Sighandler = proc (a: cint ) {.noconv , benign .}
620621 # xxx factor with ansi_c.CSighandlerT, posix.Sighandler
621622
622- proc signalHandler (sign: cint ) {.exportc : " signalHandler" , noconv .} =
623+ proc signalHandler (sign: cint ) {.exportc : " signalHandler" , noconv , raises : [] .} =
623624 template processSignal (s, action: untyped ) {.dirty .} =
624625 if s == SIGINT : action (" SIGINT: Interrupted by Ctrl-C.\n " )
625626 elif s == SIGSEGV :
@@ -641,6 +642,22 @@ when not defined(noSignalHandler) and not defined(useNimRtl):
641642 # print stack trace and quit
642643 when defined (memtracker):
643644 logPendingOps ()
645+ # On windows, it is common that the signal handler is called from a non-Nim
646+ # thread and any allocation will (likely) cause a crash. Since we're about
647+ # to quit, we can try setting up the GC - the correct course of action is to
648+ # not use the GC at all in signal handlers but that requires redesigning
649+ # the stack trace mechanism
650+ when defined (windows):
651+ when declared (initStackBottom):
652+ initStackBottom ()
653+ when declared (initGC):
654+ initGC ()
655+
656+ # On other platforms, if memory needs to be allocated and the signal happens
657+ # during memory allocation, we'll also (likely) see a crash and corrupt the
658+ # memory allocator - less frequently than on windows but still.
659+ # However, since we're about to go down anyway, YOLO.
660+
644661 when hasSomeStackTrace:
645662 when not usesDestructors: GC_disable ()
646663 var buf = newStringOfCap (2000 )
@@ -653,14 +670,17 @@ when not defined(noSignalHandler) and not defined(useNimRtl):
653670 template asgn (y) =
654671 msg = y
655672 processSignal (sign, asgn)
656- # xxx use string for msg instead of cstring, and here use showErrorMessage2(msg)
657- # unless there's a good reason to use cstring in signal handler to avoid
658- # using gc?
673+ # showErrorMessage may allocate, which may cause a crash, and calls C
674+ # library functions which is undefined behavior, ie it may also crash.
675+ # Nevertheless, we sometimes manage to emit the message regardless which
676+ # pragmatically makes this attempt "useful enough".
677+ # See also https://en.cppreference.com/w/c/program/signal
659678 showErrorMessage (msg, msg.len)
660679
661680 when defined (posix):
662681 # reset the signal handler to OS default
663- c_signal (sign, SIG_DFL )
682+ {.cast (raises: []).}: # Work around -d:laxEffects bugs
683+ discard c_signal (sign, SIG_DFL )
664684
665685 # re-raise the signal, which will arrive once this handler exit.
666686 # this lets the OS perform actions like core dumping and will
@@ -697,4 +717,5 @@ proc setControlCHook(hook: proc () {.noconv.}) =
697717when not defined (noSignalHandler) and not defined (useNimRtl):
698718 proc unsetControlCHook () =
699719 # proc to unset a hook set by setControlCHook
700- c_signal (SIGINT , signalHandler)
720+ {.gcsafe .}: # Work around -d:laxEffects bugs
721+ discard c_signal (SIGINT , signalHandler)
0 commit comments