From d738f84ed1b3283a968b0550011799e408bd0527 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Tue, 19 Jan 2016 04:32:14 +0800 Subject: [PATCH] timeout move to doubly linked list In the case we have a lot of connections, checking them all for timeout state once a second becomes burdensome. At the moment if you have 100K connections, once a second they all get checked for timeout in a loop. This patch adds a doubly-linked list based in the context to each wsi, and only wsi with pending timeouts appear on it. At checking time, we traverse the list, which costs nothing if empty because nobody has a pending timeout. Similarly adding and removing from the list costs almost nothing since no iteration is required no matter how big the list. The extra 8 or 16 bytes in the wsi are offset a little bit by demoting .pps from int to char (save 3 bytes). And trim max act exts to 2, since we only provide one, saving 8 /16 bytes by itself if exts enabled. Signed-off-by: Andy Green --- lib/libwebsockets.c | 27 ++++++++++++++++++++++++++- lib/private-libwebsockets.h | 8 ++++++-- lib/service.c | 15 +++++---------- 3 files changed, 37 insertions(+), 13 deletions(-) diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c index ad081ec..f8c60a3 100644 --- a/lib/libwebsockets.c +++ b/lib/libwebsockets.c @@ -68,6 +68,19 @@ lws_free_wsi(struct lws *wsi) lws_free(wsi); } + +static void +lws_remove_from_timeout_list(struct lws *wsi) +{ + if (!wsi->timeout_list_prev) + return; + + *wsi->timeout_list_prev = wsi->timeout_list; + wsi->timeout_list_prev = NULL; + wsi->timeout_list = NULL; +} + + void lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason) { @@ -234,6 +247,7 @@ just_kill_connection: * delete socket from the internal poll list if still present */ lws_ssl_remove_wsi_from_buffered_list(wsi); + lws_remove_from_timeout_list(wsi); /* checking return redundant since we anyway close */ remove_wsi_socket_from_fds(wsi); @@ -563,8 +577,19 @@ lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs) time(&now); + if (!wsi->pending_timeout) { + wsi->timeout_list = wsi->context->timeout_list; + if (wsi->timeout_list) + wsi->timeout_list->timeout_list_prev = &wsi->timeout_list; + wsi->timeout_list_prev = &wsi->context->timeout_list; + *wsi->timeout_list_prev = wsi; + } + wsi->pending_timeout_limit = now + secs; wsi->pending_timeout = reason; + + if (!reason) + lws_remove_from_timeout_list(wsi); } @@ -1144,7 +1169,7 @@ lws_parse_uri(char *p, const char **prot, const char **ads, int *port, const cha *port = 80; else if (!strcmp(*prot, "https") || !strcmp(*prot, "wss")) *port = 443; - + while (*p && *p != ':' && *p != '/') p++; if (*p == ':') { diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h index 92ea713..7a16cb5 100644 --- a/lib/private-libwebsockets.h +++ b/lib/private-libwebsockets.h @@ -290,7 +290,7 @@ extern "C" { #define LWS_MAX_PROTOCOLS 5 #endif #ifndef LWS_MAX_EXTENSIONS_ACTIVE -#define LWS_MAX_EXTENSIONS_ACTIVE 3 +#define LWS_MAX_EXTENSIONS_ACTIVE 2 #endif #ifndef LWS_MAX_EXT_OFFERS #define LWS_MAX_EXT_OFFERS 8 @@ -522,6 +522,8 @@ struct lws_context { const char *iface; const struct lws_token_limits *token_limits; void *user_space; + struct lws *timeout_list; + #ifndef LWS_NO_SERVER struct lws *wsi_listening; #endif @@ -907,6 +909,8 @@ struct lws { struct lws_context *context; const struct lws_protocols *protocol; + struct lws *timeout_list; + struct lws **timeout_list_prev; void *user_space; /* rxflow handling */ unsigned char *rxflow_buffer; @@ -929,7 +933,6 @@ struct lws { lws_sockfd_type sock; /* ints */ - enum lws_pending_protocol_send pps; int position_in_fds_table; int rxflow_len; int rxflow_pos; @@ -962,6 +965,7 @@ struct lws { char lws_rx_parse_state; /* enum lws_rx_parse_state */ char rx_frame_type; /* enum lws_write_protocol */ char pending_timeout; /* enum pending_timeout */ + char pps; /* enum lws_pending_protocol_send */ }; LWS_EXTERN int log_level; diff --git a/lib/service.c b/lib/service.c index 4544b50..9ddbeae 100644 --- a/lib/service.c +++ b/lib/service.c @@ -298,8 +298,7 @@ lws_service_timeout_check(struct lws *wsi, unsigned int sec) * if extensions want in on it (eg, we are a mux parent) * give them a chance to service child timeouts */ - if (lws_ext_cb_active(wsi, LWS_EXT_CB_1HZ, - NULL, sec) < 0) + if (lws_ext_cb_active(wsi, LWS_EXT_CB_1HZ, NULL, sec) < 0) return 0; if (!wsi->pending_timeout) @@ -380,7 +379,6 @@ lws_service_fd(struct lws_context *context, struct lws_pollfd *pollfd) struct lws_tokens eff_buf; unsigned int pending = 0; char draining_flow = 0; - lws_sockfd_type mfd; int timed_out = 0; struct lws *wsi; time_t now; @@ -410,18 +408,15 @@ lws_service_fd(struct lws_context *context, struct lws_pollfd *pollfd) if (pollfd) our_fd = pollfd->fd; - for (n = 0; n < context->fds_count; n++) { - mfd = context->fds[n].fd; - wsi = wsi_from_fd(context, mfd); - if (!wsi) - continue; - + wsi = context->timeout_list; + while (wsi) { if (lws_service_timeout_check(wsi, (unsigned int)now)) /* he did time out... */ - if (mfd == our_fd) + if (wsi->sock == our_fd) /* it was the guy we came to service! */ timed_out = 1; /* he's gone, no need to mark as handled */ + wsi = wsi->timeout_list; } } -- 2.7.4