--- C:/Users/EHOHEN~1/AppData/Local/Temp/socketmodule.-rev62272.svn001.tmp.c Wed Dec 22 09:50:50 2010 +++ C:/Users/EHOHEN~1/AppData/Local/Temp/socketmodule.c-rev95652.svn000.tmp.c Wed Dec 22 09:50:51 2010 @@ -475,6 +475,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. */ @@ -663,7 +674,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; @@ -675,6 +686,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 @@ -686,7 +701,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 @@ -694,8 +709,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); @@ -714,6 +729,104 @@ 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) +*/ + +/* + The following code was brazenly stolen from Python 3.2 in order to support + the BEGIN_SELECT_LOOP()/END_SELECT_LOOP() macros that were implemented in + Python 3.2 as a result of a patch submitted by IMVU to fix issues calling + socket.recv() related to non-blocking socket IO calls. It is not nearly as + efficient as the Python 3.2 version but is probably equally correct. +*/ +typedef struct { + time_t tv_sec; /* seconds since Jan. 1, 1970 */ + long tv_usec; /* and microseconds */ +} _PyTime_timeval; + +#define _PyTime_ADD_SECONDS(tv, interval) \ +do { \ + tv.tv_usec += (long) (((long) interval - interval) * 1000000); \ + tv.tv_sec += (time_t) interval + (time_t) (tv.tv_usec / 1000000); \ + tv.tv_usec %= 1000000; \ +} while (0) + +#define _PyTime_INTERVAL(tv_start, tv_end) \ + ((tv_end.tv_sec - tv_start.tv_sec) + \ + (tv_end.tv_usec - tv_start.tv_usec) * 0.000001) + +void _PyTime_gettimeofday(_PyTime_timeval *tp) { + double now = 0.0; + PyObject *t = PyImport_ImportModuleNoBlock("time"); + if (t == NULL) { + PyErr_Clear(); + } + else { + PyObject *f = PyObject_CallMethod(t, "time", NULL); + if (f == NULL) { + PyErr_Clear(); + } + else { + if (PyFloat_Check(f)) + now = PyFloat_AsDouble(f); + Py_DECREF(f); + } + Py_DECREF(t); + } + tp->tv_sec = 0; + tp->tv_usec = 0; + if (now > 0.0) { + _PyTime_ADD_SECONDS((*tp), now); + } + else { + tp->tv_sec = time(NULL); + } +} + +#define BEGIN_SELECT_LOOP(s) \ + { \ + _PyTime_timeval now, deadline = {0, 0}; \ + double interval = s->sock_timeout; \ + int has_timeout = s->sock_timeout > 0.0; \ + if (has_timeout) { \ + _PyTime_gettimeofday(&now); \ + deadline = now; \ + _PyTime_ADD_SECONDS(deadline, s->sock_timeout); \ + } \ + while (1) { \ + errno = 0; \ + +#define END_SELECT_LOOP(s) \ + if (!has_timeout || \ + (!CHECK_ERRNO(EWOULDBLOCK) && !CHECK_ERRNO(EAGAIN))) \ + break; \ + _PyTime_gettimeofday(&now); \ + interval = _PyTime_INTERVAL(now, deadline); \ + } \ + } \ + /* Initialize a new socket object. */ static double defaulttimeout = -1.0; /* Default timeout for new sockets */ @@ -1611,8 +1724,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 @@ -1621,6 +1735,7 @@ PyErr_SetString(socket_timeout, "timed out"); return NULL; } + END_SELECT_LOOP(s) #ifdef MS_WINDOWS if (newfd == INVALID_SOCKET) @@ -2307,8 +2422,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 @@ -2317,6 +2433,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 */ @@ -2338,8 +2455,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 @@ -2348,6 +2466,7 @@ PyErr_SetString(socket_timeout, "timed out"); return -1; } + END_SELECT_LOOP(s) if (nread < 0) { s->errorhandler(); return -1; @@ -2505,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) @@ -2528,6 +2648,7 @@ PyErr_SetString(socket_timeout, "timed out"); return -1; } + END_SELECT_LOOP(s) if (n < 0) { s->errorhandler(); return -1; @@ -2659,8 +2780,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); @@ -2669,12 +2791,14 @@ #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); @@ -2718,8 +2842,13 @@ #else n = send(s->sock_fd, buf, len, flags); #endif - if (n < 0) - break; + if (n < 0) { + /* Check for an error indicating the operation should be retried */ + if ((s->sock_timeout > 0.0) && (CHECK_ERRNO(EWOULDBLOCK) || CHECK_ERRNO(EAGAIN))) + continue; + else + break; + } buf += n; len -= n; } while (len > 0); @@ -2778,17 +2907,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);