On Sun, Jan 16, 2022 at 01:42:21PM -0500, Steven Schveighoffer via Digitalmars-d-learn wrote: > On 1/16/22 1:33 PM, Paul Backus wrote: [...] > > ```d > > extern(C) void handleCtrlC(int) > > { > > import core.stdc.stdlib: exit; > > import std.stdio: writeln; > > > > try throw new Exception("Killed by CTRL+C"); > > catch (Exception e) > > { > > writeln(e.message); > > writeln(e.info); > > exit(1); > > } > > } > > ``` > > This too is not going to be a good idea. writeln(e.info) is going to > possibly start allocating. A signal can come at any time, even when > locks are held or things are in an intermediate state.
Yeah, this is generally a bad idea. Code registered as a signal handler will get called in what POSIX calls "signal handler context", where you're not allowed to call a lot of C library functions because the signal handler could be invoked while inside a non-reentrant library function or syscall. While this may sometimes work, it may crash horribly when the signal happens to arrive at the wrong time, or deadlock. Many POSIX functions are marked as unsafe to call from signal handler context, and this in particular includes anything that may allocate memory (bad things will happen if you try to call, e.g., malloc from a signal handler while the code happens to be inside another malloc call -- it will likely deadlock on the malloc internal mutex). Generally, a signal handler should do the absolute minimum to inform the main program that the signal was received, and then quickly return back to the interrupted code and let the main program react to the signal later at a more convenient time. The usual idiom is to write a single byte (or a small number of bytes) to a pipe that's read by the program's main loop. The write syscall is one of the few syscalls that are signal-handler safe, and is a convenient way to flag the receipt of the signal without needing to worry about thread synchronization issues (sin ce the OS takes care of that inside the write() syscall). > I use Adam's approach normally -- set a flag and act on it later. [...] Yes, this is generally the recommended way of dealing with a signal. Though I'd add, as an interesting footnote, that sometimes you *can* do trickier things inside a signal handler. One example is something we came up with once, a hack to convert a SEGV into a D exception. The way it works is by the signal handler take advantage of a piece of information that the SEGV signal provides to it, which is the EIP value of the code that caused the segfault, and the location in the code that the signal handler would return to when it exits. The signal handler uses what amounts to a stack overflow exploit by overwriting this return address to point instead to a function that allocates and throws a SegfaultException. Once the signal handler returns, any syscalls the code may have been in will finish running, then return. Now we're officially outside signal handler context, so it's safe to now allocate and throw the exception. Of course, the special function also needs to reconstruct a proper stack frame so that the stack unwinding of the thrown exception won't go haywire. The gory details are here: https://forum.dlang.org/thread/jjn6dj$193c$1...@digitalmars.com Obviously, this is extremely tricky and system-dependent stuff that has to be done with extreme care. NOT recommended if you don't know exactly what you're doing! T -- By understanding a machine-oriented language, the programmer will tend to use a much more efficient method; it is much closer to reality. -- D. Knuth