i have a simple (non-threaded) script that listens on a socket for data, analyses it and uses internally SIGALRM
's to send emails at predefined timer internals.
the problem is during the recv()
loop, the occurrence of the SIGALRM
appears to raise a
socket.error: [Errno 4] Interrupted system call
and hence terminating the program.
i can wrap the recv()
with a try/except block, but i was wondering whether i would loose any data during this time, or whether the buffer will prevent loss.
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind((host, port))
while True:
try:
data = s.recv(2048)
except socket.error, e:
pass
yield data
s.close()
return
The standard way to handle this in C is to loop around EINTR
. And, while that shouldn't be necessary in Python, it is.
Your code is pretty close to the idiomatic way to deal with this, except for two things:
EINTR
.yield data
after ignoring an error that way, because you'll re-yield the previous packet (if there is one) or raise a NameError
(if this is the first time through the loop).So:
while True:
try:
data = s.recv(2048)
except socket.error, e:
if e.errno != errno.EINTR:
raise
else:
yield data
So, why do you have to do this?
POSIX allows almost any syscall to return EINTR for certain kinds of temporary failure—which includes being interrupted by a signal. Many POSIX platforms do this. The expected application behavior is to just retry (if you were trying a blocking call) or go back around the loop (if you're inside a level-triggered reactor). This blog post gives a pretty good explanation for why POSIX works this way. (It's a justification after the fact, and definitely not the actual rationale…) Also see the glibc documentation.
Like most scripting languages, Python is supposed to wrap all EINTR
-prone calls internally, so you shouldn't ever have to think about this (unless you're using third-party C extensions). But unfortunately, it has bugs. The most recent set of cases that were found and fixed are in issue 9867 and issue 12268.
Even if they've finally caught everything, that only helps if you can depend on a sufficiently new version of Python. Given that you're using pre-2.6-style except
syntax, and the latest fixes went in to some 2.7.x and 3.2.x bugfix release, that presumably doesn't work for you.
There are other ways to solve this problem, but they're more complicated and less portable. For example, you can replace your blocking recv
with a blocking pselect
and a non-blocking recv
, add a pipe
into the fd set along with the socket, replace all of your signal handlers with functions that just write (one byte) to that pipe, and move the actual signal-handling code into the event loop. Then, on some platforms, you will never get an EINTR
. But this probably isn't the approach you want to take in Python.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With