1. Armin Rigo
  2. cpython-withatomic

Commits

Neal Norwitz  committed 85fdcdf

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.

  • Participants
  • Parent commits 00350cb
  • Branches legacy-trunk

Comments (0)

Files changed (4)

File Lib/test/test_socket.py

View file
  • Ignore whitespace
 import sys
 import array
 from weakref import proxy
+import signal
 
 PORT = 50007
 HOST = 'localhost'
         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):

File Misc/ACKS

View file
  • Ignore whitespace
 Takahiro Nakayama
 Travers Naran
 Fredrik Nehr
+Tony Nelson
 Chad Netzer
 Max Neunh�ffer
 George Neville-Neil

File Misc/NEWS

View file
  • Ignore whitespace
 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.
 

File Modules/socketmodule.c

View file
  • Ignore whitespace
    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)
 {
 			n = select(s->sock_fd+1, &fds, NULL, NULL, &tv);
 	}
 #endif
+	
+	if (n < 0)
+		return -1;
 	if (n == 0)
 		return 1;
 	return 0;
 			       &addrlen);
 	Py_END_ALLOW_THREADS
 
-	if (timeout) {
+	if (timeout == 1) {
 		PyErr_SetString(socket_timeout, "timed out");
 		return NULL;
 	}
 	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 */
 		}
 	}
 
 	res = internal_connect(s, addr, addrlen, &timeout);
 	Py_END_ALLOW_THREADS
 
-	if (timeout) {
+	if (timeout == 1) {
 		PyErr_SetString(socket_timeout, "timed out");
 		return NULL;
 	}
 	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);
 }
 
 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
 
 		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;
 	}
 	remaining = len;
 	while (remaining != 0) {
 		unsigned int segment;
+		int nread = -1;
 
 		segment = remaining /SEGMENT_SIZE;
 		if (segment != 0) {
 			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;
 		}
 {
 	sock_addr_t addrbuf;
 	int timeout;
-	ssize_t n = 0;
+	ssize_t n = -1;
 	socklen_t addrlen;
 
 	*addr = NULL;
 	}
 	Py_END_ALLOW_THREADS
 
-	if (timeout) {
+	if (timeout == 1) {
 		PyErr_SetString(socket_timeout, "timed out");
 		return -1;
 	}
 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;
 #endif
 	Py_END_ALLOW_THREADS
 
-	if (timeout) {
+	if (timeout == 1) {
 		PyErr_SetString(socket_timeout, "timed out");
 		return NULL;
 	}
 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;
 	Py_BEGIN_ALLOW_THREADS
 	do {
 		timeout = internal_select(s, 1);
+		n = -1;
 		if (timeout)
 			break;
 #ifdef __VMS
 	} while (len > 0);
 	Py_END_ALLOW_THREADS
 
-	if (timeout) {
+	if (timeout == 1) {
 		PyErr_SetString(socket_timeout, "timed out");
 		return NULL;
 	}
 	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)) {
 		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;
 	}