]> granicus.if.org Git - python/commitdiff
Patch #1519025 and bug #926423: If a KeyboardInterrupt occurs during
authorNeal Norwitz <nnorwitz@gmail.com>
Wed, 2 Aug 2006 06:46:21 +0000 (06:46 +0000)
committerNeal Norwitz <nnorwitz@gmail.com>
Wed, 2 Aug 2006 06:46:21 +0000 (06:46 +0000)
a socket operation on a socket with a timeout, the exception will be
caught correctly.  Previously, the exception was not caught.

Lib/test/test_socket.py
Misc/ACKS
Misc/NEWS
Modules/socketmodule.c

index b7d39166b070552d2a4ccc13587435028ebc6347..356b80199bb7e3b395f88c8a5af5072e37b44cd4 100644 (file)
@@ -11,6 +11,7 @@ import Queue
 import sys
 import array
 from weakref import proxy
+import signal
 
 PORT = 50007
 HOST = 'localhost'
@@ -817,6 +818,37 @@ class TCPTimeoutTest(SocketTCPTest):
         if not ok:
             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):
 
     def testUDPTimeout(self):
index d35fb703835ead44167fec5c291597caeef71566..e13b5942e3b4416eebee3bd95cf39f6f04325c41 100644 (file)
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -435,6 +435,7 @@ Michael Muller
 Takahiro Nakayama
 Travers Naran
 Fredrik Nehr
+Tony Nelson
 Chad Netzer
 Max Neunhöffer
 George Neville-Neil
index 81f7ca41f307d637ffd0d61eb495c1043d7a36ee..0d05e038053770c314228519db0a60fee4a77ba5 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -140,6 +140,10 @@ Library
 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
   openbsd target platforms.
 
index a53a702f4c566904856375bb93993ed9fce569ce..bb99bdeb9de247f3ac8a27eb206f02bb80ed7e0d 100644 (file)
@@ -708,7 +708,7 @@ internal_setblocking(PySocketSockObject *s, int block)
    The argument writing indicates the direction.
    This does not raise an exception; we'll let our caller do that
    after they've reacquired the interpreter lock.
-   Returns 1 on timeout, 0 otherwise. */
+   Returns 1 on timeout, -1 on error, 0 otherwise. */
 static int
 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);
        }
 #endif
+       
+       if (n < 0)
+               return -1;
        if (n == 0)
                return 1;
        return 0;
@@ -1552,7 +1555,7 @@ sock_accept(PySocketSockObject *s)
                               &addrlen);
        Py_END_ALLOW_THREADS
 
