Entry
Catching signals/alarm (was: Problems with signals & exceptions...)
Jul 5th, 2000 10:02
Nathan Wallace, Hans Nowak, Snippet 252, Donn Cave
"""
Packages: operating_systems.unix
"""
"""
| I'm having trouble catching my thrown exceptions under FreeBSD 3.2. I am
| using the Python 1.5.2 that came with FreeBSD 3.2 (well, on disk 1 of
| the <n>-disk set). The following program works properly under Red Hat
| Linux version 6.0 running a self-compiled Python 1.5.2 (in place of the
| 1.5.1 which comes with Red Hat 6.0, which does not properly handle
| signals during socket i/o calls). How it works: it listens to port 6664.
| When it gets a connection, it then sets an alarm handler and alarm and
| then tries to read from the socket. If it does not get data within 10
| seconds, it then times out, at which point the alarm handler throws an
| exception and aborts the socket read.
I tried this exception-from-handler notion on Digital UNIX and BeOS,
and it does seem to work the way I guess you expect, but I do find it
weird. A signal handler executes outside the normal flow of control,
so it's not obvious to me where its exceptions ought to surface.
Probably it should be expected to work the same way on various platforms
for any given version of Python, but would future versions of Python be
guaranteed to work this way also?
Now I can't really make out what you are seeing on FreeBSD. In the
example output - is that from FreeBSD? - I think you are seeing a
problem with a change to exceptions in recent versions of Python.
Your "why" object prints "READ_TIMEOUT", but it's not that string,
rather an Exception instance with that string as args[0]. So you
need to test if why.args[0] == READ_TIMEOUT. I get the same output
from your program, on Digital UNIX.
What if your signal handler simply returns, without raising an exception?
C socket I/O functions should return error status with errno EINTR, which
translates to a Python exception. So you shouldn't need to raise your
own exception, you'd expect one anyway, because the signal interrupts the
function. Use global state to disambiguate various possible interrupts,
if necessary.
Donn Cave, University Computing Services, University of Washington
donn@u.washington.edu
-----------------------------
| The test:
| In one shell window, type:
| python testalrm.py
| in the other shell window type:
| telnet localhost 6664
| type NOTHING just let it time out...
|
| The output in the first window:
|
| bash-2.03$ python testalarm.py
| Got alarm!
| READ_TIMEOUT
| Traceback (innermost last):
| File "testalarm.py", line 30, in ?
| raise IOError, why
| IOError: READ_TIMEOUT
| bash-2.03$
|
| The program:
"""
# ----------------------------testalarm.py------------------
#
# A routine to test the 'alarm' signal handling.
import socket
import signal
READ_TIMEOUT="READ_TIMEOUT"
def alarmhandler(signum,stackframe):
print "Got alarm!"
raise IOError,READ_TIMEOUT
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
addr=("",6664)
s.bind(addr)
s.listen(5)
connection,address=s.accept()
oldhandler=signal.signal(signal.SIGALRM,alarmhandler)
signal.alarm(10) # wait 10 seconds for an alarm!
try:
str=connection.recv(4096) # get a string
except IOError, why:
print why
if why == READ_TIMEOUT :
print "why = read timeout"
else:
raise IOError, why
signal.alarm(0)
print str
# ----------------end program-------------------------------------