[Python-checkins] cpython (2.7): Issue #9090 : Error code 10035 calling socket.recv() on a socket with a timeout

kristjan.jonsson python-checkins at python.org
Tue Mar 19 19:08:13 CET 2013


http://hg.python.org/cpython/rev/8ec39bfd1f01
changeset: 82764:8ec39bfd1f01
branch: 2.7
parent: 82740:b10ec5083a53
user: Kristján Valur Jónsson <sweskman at gmail.com>
date: Tue Mar 19 10:58:59 2013 -0700
summary:
 Issue #9090 : Error code 10035 calling socket.recv() on a socket with a timeout
 (WSAEWOULDBLOCK - A non-blocking socket operation could not be completed
 immediately)
files:
 Misc/NEWS | 5 +
 Modules/socketmodule.c | 104 ++++++++++++++++++++++++----
 Modules/timemodule.c | 7 +
 3 files changed, 101 insertions(+), 15 deletions(-)
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -214,6 +214,11 @@
 Library
 -------
 
+- Issue #9090: When a socket with a timeout fails with EWOULDBLOCK or EAGAIN,
+ retry the select() loop instead of bailing out. This is because select()
+ can incorrectly report a socket as ready for reading (for example, if it
+ received some data with an invalid checksum).
+
 - Issue #1285086: Get rid of the refcounting hack and speed up urllib.unquote().
 
 - Issue #17368: Fix an off-by-one error in the Python JSON decoder that caused
diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c
--- a/Modules/socketmodule.c
+++ b/Modules/socketmodule.c
@@ -473,6 +473,17 @@
 return NULL;
 }
 
+#ifdef MS_WINDOWS
+#ifndef WSAEAGAIN
+#define WSAEAGAIN WSAEWOULDBLOCK
+#endif
+#define CHECK_ERRNO(expected) \
+ (WSAGetLastError() == WSA ## expected)
+#else
+#define CHECK_ERRNO(expected) \
+ (errno == expected)
+#endif
+
 /* Convenience function to raise an error according to errno
 and return a NULL pointer from a function. */
 
@@ -661,7 +672,7 @@
 after they've reacquired the interpreter lock.
 Returns 1 on timeout, -1 on error, 0 otherwise. */
 static int
-internal_select(PySocketSockObject *s, int writing)
+internal_select_ex(PySocketSockObject *s, int writing, double interval)
 {
 int n;
 
@@ -673,6 +684,10 @@
 if (s->sock_fd < 0)
 return 0;
 
+ /* Handling this condition here simplifies the select loops */
+ if (interval < 0.0)
+ return 1;
+
 /* Prefer poll, if available, since you can poll() any fd
 * which can't be done with select(). */
 #ifdef HAVE_POLL
@@ -684,7 +699,7 @@
 pollfd.events = writing ? POLLOUT : POLLIN;
 
 /* s->sock_timeout is in seconds, timeout in ms */
- timeout = (int)(s->sock_timeout * 1000 + 0.5);
+ timeout = (int)(interval * 1000 + 0.5);
 n = poll(&pollfd, 1, timeout);
 }
 #else
@@ -692,8 +707,8 @@
 /* Construct the arguments to select */
 fd_set fds;
 struct timeval tv;
- tv.tv_sec = (int)s->sock_timeout;
- tv.tv_usec = (int)((s->sock_timeout - tv.tv_sec) * 1e6);
+ tv.tv_sec = (int)interval;
+ tv.tv_usec = (int)((interval - tv.tv_sec) * 1e6);
 FD_ZERO(&fds);
 FD_SET(s->sock_fd, &fds);
 
@@ -712,6 +727,49 @@
 return 0;
 }
 
+static int
+internal_select(PySocketSockObject *s, int writing)
+{
+ return internal_select_ex(s, writing, s->sock_timeout);
+}
+
+/*
+ Two macros for automatic retry of select() in case of false positives
+ (for example, select() could indicate a socket is ready for reading
+ but the data then discarded by the OS because of a wrong checksum).
+ Here is an example of use:
+
+ BEGIN_SELECT_LOOP(s)
+ Py_BEGIN_ALLOW_THREADS
+ timeout = internal_select_ex(s, 0, interval);
+ if (!timeout)
+ outlen = recv(s->sock_fd, cbuf, len, flags);
+ Py_END_ALLOW_THREADS
+ if (timeout == 1) {
+ PyErr_SetString(socket_timeout, "timed out");
+ return -1;
+ }
+ END_SELECT_LOOP(s)
+*/
+PyAPI_FUNC(double) _PyTime_floattime(void); /* defined in timemodule.c */
+#define BEGIN_SELECT_LOOP(s) \
+ { \
+ double deadline, interval = s->sock_timeout; \
+ int has_timeout = s->sock_timeout > 0.0; \
+ if (has_timeout) { \
+ deadline = _PyTime_floattime() + s->sock_timeout; \
+ } \
+ while (1) { \
+ errno = 0; \
+
+#define END_SELECT_LOOP(s) \
+ if (!has_timeout || \
+ (!CHECK_ERRNO(EWOULDBLOCK) && !CHECK_ERRNO(EAGAIN))) \
+ break; \
+ interval = deadline - _PyTime_floattime(); \
+ } \
+ } \
+
 /* Initialize a new socket object. */
 
 static double defaulttimeout = -1.0; /* Default timeout for new sockets */
@@ -1656,8 +1714,9 @@
 if (!IS_SELECTABLE(s))
 return select_error();
 
+ BEGIN_SELECT_LOOP(s)
 Py_BEGIN_ALLOW_THREADS
