Patch #1519025 and bug #926423: If a KeyboardInterrupt occurs during

a socket operation on a socket with a timeout, the exception will be
caught correctly.  Previously, the exception was not caught.
This commit is contained in:
Neal Norwitz 2006-08-02 06:46:21 +00:00
parent 3ee5941f68
commit 9b0ca79213
4 changed files with 73 additions and 18 deletions

View File

@ -11,6 +11,7 @@ import Queue
import sys import sys
import array import array
from weakref import proxy from weakref import proxy
import signal
PORT = 50007 PORT = 50007
HOST = 'localhost' HOST = 'localhost'
@ -817,6 +818,37 @@ class TCPTimeoutTest(SocketTCPTest):
if not ok: if not ok:
self.fail("accept() returned success when we did not expect it") self.fail("accept() returned success when we did not expect it")
def testInterruptedTimeout(self):
# XXX I don't know how to do this test on MSWindows or any other
# plaform that doesn't support signal.alarm() or os.kill(), though
# the bug should have existed on all platforms.
if not hasattr(signal, "alarm"):
return # can only test on *nix
self.serv.settimeout(5.0) # must be longer than alarm
class Alarm(Exception):
pass
def alarm_handler(signal, frame):
raise Alarm
old_alarm = signal.signal(signal.SIGALRM, alarm_handler)
try:
signal.alarm(2) # POSIX allows alarm to be up to 1 second early
try:
foo = self.serv.accept()
except socket.timeout:
self.fail("caught timeout instead of Alarm")
except Alarm:
pass
except:
self.fail("caught other exception instead of Alarm")
else:
self.fail("nothing caught")
signal.alarm(0) # shut off alarm
except Alarm:
self.fail("got Alarm in wrong place")
finally:
# no alarm can be pending. Safe to restore old handler.
signal.signal(signal.SIGALRM, old_alarm)
class UDPTimeoutTest(SocketTCPTest): class UDPTimeoutTest(SocketTCPTest):
def testUDPTimeout(self): def testUDPTimeout(self):

View File

@ -435,6 +435,7 @@ Michael Muller
Takahiro Nakayama Takahiro Nakayama
Travers Naran Travers Naran
Fredrik Nehr Fredrik Nehr
Tony Nelson
Chad Netzer Chad Netzer
Max Neunhöffer Max Neunhöffer
George Neville-Neil George Neville-Neil

View File

@ -140,6 +140,10 @@ Library
Extension Modules Extension Modules
----------------- -----------------
- Patch #1519025 and bug #926423: If a KeyboardInterrupt occurs during
a socket operation on a socket with a timeout, the exception will be
caught correctly. Previously, the exception was not caught.
- Patch #1529514: The _ctypes extension is now compiled on more - Patch #1529514: The _ctypes extension is now compiled on more
openbsd target platforms. openbsd target platforms.

View File

