classification
Title: need siginterrupt() on Linux - impossible to do timeouts
Type: enhancement Stage:
Components: Interpreter Core, Library (Lib) Versions: Python 2.6
process
Status: closed Resolution: accepted
Dependencies: Superseder:
Assigned To: Nosy List: christian.heimes, facundobatista, gvanrossum, jvd66, schmir
Priority: normal Keywords: patch

Created on 2004-12-21 21:02 by jvd66, last changed 2008-02-23 15:09 by facundobatista. This issue is now closed.

Files
File name Uploaded Description Edit
LookupSignal.py jvd66, 2004-12-21 21:02 python timeout attempt using signal.alarm
LookupThread.py jvd66, 2004-12-21 21:04 Python timeout attempt using thread timer
no_eintr.c jvd66, 2004-12-21 21:09 c program demonstrating that signals are not interrupted without a siginterrupt call
patch schmir, 2007-10-29 20:27
siginterrupt schmir, 2008-01-06 01:16 diff against trunk (svn r59759)
siginterrupt+tests.txt schmir, 2008-02-23 00:41 patch against current trunk, now also includes unittests
Messages (8)
msg23789 - (view) Author: Jason (jvd66) Date: 2004-12-21 21:02
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 .


msg56783 - (view) Author: Ralf Schmitt (schmir) Date: 2007-10-26 09:47
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")
msg56927 - (view) Author: Ralf Schmitt (schmir) Date: 2007-10-29 20:27
here is a patch against 2.5.1
msg59233 - (view) Author: Christian Heimes (christian.heimes) * (Python committer) Date: 2008-01-04 15:50
Next time please give the patch a more meaningful name than "patch" :)
msg59243 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2008-01-04 17:21
Looks okay, but needs docs.
msg59347 - (view) Author: Ralf Schmitt (schmir) Date: 2008-01-06 01:16
I'm attaching an updated patch against trunk. It also contains some
documentation now.
msg62709 - (view) Author: Ralf Schmitt (schmir) Date: 2008-02-23 00:41
I've attached a new patch against current trunk, which now also contains
unit tests.
msg62739 - (view) Author: Facundo Batista (facundobatista) * (Python committer) Date: 2008-02-23 15:09
Applied in r60983. Thank you all!
History
Date User Action Args
2008-02-23 15:09:12facundobatistasetstatus: open -> closed
nosy: + facundobatista
resolution: accepted
messages: + msg62739
2008-02-23 00:41:38schmirsetfiles: + siginterrupt+tests.txt
type: enhancement
messages: + msg62709
versions: - Python 2.5
2008-01-06 01:16:00schmirsetfiles: + siginterrupt
messages: + msg59347
2008-01-04 17:21:19gvanrossumsetnosy: + gvanrossum
messages: + msg59243
2008-01-04 15:50:24christian.heimessetkeywords: + patch
nosy: + christian.heimes
messages: + msg59233
components: + Interpreter Core
versions: + Python 2.6, Python 2.5
2007-10-29 20:27:59schmirsetfiles: + patch
messages: + msg56927
2007-10-26 09:47:57schmirsetnosy: + schmir
messages: + msg56783
2004-12-21 21:02:40jvd66create