]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Backport 50567
authorAnthony Baxter <anthonybaxter@gmail.com>
Tue, 10 Oct 2006 16:20:41 +0000 (16:20 +0000)
committerAnthony Baxter <anthonybaxter@gmail.com>
Tue, 10 Oct 2006 16:20:41 +0000 (16:20 +0000)
#1494314: Fix a regression with high-numbered sockets in 2.4.3. This
means that select() on sockets > FD_SETSIZE (typically 1024) work again.
The patch makes sockets use poll() internally where available.

Modules/_ssl.c
Modules/socketmodule.c

index b4545579f35e3139746fde501feeeb8d48e80e9c..f90ec1389eb47f646662960701a41ce2e7b9e93a 100644 (file)
@@ -26,6 +26,12 @@ enum py_ssl_error {
 /* Include symbols from _socket module */
 #include "socketmodule.h"
 
+#if defined(HAVE_POLL_H) 
+#include <poll.h>
+#elif defined(HAVE_SYS_POLL_H)
+#include <sys/poll.h>
+#endif
+
 /* Include OpenSSL header files */
 #include "openssl/rsa.h"
 #include "openssl/crypto.h"
@@ -354,7 +360,7 @@ static void PySSL_dealloc(PySSLObject *self)
        PyObject_Del(self);
 }
 
-/* If the socket has a timeout, do a select() on the socket.
+/* If the socket has a timeout, do a select()/poll() on the socket.
    The argument writing indicates the direction.
    Returns one of the possibilities in the timeout_state enum (above).
  */
@@ -376,6 +382,26 @@ check_socket_and_wait_for_timeout(PySocketSockObject *s, int writing)
        if (s->sock_fd < 0)
                return SOCKET_HAS_BEEN_CLOSED;
 
+       /* Prefer poll, if available, since you can poll() any fd
+        * which can't be done with select(). */
+#ifdef HAVE_POLL
+       {
+               struct pollfd pollfd;
+               int timeout;
+
+               pollfd.fd = s->sock_fd;
+               pollfd.events = writing ? POLLOUT : POLLIN;
+
+               /* s->sock_timeout is in seconds, timeout in ms */
+               timeout = (int)(s->sock_timeout * 1000 + 0.5);
+               Py_BEGIN_ALLOW_THREADS
+               rc = poll(&pollfd, 1, timeout);
+               Py_END_ALLOW_THREADS
+
+               goto normal_return;
+       }
+#endif
+
        /* Guard against socket too large for select*/
 #ifndef Py_SOCKET_FD_CAN_BE_GE_FD_SETSIZE
        if (s->sock_fd >= FD_SETSIZE)
@@ -396,6 +422,7 @@ check_socket_and_wait_for_timeout(PySocketSockObject *s, int writing)
                rc = select(s->sock_fd+1, &fds, NULL, NULL, &tv);
        Py_END_ALLOW_THREADS
 
+normal_return:
        /* Return SOCKET_TIMED_OUT on timeout, SOCKET_OPERATION_OK otherwise
           (when we are able to write or when there's something to read) */
        return rc == 0 ? SOCKET_HAS_TIMED_OUT : SOCKET_OPERATION_OK;
index 1fafb7ef26705ec631688e42de7a0ba7a333d942..9c4d8cb2abed0121b47cd43871a43928f9d508a2 100644 (file)
@@ -412,14 +412,24 @@ static int taskwindow;
    there has to be a circular reference. */
 static PyTypeObject sock_type;
 
-/* Can we call select() with this socket without a buffer overrun? */
+#if defined(HAVE_POLL_H)
+#include <poll.h>
+#elif defined(HAVE_SYS_POLL_H)
+#include <sys/poll.h>
+#endif
+
 #ifdef Py_SOCKET_FD_CAN_BE_GE_FD_SETSIZE
 /* Platform can select file descriptors beyond FD_SETSIZE */
 #define IS_SELECTABLE(s) 1
+#elif defined(HAVE_POLL)
+/* Instead of select(), we'll use poll() since poll() works on any fd. */
+#define IS_SELECTABLE(s) 1
+/* Can we call select() with this socket without a buffer overrun? */
 #else
 /* POSIX says selecting file descriptors beyond FD_SETSIZE
-   has undefined behaviour. */
-#define IS_SELECTABLE(s) ((s)->sock_fd < FD_SETSIZE)
+   has undefined behaviour.  If there's no timeout left, we don't have to
+   call select, so it's a safe, little white lie. */
+#define IS_SELECTABLE(s) ((s)->sock_fd < FD_SETSIZE || s->sock_timeout <= 0.0)
 #endif
 
 static PyObject*
@@ -662,7 +672,7 @@ internal_setblocking(PySocketSockObject *s, int block)
        return 1;
 }
 
-/* Do a select() on the socket, if necessary (sock_timeout > 0).
+/* Do a select()/poll() on the socket, if necessary (sock_timeout > 0).
    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.
@@ -670,8 +680,6 @@ internal_setblocking(PySocketSockObject *s, int block)
 static int
 internal_select(PySocketSockObject *s, int writing)
 {
-       fd_set fds;
-       struct timeval tv;
        int n;
 
        /* Nothing to do unless we're in timeout mode (not non-blocking) */
@@ -682,17 +690,37 @@ internal_select(PySocketSockObject *s, int writing)
        if (s->sock_fd < 0)
                return 0;
 
-       /* Construct the arguments to select */
-       tv.tv_sec = (int)s->sock_timeout;
-       tv.tv_usec = (int)((s->sock_timeout - tv.tv_sec) * 1e6);
-       FD_ZERO(&fds);
-       FD_SET(s->sock_fd, &fds);
+       /* Prefer poll, if available, since you can poll() any fd
+        * which can't be done with select(). */
+#ifdef HAVE_POLL
+       {
+               struct pollfd pollfd;
+               int timeout;
 
-       /* See if the socket is ready */
-       if (writing)
-               n = select(s->sock_fd+1, NULL, &fds, NULL, &tv);
-       else
-               n = select(s->sock_fd+1, &fds, NULL, NULL, &tv);
+               pollfd.fd = s->sock_fd;
+               pollfd.events = writing ? POLLOUT : POLLIN;
+
+               /* s->sock_timeout is in seconds, timeout in ms */
+               timeout = (int)(s->sock_timeout * 1000 + 0.5); 
+               n = poll(&pollfd, 1, timeout);
+       }
+#else
+       {
+               /* 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);
+               FD_ZERO(&fds);
+               FD_SET(s->sock_fd, &fds);
+
+               /* See if the socket is ready */
+               if (writing)
+                       n = select(s->sock_fd+1, NULL, &fds, NULL, &tv);
+               else
+                       n = select(s->sock_fd+1, &fds, NULL, NULL, &tv);
+       }
+#endif
        if (n == 0)
                return 1;
        return 0;