Issue 1089358: need siginterrupt() on Linux - impossible to do timeouts (original) (raw)

On latest versions of Linux (eg. Red Hat FC2 / FC3, kernel 2.6+, glibc 2.3.3+) it seems to be impossible to get a system call invoked by Python to time out if it enters a blocking operation (eg read, recvfrom) using Python's signal or threading modules.

A good example is 'gethostbyname' - if the network cable is unplugged, or the name servers are not contactable, then a Python program will hang for a very long time (5 minutes by default) before being able to proceed.

I've tried to do timeouts using either the signal module or using threading timers (see attached examples) - but though the signal handler is called when the SIGALRM is received , and the timer activates the callback, the main thread is still blocked in gethostbyname and the whole process is blocked - so timeouts cannot be implemented. If anyone knows a better way of getting a blocking system call to timeout in python, please let me know. I've finally resorted to invoking the BIND 'dig' program with timeout parameters from commands.getstatusoutput so my app can recover from network connectivity problems.

I think we need to be able to invoke the glibc siginterrupt() function from Python; ONLY by doing so are signals allowed to interrupt system calls so that they return EINTR .

Note that this differs from "old" Linux behaviour . The siginterrupt man page states: " system calls will be restarted if interrupted by the speci- fied signal sig. This is the default behaviour in Linux.
" (this is true) " However, when a new signal handler is specified with the signal(2) function, the system call is interrupted by default. " ( THIS IS FALSE !) With modern Linux kernels + glibcs, all signals are restarted UNLESS the siginterrupt( sig, 1 ) call is invoked. This may be a glibc bug, but many glibcs out there have it. This issue can be reproduced using the attached c program - without the siginterrupt, the system call is not interrupted .

PLEASE provide a wrapper to call the siginterrupt(3) glibc function from Python - THANKS .

PyOS_setsig in pythonrun.c now calls siginterrupt(sig, 1) (in Python 2.4.4/Python 2.5.1, but not in Python 2.3). So you should be able to timeout the system calls with a signal.alarm call.

However, having siginterrupt available would still be useful. I have some patches for the signal module and will clean them up in some days and attach to this bug.

Here's an implementation using ctypes:

def siginterrupt(signum, flag): """change restart behavior when a function is interrupted by the specified signal. see man siginterrupt. """ import ctypes import sys

if flag:
    flag = 1
else:
    flag = 0
    
if sys.platform=='darwin':
    libc = ctypes.CDLL("libc.dylib")
elif sys.platform=='linux2':
    libc = ctypes.CDLL("libc.so.6")
else:
    libc = ctypes.CDLL("libc.so")

if libc.siginterrupt(signum, flag)!=0:
    raise OSError("siginterrupt failed")