From ae595cd529e4618611c2a2258e73d949f2856bb9 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Sat, 26 Mar 2011 10:59:48 -0500 Subject: [PATCH] use poll instead of select in ESL client lib because select is not your friend.... --- libs/esl/src/esl.c | 215 +++++++++++++++++++++++++++---------- libs/esl/src/include/esl.h | 7 ++ 2 files changed, 168 insertions(+), 54 deletions(-) diff --git a/libs/esl/src/esl.c b/libs/esl/src/esl.c index 27000f6071..fab8b4ad42 100644 --- a/libs/esl/src/esl.c +++ b/libs/esl/src/esl.c @@ -31,6 +31,30 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +/* Use select on windows and poll everywhere else. + Select is the devil. Especially if you are doing a lot of small socket connections. + If your FD number is bigger than 1024 you will silently create memory corruption. + + If you have build errors on your platform because you don't have poll find a way to detect it and #define ESL_USE_SELECT and #undef ESL_USE_POLL + All of this will be upgraded to autoheadache eventually. +*/ + +/* TBD for win32 figure out how to tell if you have WSAPoll (vista or higher) and use it when available by #defining ESL_USE_WSAPOLL (see below) */ + +#ifdef _MSC_VER +#define FD_SETSIZE 8192 +#define ESL_USE_SELECT +#else +#define ESL_USE_POLL +#endif +#ifdef ESL_USE_POLL +#include +#endif + + + + #include #ifndef WIN32 #define closesocket(x) close(x) @@ -614,6 +638,135 @@ ESL_DECLARE(esl_status_t) esl_listen(const char *host, esl_port_t port, esl_list } + +/* USE WSAPoll on vista or higher */ +#ifdef ESL_USE_WSAPOLL +ESL_DECLARE(int) esl_wait_sock(esl_socket_t sock, uint32_t ms, esl_poll_t flags) +{ +} +#endif + + +#ifdef ESL_USE_SELECT +ESL_DECLARE(int) esl_wait_sock(esl_socket_t sock, uint32_t ms, esl_poll_t flags) +{ + int s = 0, r = 0; + fd_set rfds; + fd_set wfds; + fd_set efds; + struct timeval tv; + + /* Wouldn't you rather know?? */ + assert(sock <= FD_SETSIZE); + + + if ((flags & ESL_POLL_READ)) { + FD_ZERO(&rfds); + +#ifdef WIN32 +#pragma warning( push ) +#pragma warning( disable : 4127 ) + FD_SET(sock, &rfds); +#pragma warning( pop ) +#else + FD_SET(sock, &rfds); +#endif + } + + if ((flags & ESL_POLL_WRITE)) { + FD_ZERO(&wfds); + +#ifdef WIN32 +#pragma warning( push ) +#pragma warning( disable : 4127 ) + FD_SET(sock, &wfds); +#pragma warning( pop ) +#else + FD_SET(sock, &wfds); +#endif + } + + if ((flags & ESL_POLL_ERROR)) { + FD_ZERO(&efds); + +#ifdef WIN32 +#pragma warning( push ) +#pragma warning( disable : 4127 ) + FD_SET(sock, &efds); +#pragma warning( pop ) +#else + FD_SET(sock, &efds); +#endif + } + + tv.tv_sec = ms / 1000; + tv.tv_usec = (ms % 1000) * ms; + + s = select(sock + 1, (flags & ESL_POLL_READ) ? &rfds : NULL, (flags & ESL_POLL_WRITE) ? &wfds : NULL, (flags & ESL_POLL_ERROR) ? &efds : NULL, &tv); + + if (s < 0) { + r = s; + } else if (s > 0) { + if ((flags & ESL_POLL_READ) && FD_ISSET(sock, &rfds)) { + r |= ESL_POLL_READ; + } + + if ((flags & ESL_POLL_WRITE) && FD_ISSET(sock, &wfds)) { + r |= ESL_POLL_WRITE; + } + + if ((flags & ESL_POLL_ERROR) && FD_ISSET(sock, &efds)) { + r |= ESL_POLL_ERROR; + } + } + + return r; + +} +#endif + +#ifdef ESL_USE_POLL +ESL_DECLARE(int) esl_wait_sock(esl_socket_t sock, uint32_t ms, esl_poll_t flags) +{ + struct pollfd pfds[2] = { { 0 } }; + int s = 0, r = 0; + + pfds[0].fd = sock; + + if ((flags & ESL_POLL_READ)) { + pfds[0].events |= POLLIN; + } + + if ((flags & ESL_POLL_WRITE)) { + pfds[0].events |= POLLOUT; + } + + if ((flags & ESL_POLL_ERROR)) { + pfds[0].events |= POLLERR; + } + + s = poll(pfds, 1, ms); + + if (s < 0) { + r = s; + } else if (s > 0) { + if ((pfds[0].revents & POLLIN)) { + r |= ESL_POLL_READ; + } + if ((pfds[0].revents & POLLOUT)) { + r |= ESL_POLL_WRITE; + } + if ((pfds[0].revents & POLLERR)) { + r |= ESL_POLL_ERROR; + } + } + + return r; + +} +#endif + + ESL_DECLARE(esl_status_t) esl_connect_timeout(esl_handle_t *handle, const char *host, esl_port_t port, const char *user, const char *password, uint32_t timeout) { char sendbuf[256]; @@ -681,30 +834,17 @@ ESL_DECLARE(esl_status_t) esl_connect_timeout(esl_handle_t *handle, const char * rval = connect(handle->sock, (struct sockaddr*)&handle->sockaddr, sizeof(handle->sockaddr)); if (timeout) { - fd_set wfds; - struct timeval tv; int r; - tv.tv_sec = timeout / 1000; - tv.tv_usec = (timeout % 1000) * 1000; - FD_ZERO(&wfds); -#ifdef WIN32 -#pragma warning( push ) -#pragma warning( disable : 4127 ) - FD_SET(handle->sock, &wfds); -#pragma warning( pop ) -#else - FD_SET(handle->sock, &wfds); -#endif - r = select(handle->sock + 1, NULL, &wfds, NULL, &tv); + r = esl_wait_sock(handle->sock, timeout, ESL_POLL_WRITE); if (r <= 0) { snprintf(handle->err, sizeof(handle->err), "Connection timed out"); goto fail; } - if (!FD_ISSET(handle->sock, &wfds)) { + if (!(r & ESL_POLL_WRITE)) { snprintf(handle->err, sizeof(handle->err), "Connection timed out"); goto fail; } @@ -823,9 +963,7 @@ ESL_DECLARE(esl_status_t) esl_disconnect(esl_handle_t *handle) ESL_DECLARE(esl_status_t) esl_recv_event_timed(esl_handle_t *handle, uint32_t ms, int check_q, esl_event_t **save_event) { - fd_set rfds, efds; - struct timeval tv = { 0 }; - int max, activity; + int activity; esl_status_t status = ESL_SUCCESS; if (!ms) { @@ -845,55 +983,24 @@ ESL_DECLARE(esl_status_t) esl_recv_event_timed(esl_handle_t *handle, uint32_t ms esl_mutex_unlock(handle->mutex); } - tv.tv_usec = ms * 1000; - - FD_ZERO(&rfds); - FD_ZERO(&efds); - -#ifdef WIN32 -#pragma warning( push ) -#pragma warning( disable : 4127 ) - FD_SET(handle->sock, &rfds); - FD_SET(handle->sock, &efds); -#pragma warning( pop ) -#else - FD_SET(handle->sock, &rfds); - FD_SET(handle->sock, &efds); -#endif - - max = handle->sock + 1; + activity = esl_wait_sock(handle->sock, ms, ESL_POLL_READ|ESL_POLL_ERROR); - if ((activity = select(max, &rfds, NULL, &efds, &tv)) < 0) { + if (activity < 0) { handle->connected = 0; return ESL_FAIL; } - if (activity == 0 || !FD_ISSET(handle->sock, &rfds) || (esl_mutex_trylock(handle->mutex) != ESL_SUCCESS)) { + if (activity == 0 || !(activity & ESL_POLL_READ) || (esl_mutex_trylock(handle->mutex) != ESL_SUCCESS)) { return ESL_BREAK; } - tv.tv_usec = 0; + activity = esl_wait_sock(handle->sock, ms, ESL_POLL_READ|ESL_POLL_ERROR); - FD_ZERO(&rfds); - FD_ZERO(&efds); - -#ifdef WIN32 -#pragma warning( push ) -#pragma warning( disable : 4127 ) - FD_SET(handle->sock, &rfds); - FD_SET(handle->sock, &efds); -#pragma warning( pop ) -#else - FD_SET(handle->sock, &rfds); - FD_SET(handle->sock, &efds); -#endif - - activity = select(max, &rfds, NULL, &efds, &tv); if (activity < 0) { handle->connected = 0; status = ESL_FAIL; - } else if (activity > 0 && FD_ISSET(handle->sock, &rfds)) { + } else if (activity > 0 && (activity & ESL_POLL_READ)) { if (esl_recv_event(handle, check_q, save_event)) { status = ESL_FAIL; } diff --git a/libs/esl/src/include/esl.h b/libs/esl/src/include/esl.h index 99ab53ae95..17ebf26ee1 100644 --- a/libs/esl/src/include/esl.h +++ b/libs/esl/src/include/esl.h @@ -46,6 +46,11 @@ extern "C" { typedef struct esl_event_header esl_event_header_t; typedef struct esl_event esl_event_t; +typedef enum { + ESL_POLL_READ = (1 << 0), + ESL_POLL_WRITE = (1 << 1), + ESL_POLL_ERROR = (1 << 2) +} esl_poll_t; typedef enum { ESL_EVENT_TYPE_PLAIN, @@ -446,6 +451,8 @@ ESL_DECLARE(esl_status_t) esl_filter(esl_handle_t *handle, const char *header, c */ ESL_DECLARE(esl_status_t) esl_events(esl_handle_t *handle, esl_event_type_t etype, const char *value); +ESL_DECLARE(int) esl_wait_sock(esl_socket_t sock, uint32_t ms, esl_poll_t flags); + #define esl_recv(_h) esl_recv_event(_h, 0, NULL) #define esl_recv_timed(_h, _ms) esl_recv_event_timed(_h, _ms, 0, NULL)