This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: socket timeouts + Ctrl-C don't play nice
Type: Stage:
Components: Library (Lib) Versions: Python 2.5
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: nnorwitz Nosy List: dmcooke, irmen, nnorwitz
Priority: normal Keywords:

Created on 2004-03-31 03:48 by dmcooke, last changed 2022-04-11 14:56 by admin. This issue is now closed.

Files
File name Uploaded Description Edit
socket_timeout_problem.py dmcooke, 2004-03-31 03:48 example code to show problem.
Messages (5)
msg20390 - (view) Author: David M. Cooke (dmcooke) Date: 2004-03-31 03:48
As reported by Koen Vossen in c.l.py, when a timeout is
set on a socket, a try...except block around a
socket.recv (or socket.recvfrom) won't properly catch a
keyboard interrupt (by Ctrl-C). I've attached example
code that shows this. Run it and press Ctrl-C before
the socket times out. This is for Python 2.3 under Linux.

I believe the problem boils down to this sequence of
events inside of the socketmodule.c::sock_recv function
(and similiar for sock_recvfrom):

1) internal_select is called, which calls select, which
waits for a timeout. A SIGINT is received (and caught
by the default handler). The select returns with errno
set to EINTR. internal_select returns with timeout==1

2) without checking errno, recv() is called. Since
there is actually no data, it returns an error, with
errno set to EAGAIN.

3) the default socket error handler is called, which
calls PyErr_SetFromErrno(). Since errno != EINTR (it's
now EAGAIN), a socket_error exception is thrown.

4) the innermost try..except block is triggered.

5) next loop around in eval_frame, notices that SIGINT
was caught, and so KeyboardInterrupt is raised, exiting
innermost try..except clause

6) KeyboardInterrupt is caught by the outermost
try..except block. 

I was going to make a patch, but I couldn't figure out
the best way to fix this in general :-( There are
likely similiar problems with everywhere
internal_select is used. The quick fix is to check
errno before calling recv()
msg20391 - (view) Author: Irmen de Jong (irmen) (Python triager) Date: 2005-01-13 22:59
Logged In: YES 
user_id=129426

(confirmed on Python 2.4 and HEAD)

But: your analysis is not correct:
internal_select doesn't return with timeout==1 when a SIGINT
occurs, instead it returns 0, because it doesn't check for
an error condition that select() may return.

So I hacked around a bit and changed internal_select to
return -1 when the select() system call returns -1,
and then also added the following in sock_recv:

if(errno) {
   Py_DECREF(buf);
   return PyErr_SetFromErrno(socket_error);
}

Which seems to make the example script that is attached to
this bug report run as expected.

As you say, I also think that this check must be added at
all locations where internal_select is called.

What do you say? I can make a patch if this is okay.
msg20392 - (view) Author: David M. Cooke (dmcooke) Date: 2005-01-13 23:28
Logged In: YES 
user_id=65069

Looks like my assertion that timeout==1 is wrong :-).
Otherwise, the analysis is correct: recv is called when
select() retuned EINTR.

I think your approach is the way to go. Since the return
value of internal_select is no longer a boolean for a
timeout, maybe in the expressions "timeout =
internal_select(...)", "timeout" should be renamed to
something more generic (possibly 'selectresult')?
msg20393 - (view) Author: Irmen de Jong (irmen) (Python triager) Date: 2005-01-15 11:38
Logged In: YES 
user_id=129426

I've submitted a patch, it's at 1102879
Please have a close look at it.
msg20394 - (view) Author: Neal Norwitz (nnorwitz) * (Python committer) Date: 2006-08-02 06:47
Logged In: YES 
user_id=33168

Committed revision 51039. (2.5)
History
Date User Action Args
2022-04-11 14:56:03adminsetgithub: 40106
2004-03-31 03:48:42dmcookecreate