@ -708,7 +708,7 @@ internal_setblocking(PySocketSockObject *s, int block)
The argument writing indicates the direction. The argument writing indicates the direction.
This does not raise an exception; we'll let our caller do that This does not raise an exception; we'll let our caller do that
after they've reacquired the interpreter lock. after they've reacquired the interpreter lock.
Returns 1 on timeout, 0 otherwise. */ Returns 1 on timeout, -1 on error, 0 otherwise. */
static int static int
internal_select(PySocketSockObject *s, int writing) internal_select(PySocketSockObject *s, int writing)
{ {
@ -753,6 +753,9 @@ internal_select(PySocketSockObject *s, int writing)
n = select(s->sock_fd+1, &fds, NULL, NULL, &tv); n = select(s->sock_fd+1, &fds, NULL, NULL, &tv);
} }
#endif #endif
if (n < 0)
return -1;
if (n == 0) if (n == 0)
return 1; return 1;
return 0; return 0;
@ -1552,7 +1555,7 @@ sock_accept(PySocketSockObject *s)
&addrlen); &addrlen);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
if (timeout) { if (timeout == 1) {
PyErr_SetString(socket_timeout, "timed out"); PyErr_SetString(socket_timeout, "timed out");
return NULL; return NULL;
} }
@ -1923,10 +1926,16 @@ internal_connect(PySocketSockObject *s, struct sockaddr *addr, int addrlen,
if (s->sock_timeout > 0.0) { if (s->sock_timeout > 0.0) {
if (res < 0 && errno == EINPROGRESS && IS_SELECTABLE(s)) { if (res < 0 && errno == EINPROGRESS && IS_SELECTABLE(s)) {
timeout = internal_select(s, 1); timeout = internal_select(s, 1);
if (timeout == 0) {
res = connect(s->sock_fd, addr, addrlen); res = connect(s->sock_fd, addr, addrlen);
if (res < 0 && errno == EISCONN) if (res < 0 && errno == EISCONN)
res = 0; res = 0;
} }
else if (timeout == -1)
res = errno; /* had error */
else
res = EWOULDBLOCK; /* timed out */
}
} }
if (res < 0) if (res < 0)
@ -1955,7 +1964,7 @@ sock_connect(PySocketSockObject *s, PyObject *addro)
res = internal_connect(s, addr, addrlen, &timeout); res = internal_connect(s, addr, addrlen, &timeout);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
if (timeout) { if (timeout == 1) {
PyErr_SetString(socket_timeout, "timed out"); PyErr_SetString(socket_timeout, "timed out");
return NULL; return NULL;
} }
@ -1989,6 +1998,13 @@ sock_connect_ex(PySocketSockObject *s, PyObject *addro)
res = internal_connect(s, addr, addrlen, &timeout); res = internal_connect(s, addr, addrlen, &timeout);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
/* Signals are not errors (though they may raise exceptions). Adapted
from PyErr_SetFromErrnoWithFilenameObject(). */
#ifdef EINTR
if (res == EINTR && PyErr_CheckSignals())
return NULL;
#endif
return PyInt_FromLong((long) res); return PyInt_FromLong((long) res);
} }
@ -2209,10 +2225,10 @@ The mode and buffersize arguments are as for the built-in open() function.");
static ssize_t static ssize_t
sock_recv_guts(PySocketSockObject *s, char* cbuf, int len, int flags) sock_recv_guts(PySocketSockObject *s, char* cbuf, int len, int flags)
{ {
ssize_t outlen = 0; ssize_t outlen = -1;
int timeout; int timeout;
#ifdef __VMS #ifdef __VMS
int remaining, nread; int remaining;
char *read_buf; char *read_buf;
#endif #endif
@ -2228,7 +2244,7 @@ sock_recv_guts(PySocketSockObject *s, char* cbuf, int len, int flags)
outlen = recv(s->sock_fd, cbuf, len, flags); outlen = recv(s->sock_fd, cbuf, len, flags);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
if (timeout) { if (timeout == 1) {
PyErr_SetString(socket_timeout, "timed out"); PyErr_SetString(socket_timeout, "timed out");
return -1; return -1;
} }
@ -2243,6 +2259,7 @@ sock_recv_guts(PySocketSockObject *s, char* cbuf, int len, int flags)
remaining = len; remaining = len;
while (remaining != 0) { while (remaining != 0) {
unsigned int segment; unsigned int segment;
int nread = -1;
segment = remaining /SEGMENT_SIZE; segment = remaining /SEGMENT_SIZE;
if (segment != 0) { if (segment != 0) {
@ -2258,7 +2275,7 @@ sock_recv_guts(PySocketSockObject *s, char* cbuf, int len, int flags)
nread = recv(s->sock_fd, read_buf, segment, flags); nread = recv(s->sock_fd, read_buf, segment, flags);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
if (timeout) { if (timeout == 1) {
PyErr_SetString(socket_timeout, "timed out"); PyErr_SetString(socket_timeout, "timed out");
return -1; return -1;
} }
@ -2406,7 +2423,7 @@ sock_recvfrom_guts(PySocketSockObject *s, char* cbuf, int len, int flags,
{ {
sock_addr_t addrbuf; sock_addr_t addrbuf;
int timeout; int timeout;
ssize_t n = 0; ssize_t n = -1;
socklen_t addrlen; socklen_t addrlen;
*addr = NULL; *addr = NULL;
@ -2438,7 +2455,7 @@ sock_recvfrom_guts(PySocketSockObject *s, char* cbuf, int len, int flags,
} }
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
if (timeout) { if (timeout == 1) {
PyErr_SetString(socket_timeout, "timed out"); PyErr_SetString(socket_timeout, "timed out");
return -1; return -1;
} }
@ -2553,7 +2570,7 @@ static PyObject *
sock_send(PySocketSockObject *s, PyObject *args) sock_send(PySocketSockObject *s, PyObject *args)
{ {
char *buf; char *buf;
int len, n = 0, flags = 0, timeout; int len, n = -1, flags = 0, timeout;
if (!PyArg_ParseTuple(args, "s#|i:send", &buf, &len, &flags)) if (!PyArg_ParseTuple(args, "s#|i:send", &buf, &len, &flags))
return NULL; return NULL;
@ -2571,7 +2588,7 @@ sock_send(PySocketSockObject *s, PyObject *args)
#endif #endif
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
if (timeout) { if (timeout == 1) {
PyErr_SetString(socket_timeout, "timed out"); PyErr_SetString(socket_timeout, "timed out");
return NULL; return NULL;
} }
@ -2594,7 +2611,7 @@ static PyObject *
sock_sendall(PySocketSockObject *s, PyObject *args) sock_sendall(PySocketSockObject *s, PyObject *args)
{ {
char *buf; char *buf;
int len, n = 0, flags = 0, timeout; int len, n = -1, flags = 0, timeout;
if (!PyArg_ParseTuple(args, "s#|i:sendall", &buf, &len, &flags)) if (!PyArg_ParseTuple(args, "s#|i:sendall", &buf, &len, &flags))
return NULL; return NULL;
@ -2605,6 +2622,7 @@ sock_sendall(PySocketSockObject *s, PyObject *args)
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
do { do {
timeout = internal_select(s, 1); timeout = internal_select(s, 1);
n = -1;
if (timeout) if (timeout)
break; break;
#ifdef __VMS #ifdef __VMS
@ -2619,7 +2637,7 @@ sock_sendall(PySocketSockObject *s, PyObject *args)
} while (len > 0); } while (len > 0);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
if (timeout) { if (timeout == 1) {
PyErr_SetString(socket_timeout, "timed out"); PyErr_SetString(socket_timeout, "timed out");
return NULL; return NULL;
} }
@ -2647,7 +2665,7 @@ sock_sendto(PySocketSockObject *s, PyObject *args)
PyObject *addro; PyObject *addro;
char *buf; char *buf;
struct sockaddr *addr; struct sockaddr *addr;
int addrlen, len, n = 0, flags, timeout; int addrlen, len, n = -1, flags, timeout;
flags = 0; flags = 0;
if (!PyArg_ParseTuple(args, "s#O:sendto", &buf, &len, &addro)) { if (!PyArg_ParseTuple(args, "s#O:sendto", &buf, &len, &addro)) {
@ -2669,7 +2687,7 @@ sock_sendto(PySocketSockObject *s, PyObject *args)
n = sendto(s->sock_fd, buf, len, flags, addr, addrlen); n = sendto(s->sock_fd, buf, len, flags, addr, addrlen);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
if (timeout) { if (timeout == 1) {
PyErr_SetString(socket_timeout, "timed out"); PyErr_SetString(socket_timeout, "timed out");
return NULL; return NULL;
} }