-       if (timeout) {
+       if (timeout == 1) {
                PyErr_SetString(socket_timeout, "timed out");
                return NULL;
        }
@@ -1923,9 +1926,15 @@ internal_connect(PySocketSockObject *s, struct sockaddr *addr, int addrlen,
        if (s->sock_timeout > 0.0) {
                if (res < 0 && errno == EINPROGRESS && IS_SELECTABLE(s)) {
                        timeout = internal_select(s, 1);
-                       res = connect(s->sock_fd, addr, addrlen);
-                       if (res < 0 && errno == EISCONN)
-                               res = 0;
+                       if (timeout == 0) {
+                               res = connect(s->sock_fd, addr, addrlen);
+                               if (res < 0 && errno == EISCONN)
+                                       res = 0;
+                       }
+                       else if (timeout == -1)
+                               res = errno;            /* had error */
+                       else
+                               res = EWOULDBLOCK;      /* timed out */
                }
        }
 
@@ -1955,7 +1964,7 @@ sock_connect(PySocketSockObject *s, PyObject *addro)
        res = internal_connect(s, addr, addrlen, &timeout);
        Py_END_ALLOW_THREADS
 
-       if (timeout) {
+       if (timeout == 1) {
                PyErr_SetString(socket_timeout, "timed out");
                return NULL;
        }
@@ -1989,6 +1998,13 @@ sock_connect_ex(PySocketSockObject *s, PyObject *addro)
        res = internal_connect(s, addr, addrlen, &timeout);
        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);
 }
 
@@ -2209,10 +2225,10 @@ The mode and buffersize arguments are as for the built-in open() function.");
 static ssize_t
 sock_recv_guts(PySocketSockObject *s, char* cbuf, int len, int flags)
 {
-        ssize_t outlen = 0;
+        ssize_t outlen = -1;
         int timeout;
 #ifdef __VMS
-       int remaining, nread;
+       int remaining;
        char *read_buf;
 #endif
 
@@ -2228,7 +2244,7 @@ sock_recv_guts(PySocketSockObject *s, char* cbuf, int len, int flags)
                outlen = recv(s->sock_fd, cbuf, len, flags);
        Py_END_ALLOW_THREADS
 
-       if (timeout) {
+       if (timeout == 1) {
                PyErr_SetString(socket_timeout, "timed out");
                return -1;
        }
@@ -2243,6 +2259,7 @@ sock_recv_guts(PySocketSockObject *s, char* cbuf, int len, int flags)
        remaining = len;
        while (remaining != 0) {
                unsigned int segment;
+               int nread = -1;
 
                segment = remaining /SEGMENT_SIZE;
                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);
                Py_END_ALLOW_THREADS
 
-               if (timeout) {
+               if (timeout == 1) {
                        PyErr_SetString(socket_timeout, "timed out");
                        return -1;
                }
@@ -2406,7 +2423,7 @@ sock_recvfrom_guts(PySocketSockObject *s, char* cbuf, int len, int flags,
 {
        sock_addr_t addrbuf;
        int timeout;
-       ssize_t n = 0;
+       ssize_t n = -1;
        socklen_t addrlen;
 
        *addr = NULL;
@@ -2438,7 +2455,7 @@ sock_recvfrom_guts(PySocketSockObject *s, char* cbuf, int len, int flags,
        }
        Py_END_ALLOW_THREADS
 
-       if (timeout) {
+       if (timeout == 1) {
                PyErr_SetString(socket_timeout, "timed out");
                return -1;
        }
@@ -2553,7 +2570,7 @@ static PyObject *
 sock_send(PySocketSockObject *s, PyObject *args)
 {
        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))
                return NULL;
@@ -2571,7 +2588,7 @@ sock_send(PySocketSockObject *s, PyObject *args)
 #endif
        Py_END_ALLOW_THREADS
 
-       if (timeout) {
+       if (timeout == 1) {
                PyErr_SetString(socket_timeout, "timed out");
                return NULL;
        }
@@ -2594,7 +2611,7 @@ static PyObject *
 sock_sendall(PySocketSockObject *s, PyObject *args)
 {
        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))
                return NULL;
@@ -2605,6 +2622,7 @@ sock_sendall(PySocketSockObject *s, PyObject *args)
        Py_BEGIN_ALLOW_THREADS
        do {
                timeout = internal_select(s, 1);
+               n = -1;
                if (timeout)
                        break;
 #ifdef __VMS
@@ -2619,7 +2637,7 @@ sock_sendall(PySocketSockObject *s, PyObject *args)
        } while (len > 0);
        Py_END_ALLOW_THREADS
 
-       if (timeout) {
+       if (timeout == 1) {
                PyErr_SetString(socket_timeout, "timed out");
                return NULL;
        }
@@ -2647,7 +2665,7 @@ sock_sendto(PySocketSockObject *s, PyObject *args)
        PyObject *addro;
        char *buf;
        struct sockaddr *addr;
-       int addrlen, len, n = 0, flags, timeout;
+       int addrlen, len, n = -1, flags, timeout;
 
        flags = 0;
        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);
        Py_END_ALLOW_THREADS
 
-       if (timeout) {
+       if (timeout == 1) {
                PyErr_SetString(socket_timeout, "timed out");
                return NULL;
        }