Commit 93ab5fa1 authored by Anthony Baxter's avatar Anthony Baxter

#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.
parent 06c68b80
...@@ -105,6 +105,10 @@ Library ...@@ -105,6 +105,10 @@ Library
Extension Modules Extension Modules
----------------- -----------------
- #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.
- Assigning None to pointer type fields in ctypes structures possible - Assigning None to pointer type fields in ctypes structures possible
overwrote the wrong fields, this is fixed now. overwrote the wrong fields, this is fixed now.
......
...@@ -26,6 +26,12 @@ enum py_ssl_error { ...@@ -26,6 +26,12 @@ enum py_ssl_error {
/* Include symbols from _socket module */ /* Include symbols from _socket module */
#include "socketmodule.h" #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 header files */
#include "openssl/rsa.h" #include "openssl/rsa.h"
#include "openssl/crypto.h" #include "openssl/crypto.h"
...@@ -351,7 +357,7 @@ static void PySSL_dealloc(PySSLObject *self) ...@@ -351,7 +357,7 @@ static void PySSL_dealloc(PySSLObject *self)
PyObject_Del(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. The argument writing indicates the direction.
Returns one of the possibilities in the timeout_state enum (above). Returns one of the possibilities in the timeout_state enum (above).
*/ */
...@@ -373,6 +379,26 @@ check_socket_and_wait_for_timeout(PySocketSockObject *s, int writing) ...@@ -373,6 +379,26 @@ check_socket_and_wait_for_timeout(PySocketSockObject *s, int writing)
if (s->sock_fd < 0) if (s->sock_fd < 0)
return SOCKET_HAS_BEEN_CLOSED; 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*/ /* Guard against socket too large for select*/
#ifndef Py_SOCKET_FD_CAN_BE_GE_FD_SETSIZE #ifndef Py_SOCKET_FD_CAN_BE_GE_FD_SETSIZE
if (s->sock_fd >= FD_SETSIZE) if (s->sock_fd >= FD_SETSIZE)
...@@ -393,6 +419,7 @@ check_socket_and_wait_for_timeout(PySocketSockObject *s, int writing) ...@@ -393,6 +419,7 @@ check_socket_and_wait_for_timeout(PySocketSockObject *s, int writing)
rc = select(s->sock_fd+1, &fds, NULL, NULL, &tv); rc = select(s->sock_fd+1, &fds, NULL, NULL, &tv);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
normal_return:
/* Return SOCKET_TIMED_OUT on timeout, SOCKET_OPERATION_OK otherwise /* Return SOCKET_TIMED_OUT on timeout, SOCKET_OPERATION_OK otherwise
(when we are able to write or when there's something to read) */ (when we are able to write or when there's something to read) */
return rc == 0 ? SOCKET_HAS_TIMED_OUT : SOCKET_OPERATION_OK; return rc == 0 ? SOCKET_HAS_TIMED_OUT : SOCKET_OPERATION_OK;
......
...@@ -411,14 +411,24 @@ static int taskwindow; ...@@ -411,14 +411,24 @@ static int taskwindow;
there has to be a circular reference. */ there has to be a circular reference. */
static PyTypeObject sock_type; 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 #ifdef Py_SOCKET_FD_CAN_BE_GE_FD_SETSIZE
/* Platform can select file descriptors beyond FD_SETSIZE */ /* Platform can select file descriptors beyond FD_SETSIZE */
#define IS_SELECTABLE(s) 1 #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 #else
/* POSIX says selecting file descriptors beyond FD_SETSIZE /* POSIX says selecting file descriptors beyond FD_SETSIZE
has undefined behaviour. */ has undefined behaviour. If there's no timeout left, we don't have to
#define IS_SELECTABLE(s) ((s)->sock_fd < FD_SETSIZE) 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 #endif
static PyObject* static PyObject*
...@@ -686,7 +696,7 @@ internal_setblocking(PySocketSockObject *s, int block) ...@@ -686,7 +696,7 @@ internal_setblocking(PySocketSockObject *s, int block)
return 1; 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. The argument writing indicates the direction.
This does not raise an exception; we'll let our caller do that This does not raise an exception; we'll let our caller do that
after they've reacquired the interpreter lock. after they've reacquired the interpreter lock.
...@@ -694,8 +704,6 @@ internal_setblocking(PySocketSockObject *s, int block) ...@@ -694,8 +704,6 @@ internal_setblocking(PySocketSockObject *s, int block)
static int static int
internal_select(PySocketSockObject *s, int writing) internal_select(PySocketSockObject *s, int writing)
{ {
fd_set fds;
struct timeval tv;
int n; int n;
/* Nothing to do unless we're in timeout mode (not non-blocking) */ /* Nothing to do unless we're in timeout mode (not non-blocking) */
...@@ -706,17 +714,37 @@ internal_select(PySocketSockObject *s, int writing) ...@@ -706,17 +714,37 @@ internal_select(PySocketSockObject *s, int writing)
if (s->sock_fd < 0) if (s->sock_fd < 0)
return 0; return 0;
/* Construct the arguments to select */ /* Prefer poll, if available, since you can poll() any fd
tv.tv_sec = (int)s->sock_timeout; * which can't be done with select(). */
tv.tv_usec = (int)((s->sock_timeout - tv.tv_sec) * 1e6); #ifdef HAVE_POLL
FD_ZERO(&fds); {
FD_SET(s->sock_fd, &fds); struct pollfd pollfd;
int timeout;
/* See if the socket is ready */ pollfd.fd = s->sock_fd;
if (writing) pollfd.events = writing ? POLLOUT : POLLIN;
n = select(s->sock_fd+1, NULL, &fds, NULL, &tv);
else /* s->sock_timeout is in seconds, timeout in ms */
n = select(s->sock_fd+1, &fds, NULL, NULL, &tv); 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) if (n == 0)
return 1; return 1;
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment