On most platforms select() is limitted by FD_SETSIZE while
poll() is not. This was a scaling issue for multi-user setups.
Using poll() has no downside other than the usual risk
that code modifications may introduce new bugs that have
yet to be found and fixed.
At least in theory this commit could also reduce the latency
when there are lots of connections and select() would use
"bit fields in arrays of integers" to store file descriptors.
Another side effect is that Privoxy no longer has to stop
monitoring the client sockets when pipelined requests are
waiting but can't be read yet.
This code keeps the select()-based code behind ifdefs for
now but hopefully it can be removed soonish to make the
code more readable.
Sponsored by: Robert Klemme
-const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.138 2016/09/27 22:48:28 ler762 Exp $";
+const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.139 2016/12/24 16:00:49 fabiankeil Exp $";
/*********************************************************************
*
* File : $Source: /cvsroot/ijbswa/current/jbsockets.c,v $
/*********************************************************************
*
* File : $Source: /cvsroot/ijbswa/current/jbsockets.c,v $
char service[6];
int retval;
jb_socket fd;
char service[6];
int retval;
jb_socket fd;
+#ifdef HAVE_POLL
+ struct pollfd poll_fd[1];
+#else
fd_set wfds;
struct timeval timeout;
fd_set wfds;
struct timeval timeout;
#if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__)
int flags;
#endif
#if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__)
int flags;
#endif
#ifndef _WIN32
if (fd >= FD_SETSIZE)
{
#ifndef _WIN32
if (fd >= FD_SETSIZE)
{
return JB_INVALID_SOCKET;
}
#endif
return JB_INVALID_SOCKET;
}
#endif
#ifdef FEATURE_EXTERNAL_FILTERS
mark_socket_for_close_on_execute(fd);
#ifdef FEATURE_EXTERNAL_FILTERS
mark_socket_for_close_on_execute(fd);
}
#endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) */
}
#endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) */
+#ifdef HAVE_POLL
+ poll_fd[0].fd = fd;
+ poll_fd[0].events = POLLOUT;
+
+ if (poll(poll_fd, 1, 30000) > 0)
+#else
/* wait for connection to complete */
FD_ZERO(&wfds);
FD_SET(fd, &wfds);
/* wait for connection to complete */
FD_ZERO(&wfds);
FD_SET(fd, &wfds);
/* MS Windows uses int, not SOCKET, for the 1st arg of select(). Weird! */
if ((select((int)fd + 1, NULL, &wfds, NULL, &timeout) > 0)
&& FD_ISSET(fd, &wfds))
/* MS Windows uses int, not SOCKET, for the 1st arg of select(). Weird! */
if ((select((int)fd + 1, NULL, &wfds, NULL, &timeout) > 0)
&& FD_ISSET(fd, &wfds))
{
socklen_t optlen = sizeof(socket_error);
if (!getsockopt(fd, SOL_SOCKET, SO_ERROR, &socket_error, &optlen))
{
socklen_t optlen = sizeof(socket_error);
if (!getsockopt(fd, SOL_SOCKET, SO_ERROR, &socket_error, &optlen))
struct sockaddr_in inaddr;
jb_socket fd;
unsigned int addr;
struct sockaddr_in inaddr;
jb_socket fd;
unsigned int addr;
+#ifdef HAVE_POLL
+ struct pollfd poll_fd[1];
+#else
fd_set wfds;
struct timeval tv[1];
fd_set wfds;
struct timeval tv[1];
#if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__)
int flags;
#endif
#if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__)
int flags;
#endif
return(JB_INVALID_SOCKET);
}
return(JB_INVALID_SOCKET);
}
#ifndef _WIN32
if (fd >= FD_SETSIZE)
{
#ifndef _WIN32
if (fd >= FD_SETSIZE)
{
close_socket(fd);
return JB_INVALID_SOCKET;
}
close_socket(fd);
return JB_INVALID_SOCKET;
}
#endif
set_no_delay_flag(fd);
#endif
set_no_delay_flag(fd);
}
#endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) */
}
#endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) */
+#ifdef HAVE_POLL
+ poll_fd[0].fd = fd;
+ poll_fd[0].events = POLLOUT;
+
+ if (poll(poll_fd, 1, 30000) <= 0)
+#else
/* wait for connection to complete */
FD_ZERO(&wfds);
FD_SET(fd, &wfds);
/* wait for connection to complete */
FD_ZERO(&wfds);
FD_SET(fd, &wfds);
/* MS Windows uses int, not SOCKET, for the 1st arg of select(). Weird! */
if (select((int)fd + 1, NULL, &wfds, NULL, tv) <= 0)
/* MS Windows uses int, not SOCKET, for the 1st arg of select(). Weird! */
if (select((int)fd + 1, NULL, &wfds, NULL, tv) <= 0)
{
close_socket(fd);
return(JB_INVALID_SOCKET);
{
close_socket(fd);
return(JB_INVALID_SOCKET);
*********************************************************************/
int data_is_available(jb_socket fd, int seconds_to_wait)
{
*********************************************************************/
int data_is_available(jb_socket fd, int seconds_to_wait)
{
+#ifdef HAVE_POLL
+ struct pollfd poll_fd[1];
+
+ poll_fd[0].fd = fd;
+ poll_fd[0].events = POLLIN;
+
+ n = poll(poll_fd, 1, seconds_to_wait * 1000);
+#else
fd_set rfds;
struct timeval timeout;
fd_set rfds;
struct timeval timeout;
memset(&timeout, 0, sizeof(timeout));
timeout.tv_sec = seconds_to_wait;
memset(&timeout, 0, sizeof(timeout));
timeout.tv_sec = seconds_to_wait;
FD_SET(fd, &rfds);
n = select(fd+1, &rfds, NULL, NULL, &timeout);
FD_SET(fd, &rfds);
n = select(fd+1, &rfds, NULL, NULL, &timeout);
/*
* XXX: Do we care about the different error conditions?
/*
* XXX: Do we care about the different error conditions?
int retval;
int i;
int max_selected_socket;
int retval;
int i;
int max_selected_socket;
+#ifdef HAVE_POLL
+ struct pollfd poll_fds[MAX_LISTENING_SOCKETS];
+ nfds_t polled_sockets;
+#else
jb_socket fd;
const char *host_addr;
size_t listen_addr_size;
c_length = sizeof(client);
jb_socket fd;
const char *host_addr;
size_t listen_addr_size;
c_length = sizeof(client);
+#ifdef HAVE_POLL
+ memset(poll_fds, 0, sizeof(poll_fds));
+ polled_sockets = 0;
+#else
/*
* Wait for a connection on any socket.
* Return immediately if no socket is listening.
* XXX: Why not treat this as fatal error?
*/
FD_ZERO(&selected_fds);
/*
* Wait for a connection on any socket.
* Return immediately if no socket is listening.
* XXX: Why not treat this as fatal error?
*/
FD_ZERO(&selected_fds);
max_selected_socket = 0;
for (i = 0; i < MAX_LISTENING_SOCKETS; i++)
{
if (JB_INVALID_SOCKET != fds[i])
{
max_selected_socket = 0;
for (i = 0; i < MAX_LISTENING_SOCKETS; i++)
{
if (JB_INVALID_SOCKET != fds[i])
{
+#ifdef HAVE_POLL
+ poll_fds[i].fd = fds[i];
+ poll_fds[i].events = POLLIN;
+ polled_sockets++;
+#else
FD_SET(fds[i], &selected_fds);
FD_SET(fds[i], &selected_fds);
if (max_selected_socket < fds[i] + 1)
{
max_selected_socket = fds[i] + 1;
if (max_selected_socket < fds[i] + 1)
{
max_selected_socket = fds[i] + 1;
+#ifdef HAVE_POLL
+ retval = poll(poll_fds, polled_sockets, -1);
+#else
retval = select(max_selected_socket, &selected_fds, NULL, NULL, NULL);
retval = select(max_selected_socket, &selected_fds, NULL, NULL, NULL);
} while (retval < 0 && errno == EINTR);
if (retval <= 0)
{
} while (retval < 0 && errno == EINTR);
if (retval <= 0)
{
+#ifdef HAVE_POLL
+ for (i = 0; i < MAX_LISTENING_SOCKETS && (poll_fds[i].revents == 0); i++);
+#else
for (i = 0; i < MAX_LISTENING_SOCKETS && !FD_ISSET(fds[i], &selected_fds);
i++);
for (i = 0; i < MAX_LISTENING_SOCKETS && !FD_ISSET(fds[i], &selected_fds);
i++);
if (i >= MAX_LISTENING_SOCKETS)
{
log_error(LOG_LEVEL_ERROR,
if (i >= MAX_LISTENING_SOCKETS)
{
log_error(LOG_LEVEL_ERROR,
#ifndef _WIN32
if (afd >= FD_SETSIZE)
{
#ifndef _WIN32
if (afd >= FD_SETSIZE)
{
#ifdef FEATURE_EXTERNAL_FILTERS
mark_socket_for_close_on_execute(afd);
#ifdef FEATURE_EXTERNAL_FILTERS
mark_socket_for_close_on_execute(afd);
-const char jcc_rcs[] = "$Id: jcc.c,v 1.454 2017/05/25 11:14:38 fabiankeil Exp $";
+const char jcc_rcs[] = "$Id: jcc.c,v 1.455 2017/05/25 11:16:04 fabiankeil Exp $";
/*********************************************************************
*
* File : $Source: /cvsroot/ijbswa/current/jcc.c,v $
/*********************************************************************
*
* File : $Source: /cvsroot/ijbswa/current/jcc.c,v $
# include <os2.h>
# endif
# include <os2.h>
# endif
+#ifdef HAVE_POLL
+#ifdef __GLIBC__
+#include <sys/poll.h>
+#else
+#include <poll.h>
+#endif /* def __GLIBC__ */
+#else
# ifndef FD_ZERO
# include <select.h>
# endif
# ifndef FD_ZERO
# include <select.h>
# endif
char buf[BUFFER_SIZE];
char *hdr;
char *p;
char buf[BUFFER_SIZE];
char *hdr;
char *p;
+#ifdef HAVE_POLL
+ struct pollfd poll_fds[2];
+#else
+ fd_set rfds;
+ struct timeval timeout;
+#endif
int server_body;
int ms_iis5_hack = 0;
unsigned long long byte_count = 0;
int server_body;
int ms_iis5_hack = 0;
unsigned long long byte_count = 0;
/* Skeleton for HTTP response, if we should intercept the request */
struct http_response *rsp;
/* Skeleton for HTTP response, if we should intercept the request */
struct http_response *rsp;
- struct timeval timeout;
#ifdef FEATURE_CONNECTION_KEEP_ALIVE
int watch_client_socket;
#endif
#ifdef FEATURE_CONNECTION_KEEP_ALIVE
int watch_client_socket;
#endif
maxfd = (csp->cfd > csp->server_connection.sfd) ?
csp->cfd : csp->server_connection.sfd;
maxfd = (csp->cfd > csp->server_connection.sfd) ?
csp->cfd : csp->server_connection.sfd;
/* pass data between the client and server
* until one or the other shuts down the connection.
/* pass data between the client and server
* until one or the other shuts down the connection.
#ifdef __OS2__
/*
* FD_ZERO here seems to point to an errant macro which crashes.
#ifdef __OS2__
/*
* FD_ZERO here seems to point to an errant macro which crashes.
}
FD_SET(csp->server_connection.sfd, &rfds);
}
FD_SET(csp->server_connection.sfd, &rfds);
+#endif /* ndef HAVE_POLL */
#ifdef FEATURE_CONNECTION_KEEP_ALIVE
if ((csp->flags & CSP_FLAG_CHUNKED)
#ifdef FEATURE_CONNECTION_KEEP_ALIVE
if ((csp->flags & CSP_FLAG_CHUNKED)
}
#endif /* FEATURE_CONNECTION_KEEP_ALIVE */
}
#endif /* FEATURE_CONNECTION_KEEP_ALIVE */
+#ifdef HAVE_POLL
+ poll_fds[0].fd = csp->cfd;
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
+ if (!watch_client_socket)
+ {
+ /*
+ * Ignore incoming data, but still watch out
+ * for disconnects etc. These flags are always
+ * implied anyway but explicitly setting them
+ * doesn't hurt.
+ */
+ poll_fds[0].events = POLLERR|POLLHUP;
+ }
+ else
+#endif
+ {
+ poll_fds[0].events = POLLIN;
+ }
+ poll_fds[1].fd = csp->server_connection.sfd;
+ poll_fds[1].events = POLLIN;
+ n = poll(poll_fds, 2, csp->config->socket_timeout * 1000);
+#else
timeout.tv_sec = csp->config->socket_timeout;
timeout.tv_usec = 0;
n = select((int)maxfd+1, &rfds, NULL, NULL, &timeout);
timeout.tv_sec = csp->config->socket_timeout;
timeout.tv_usec = 0;
n = select((int)maxfd+1, &rfds, NULL, NULL, &timeout);
+#endif /* def HAVE_POLL */
+#ifdef HAVE_POLL
+ log_error(LOG_LEVEL_ERROR, "poll() failed!: %E");
+#else
log_error(LOG_LEVEL_ERROR, "select() failed!: %E");
log_error(LOG_LEVEL_ERROR, "select() failed!: %E");
mark_server_socket_tainted(csp);
return;
}
mark_server_socket_tainted(csp);
return;
}
* XXX: Make sure the client doesn't use pipelining
* behind Privoxy's back.
*/
* XXX: Make sure the client doesn't use pipelining
* behind Privoxy's back.
*/
+#ifdef HAVE_POLL
+ if ((poll_fds[0].revents & (POLLERR|POLLHUP|POLLNVAL)) != 0)
+ {
+ log_error(LOG_LEVEL_CONNECT,
+ "The client socket %d has become unusable while "
+ "the server socket %d is still open.",
+ csp->cfd, csp->server_connection.sfd);
+ mark_server_socket_tainted(csp);
+ break;
+ }
+
+ if (poll_fds[0].revents != 0)
+#else
if (FD_ISSET(csp->cfd, &rfds))
if (FD_ISSET(csp->cfd, &rfds))
+#endif /* def HAVE_POLL*/
{
int max_bytes_to_read = sizeof(buf) - 1;
{
int max_bytes_to_read = sizeof(buf) - 1;
* If `hdr' is null, then it's the header otherwise it's the body.
* FIXME: Does `hdr' really mean `host'? No.
*/
* If `hdr' is null, then it's the header otherwise it's the body.
* FIXME: Does `hdr' really mean `host'? No.
*/
+#ifdef HAVE_POLL
+ if (poll_fds[1].revents != 0)
+#else
if (FD_ISSET(csp->server_connection.sfd, &rfds))
if (FD_ISSET(csp->server_connection.sfd, &rfds))
{
#ifdef FEATURE_CONNECTION_KEEP_ALIVE
/*
{
#ifdef FEATURE_CONNECTION_KEEP_ALIVE
/*
return JB_INVALID_SOCKET;
}
return JB_INVALID_SOCKET;
}
#ifndef _WIN32
if (bfd >= FD_SETSIZE)
{
#ifndef _WIN32
if (bfd >= FD_SETSIZE)
{
"Bind socket number too high to use select(): %d >= %d",
bfd, FD_SETSIZE);
}
"Bind socket number too high to use select(): %d >= %d",
bfd, FD_SETSIZE);
}
#endif
if (haddr == NULL)
#endif
if (haddr == NULL)
-const char loadcfg_rcs[] = "$Id: loadcfg.c,v 1.156 2017/02/24 11:59:44 fabiankeil Exp $";
+const char loadcfg_rcs[] = "$Id: loadcfg.c,v 1.157 2017/05/20 09:24:35 fabiankeil Exp $";
/*********************************************************************
*
* File : $Source: /cvsroot/ijbswa/current/loadcfg.c,v $
/*********************************************************************
*
* File : $Source: /cvsroot/ijbswa/current/loadcfg.c,v $
{
int max_client_connections = parse_numeric_value(cmd, arg);
{
int max_client_connections = parse_numeric_value(cmd, arg);
+#if !defined(_WIN32) && !defined(HAVE_POLL)
/*
* Reject values below 1 for obvious reasons and values above
* FD_SETSIZE/2 because Privoxy needs two sockets to serve
/*
* Reject values below 1 for obvious reasons and values above
* FD_SETSIZE/2 because Privoxy needs two sockets to serve
* passed to select().
* https://msdn.microsoft.com/en-us/library/windows/desktop/ms739169%28v=vs.85%29.aspx
*
* passed to select().
* https://msdn.microsoft.com/en-us/library/windows/desktop/ms739169%28v=vs.85%29.aspx
*
+ * On platforms were we use poll() we don't have to enforce
+ * an upper connection limit either.
+ *
* XXX: Do OS/2, Amiga etc. belong here as well?
*/
if (max_client_connections < 1)
* XXX: Do OS/2, Amiga etc. belong here as well?
*/
if (max_client_connections < 1)