Re: Why doesn't python die gracefully on an SIGTERM???

Guido van Rossum (Guido.van.Rossum@cwi.nl)
Tue, 29 Sep 1992 21:37:36 +0100

>Would it be possible to add at least some functionality to allow for
>graceful termination after the most common signal, SIGTERM?

This must be one of the most frequently requested features: either
provide a general signal handling mechanism, or provide at least a way
to clean up when a signal arrives. So far I've avoided the issue of
designing a "safe" signal handling mechanism for Python, because it's
almost impossible to do right for everybody without adding
considerable overhead to the interpreter. Many proposed solutions
don't work because of the interpreter's structure (e.g., anything
involving longjmp() back to the main loop is unsafe because it may
leave large amounts of allocated memory unreachable and leave internal
data structures in an inconsistent state). But given the persistence
of those who ask for some kind of signal handling, I suppose I simply
have to add *something*.

First, in 0.9.7 there will be a cleanup feature that's independent of
try...finally: if you assign a function object to sys.exitfunc, it
will be called when Python is about to exit. I suppose it shouldn't
be too hard to arrange that this is called when SIGTERM (or SIGHUP)
arrives too.

There will be a risk when using this feature: some part of the library
or interpreter may be busy when the signal arrives and not allow
re-entrance. A famous example in C is trying to use printf() to print
a message from a signal handler: this may fail (causing a core dump)
when the signal arrives while the mainline code of the program is
already using printf(). Since Python uses C's stdio for its own I/O,
this may occur in Python as well. Other risks specific to Python are
touching mutable global data structures such as lists, which may be
inconsistent when an update is in progress. But again a careful
Python programmer can minimize the risk by restricting the clean-up
procedure to the absolutely minimum necessary.

Another idea, harder to implement, but also more generally useful, is
to add an interface whereby arbitrary signals can be mapped to
exceptions just like SIGINT is already mapped to KeyboardInterrupt.
Or perhaps they should be mappable to function calls (and the default
mapping for SIGINT could be a function that raises KeyboardInterrupt).
In this case, the actual call to the (Python) handler will be delayed
slightly so the interpreter can make sure there are no inconsistent
(internal) data structures to worry about. This is already done for
SIGINT: the actual SIGINT handler just sets a global flag, which is
checked every few "virtual instructions".

There is a problem with blocking system calls like read() that needs
to be fixed in a more general way, too, if this solution is adopted:
some UNIX systems restart system calls that are interrupted by a
signal handler. This would mean that the Python handler would be
called only after the call returns, which may be never (e.g. when
reading from a socket or tty). The current solution, adopted for a
few important blocking calls only (sleep, read), is to temporarily
change the SIGINT handler to one that calls longjmp() to a safe point
in the Python wrapper. This has to be extended to all caught signals
and all (or at least most) blocking calls, else the signal mechanism
would not be reliable. I'll have to think about this long and deep
before I proceed to implement it...

--Guido van Rossum, CWI, Amsterdam <guido@cwi.nl>
"I think you have not fully appreciated our attitude towards the tenants"