[Python-Dev] Signal-resistant code (was: Two random and nearly unrelated ideas)

Oren Tirosh oren-py-d@hishome.net
Wed, 4 Sep 2002 23:07:09 +0300


On Wed, Sep 04, 2002 at 03:16:25PM -0400, Guido van Rossum wrote:
> > When an I/O operation is interrupted by an unmasked signal it returns 
> > with errno==EINTR.  The state of the file is not affected and repeating
> > the operation should recover and continue with no loss of data.
> 
> What if the operation is a select() call?  Is restarting the right
> thing?  How to take into account the consumed portion of the timeout,
> if given?

Some versions of select update the timeout structure to the remainder if 
they are interrupted by a signal. It's probably not a good idea to rely 
on this so gettimeofday could be used to calculate the remainder.

> Or is this a stdio problem?  I believe that calls like fgets() and
> getchar() don't lose data, but maybe I misunderstand your observation.

This is not the point - even if Python I/O calls were fully restartable
would you actually expect people to check for EINTR and restart for
*every* I/O operation in the program just in case some module happens to
use signals?

Instead of

    for line in file:
        do_something_with(line)

we would need to write

    while 1:
        try:
            line = file.next()
        except IOError, exc:
            if exc.errno == errno.EINTR:
                continue
            else:
                raise
        except StopIteration:
            break
        do_something_with(line)

> As I said before, I'm very skeptical that making the I/O ops
> EINTR-safe would be enough to allow the use of signals as suggested by
> Skip

If it's good enough for other purposes it should be good enough for Skip's
proposal, too.

> Skip, but that might still be useful for other purposes, *if* we can
> decide when to honor EINTR and when not.

Only low-level functions like os.read and os.write that map directly to
stdio functions should ever return EINTR.  To make Python signal-safe all
other calls that can return EINTR should have a retry loop. On EINTR they
should check if there are things to do and if so grab the GIL, make
pending calls, release the GIL and retry the operation (unless an
exception has been raised by the signal handler, of course).

This way I could finally write a Python daemon that reloads its 
configuration files on getting the customary SIGHUP :-)

	Oren