3e8.org

In EBCDIC we trust.

August 2, 2011

Take off every SIGTERM

While working on the zguide for the zmq egg in Chicken, I encountered a problem with terminating a process in a timely manner when it has a SIGTERM handler installed.

(use posix)
(on-exit (lambda () (print "exiting")))
(set-signal-handler! signal/term
                     (lambda (s) (print "terminating") (exit)))
(read)
(print "finished")

If you compile and run this program, and kill -TERM it from another window, nothing happens right away. You have to hit Enter afterwards, and then it will print terminating and exiting.

The reason for this is that the posix unit installs signals with signal(3), which sets the sigaction flag SA_RESTART under the hood. "Slow" system calls (read, zmq_recv) which have received no input yet will automatically restart when the signal handler returns. Chicken has a single global signal handler that just sets a flag indicating which signal was received, schedules the interrupt handler to run in a moment, and returns immediately. The syscall is then restarted before the interrupt handler gets a chance to run.

To hack around this behavior you can add

(foreign-code "siginterrupt(SIGTERM, 1);")

to the top of your file, so slow syscalls will immediately exit with EINTR even when no input is available, and your handler will be invoked. To wit:

(foreign-code "siginterrupt(SIGTERM, 1);")
(use posix)
(on-exit (lambda () (print "exiting...")))
(set-signal-handler! signal/term
                     (lambda (s) (print "terminating...") (exit)))
(read)
(print "finished")

If you do not explicitly hook SIGTERM, a TERM signal will interrupt read immediately and terminate. This is true on Linux and Mac OS X at least, but it's not clear to me that this can be relied upon.

You can see this behavior as well with SIGINT. By default Ctrl-C terminates a read immediately. However, when the posix egg is loaded, it hooks Ctrl-C with signal(3) and so Ctrl-C will not immediately terminate a read.

 (use posix)
 (read)
 ^C^C^C^C^C^C

Instead, you have to press Enter afterward (as line buffering is active by default). To terminate immediately you can do:

 (use posix)
 (foreign-code "siginterrupt(SIGINT, 1);")
 (read)

This situation might need to be addressed with an egg that provides more nuanced signal handling than the posix unit.

Final note: It's not possible to restore the default signal handler (SIG_DFL) with the posix unit, either. Passing #f to set-signal-handler! will ignore the signal (SIG_IGN).