From: Andy Green Date: Tue, 15 Jan 2013 23:59:47 +0000 (+0800) Subject: listen socket more frequent service X-Git-Tag: accepted/2.0/20130307.220733~258 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Fchanges%2F03%2F2903%2F1;p=profile%2Fivi%2Flibwebsockets.git listen socket more frequent service From an idea by Edwin van den Oetelaar When testing libwebsockets with ab, Edwin found an unexpected bump in the distribution of latencies, some connections were held back almost the whole test duration. http://ml.libwebsockets.org/pipermail/libwebsockets/2013-January/000006.html Studying the problem revealed that when there are mass pending connections amongst many active connections, we do not service the listen socket often enough to clear the backlog, some seem to get stale violating FIFO ordering. This patch introduces listen socket service "piggybacking", where every n normal socket service actions we also check the listen socket and deal with pending connections there. Normally, it checks the listen socket gratuitously every 10 normal socket services. However, if it finds something waiting, it forces a check on the next normal socket service too by keeping stats on how often something was waiting. If the probability of something waiting each time becomes high, it will allow up to two waiting connections to be serviced for each normal socket service. In that way it has low burden in the normal case, but rapidly adapts by detecting mass connection loads as found in ab. Signed-off-by: Andy Green --- diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c index 8bc91de..bbbbe49 100644 --- a/lib/libwebsockets.c +++ b/lib/libwebsockets.c @@ -1534,6 +1534,41 @@ libwebsocket_service_fd(struct libwebsocket_context *context, /* no, here to service a socket descriptor */ + /* + * deal with listen service piggybacking + * every listen_service_modulo services of other fds, we + * sneak one in to service the listen socket if there's anything waiting + * + * To handle connection storms, as found in ab, if we previously saw a + * pending connection here, it causes us to check again next time. + */ + + if (context->listen_service_fd && pollfd->fd != context->listen_service_fd) { + context->listen_service_count++; + if (context->listen_service_extraseen || + context->listen_service_count == context->listen_service_modulo) { + context->listen_service_count = 0; + m = 1; + if (context->listen_service_extraseen > 5) + m = 2; + while (m--) { + /* even with extpoll, we prepared this internal fds for listen */ + n = poll(&context->fds[0], 1, 0); + if (n > 0) { /* there's a connection waiting for us */ + libwebsocket_service_fd(context, &context->fds[0]); + context->listen_service_extraseen++; + } else { + if (context->listen_service_extraseen) + context->listen_service_extraseen--; + break; + } + } + } + + } + + /* okay, what we came here to do... */ + wsi = wsi_from_fd(context, pollfd->fd); if (wsi == NULL) { @@ -3040,10 +3075,14 @@ libwebsocket_create_context(int port, const char *interf, wsi->mode = LWS_CONNMODE_SERVER_LISTENER; insert_wsi(context, wsi); + context->listen_service_modulo = LWS_LISTEN_SERVICE_MODULO; + context->listen_service_count = 0; + context->listen_service_fd = sockfd; + listen(sockfd, LWS_SOMAXCONN); lwsl_info(" Listening on port %d\n", port); - /* list in the internal poll array */ + /* list in the internal poll array - we're always first */ context->fds[context->fds_count].fd = sockfd; context->fds[context->fds_count++].events = POLLIN; diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h index 27b2049..3d16a32 100644 --- a/lib/private-libwebsockets.h +++ b/lib/private-libwebsockets.h @@ -173,6 +173,11 @@ extern void _lws_log(int filter, const char *format, ...); #define LWS_MAX_ZLIB_CONN_BUFFER (64 * 1024) #endif +/* + * if not in a connection storm, check for incoming + * connections this many normal connection services + */ +#define LWS_LISTEN_SERVICE_MODULO 10 enum lws_websocket_opcodes_04 { LWS_WS_OPCODE_04__CONTINUATION = 0, @@ -294,6 +299,10 @@ struct libwebsocket_context { unsigned long last_timeout_check_s; int fd_random; + int listen_service_modulo; + int listen_service_count; + int listen_service_fd; + int listen_service_extraseen; #ifdef LWS_OPENSSL_SUPPORT int use_ssl;