- timeout = internal_select(s, 0);
+ timeout = internal_select_ex(s, 0, interval);
 if (!timeout)
 newfd = accept(s->sock_fd, SAS2SA(&addrbuf), &addrlen);
 Py_END_ALLOW_THREADS
@@ -1666,6 +1725,7 @@
 PyErr_SetString(socket_timeout, "timed out");
 return NULL;
 }
+ END_SELECT_LOOP(s)
 
 #ifdef MS_WINDOWS
 if (newfd == INVALID_SOCKET)
@@ -2355,8 +2415,9 @@
 }
 
 #ifndef __VMS
+ BEGIN_SELECT_LOOP(s)
 Py_BEGIN_ALLOW_THREADS
- timeout = internal_select(s, 0);
+ timeout = internal_select_ex(s, 0, interval);
 if (!timeout)
 outlen = recv(s->sock_fd, cbuf, len, flags);
 Py_END_ALLOW_THREADS
@@ -2365,6 +2426,7 @@
 PyErr_SetString(socket_timeout, "timed out");
 return -1;
 }
+ END_SELECT_LOOP(s)
 if (outlen < 0) {
 /* Note: the call to errorhandler() ALWAYS indirectly returned
 NULL, so ignore its return value */
@@ -2386,8 +2448,9 @@
 segment = remaining;
 }
 
+ BEGIN_SELECT_LOOP(s)
 Py_BEGIN_ALLOW_THREADS
- timeout = internal_select(s, 0);
+ timeout = internal_select_ex(s, 0, interval);
 if (!timeout)
 nread = recv(s->sock_fd, read_buf, segment, flags);
 Py_END_ALLOW_THREADS
@@ -2396,6 +2459,8 @@
 PyErr_SetString(socket_timeout, "timed out");
 return -1;
 }
+ END_SELECT_LOOP(s)
+
 if (nread < 0) {
 s->errorhandler();
 return -1;
@@ -2559,9 +2624,10 @@
 return -1;
 }
 
+ BEGIN_SELECT_LOOP(s)
 Py_BEGIN_ALLOW_THREADS
 memset(&addrbuf, 0, addrlen);
- timeout = internal_select(s, 0);
+ timeout = internal_select_ex(s, 0, interval);
 if (!timeout) {
 #ifndef MS_WINDOWS
 #if defined(PYOS_OS2) && !defined(PYCC_GCC)
@@ -2582,6 +2648,7 @@
 PyErr_SetString(socket_timeout, "timed out");
 return -1;
 }
+ END_SELECT_LOOP(s)
 if (n < 0) {
 s->errorhandler();
 return -1;
@@ -2719,8 +2786,9 @@
 buf = pbuf.buf;
 len = pbuf.len;
 
+ BEGIN_SELECT_LOOP(s)
 Py_BEGIN_ALLOW_THREADS
- timeout = internal_select(s, 1);
+ timeout = internal_select_ex(s, 1, interval);
 if (!timeout)
 #ifdef __VMS
 n = sendsegmented(s->sock_fd, buf, len, flags);
@@ -2728,13 +2796,14 @@
 n = send(s->sock_fd, buf, len, flags);
 #endif
 Py_END_ALLOW_THREADS
-
- PyBuffer_Release(&pbuf);
-
 if (timeout == 1) {
+ PyBuffer_Release(&pbuf);
 PyErr_SetString(socket_timeout, "timed out");
 return NULL;
 }
+ END_SELECT_LOOP(s)
+
+ PyBuffer_Release(&pbuf);
 if (n < 0)
 return s->errorhandler();
 return PyInt_FromLong((long)n);
@@ -2768,8 +2837,9 @@
 }
 
 do {
+ BEGIN_SELECT_LOOP(s)
 Py_BEGIN_ALLOW_THREADS
- timeout = internal_select(s, 1);
+ timeout = internal_select_ex(s, 1, interval);
 n = -1;
 if (!timeout) {
 #ifdef __VMS
@@ -2784,6 +2854,7 @@
 PyErr_SetString(socket_timeout, "timed out");
 return NULL;
 }
+ END_SELECT_LOOP(s)
 /* PyErr_CheckSignals() might change errno */
 saved_errno = errno;
 /* We must run our signal handlers before looping again.
@@ -2863,17 +2934,20 @@
 return NULL;
 }
 
+ BEGIN_SELECT_LOOP(s)
 Py_BEGIN_ALLOW_THREADS
- timeout = internal_select(s, 1);
+ timeout = internal_select_ex(s, 1, interval);
 if (!timeout)
 n = sendto(s->sock_fd, buf, len, flags, SAS2SA(&addrbuf), addrlen);
 Py_END_ALLOW_THREADS
 
- PyBuffer_Release(&pbuf);
 if (timeout == 1) {
+ PyBuffer_Release(&pbuf);
 PyErr_SetString(socket_timeout, "timed out");
 return NULL;
 }
+ END_SELECT_LOOP(s)
+ PyBuffer_Release(&pbuf);
 if (n < 0)
 return s->errorhandler();
 return PyInt_FromLong((long)n);
diff --git a/Modules/timemodule.c b/Modules/timemodule.c
--- a/Modules/timemodule.c
+++ b/Modules/timemodule.c
@@ -1055,3 +1055,10 @@
 
 return 0;
 }
+
+/* export floattime to socketmodule.c */
+PyAPI_FUNC(double)
+_PyTime_floattime(void)
+{
+ return floattime();
+}
-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list

AltStyle によって変換されたページ (->オリジナル) /