From 78f4b120a199eee616d61d79185a15567ea1ae83 Mon Sep 17 00:00:00 2001 From: Igor Zinkovsky Date: Wed, 26 Oct 2011 13:29:21 -0700 Subject: [PATCH] windows: knob for tuning number of concurrent accept requests --- include/uv-private/uv-win.h | 1 + include/uv.h | 10 ++++++ src/unix/tcp.c | 5 +++ src/win/internal.h | 56 +++++++++++++++++--------------- src/win/tcp.c | 65 ++++++++++++++++++++++++++++++++++--- 5 files changed, 106 insertions(+), 31 deletions(-) diff --git a/include/uv-private/uv-win.h b/include/uv-private/uv-win.h index 9d18fa16..5d461090 100644 --- a/include/uv-private/uv-win.h +++ b/include/uv-private/uv-win.h @@ -247,6 +247,7 @@ RB_HEAD(uv_timer_tree_s, uv_timer_s); #define uv_tcp_server_fields \ uv_tcp_accept_t* accept_reqs; \ + unsigned int processed_accepts; \ uv_tcp_accept_t* pending_accepts; \ LPFN_ACCEPTEX func_acceptex; diff --git a/include/uv.h b/include/uv.h index b1ea7a56..3336b757 100644 --- a/include/uv.h +++ b/include/uv.h @@ -481,6 +481,16 @@ UV_EXTERN int uv_tcp_nodelay(uv_tcp_t* handle, int enable); UV_EXTERN int uv_tcp_keepalive(uv_tcp_t* handle, int enable, unsigned int delay); +/* + * This setting applies to Windows only. + * Enable/disable simultaneous asynchronous accept requests that are + * queued by the operating system when listening for new tcp connections. + * This setting is used to tune a tcp server for the desired performance. + * Having simultaneous accepts can significantly improve the rate of + * accepting connections (which is why it is enabled by default). + */ +UV_EXTERN int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable); + UV_EXTERN int uv_tcp_bind(uv_tcp_t* handle, struct sockaddr_in); UV_EXTERN int uv_tcp_bind6(uv_tcp_t* handle, struct sockaddr_in6); UV_EXTERN int uv_tcp_getsockname(uv_tcp_t* handle, struct sockaddr* name, diff --git a/src/unix/tcp.c b/src/unix/tcp.c index 67ed2171..50608da7 100644 --- a/src/unix/tcp.c +++ b/src/unix/tcp.c @@ -319,3 +319,8 @@ int uv_tcp_keepalive(uv_tcp_t* handle, int enable, unsigned int delay) { return 0; } + + +int uv_tcp_multiple_simultaneous_accepts(uv_tcp_t* handle, int enable) { + return 0; +} diff --git a/src/win/internal.h b/src/win/internal.h index 8be4fb86..08dbcc2f 100644 --- a/src/win/internal.h +++ b/src/win/internal.h @@ -44,33 +44,35 @@ void uv_process_timers(uv_loop_t* loop); */ /* Private uv_handle flags */ -#define UV_HANDLE_CLOSING 0x0000001 -#define UV_HANDLE_CLOSED 0x0000002 -#define UV_HANDLE_BOUND 0x0000004 -#define UV_HANDLE_LISTENING 0x0000008 -#define UV_HANDLE_CONNECTION 0x0000010 -#define UV_HANDLE_CONNECTED 0x0000020 -#define UV_HANDLE_READING 0x0000040 -#define UV_HANDLE_ACTIVE 0x0000040 -#define UV_HANDLE_EOF 0x0000080 -#define UV_HANDLE_SHUTTING 0x0000100 -#define UV_HANDLE_SHUT 0x0000200 -#define UV_HANDLE_ENDGAME_QUEUED 0x0000400 -#define UV_HANDLE_BIND_ERROR 0x0001000 -#define UV_HANDLE_IPV6 0x0002000 -#define UV_HANDLE_PIPESERVER 0x0004000 -#define UV_HANDLE_READ_PENDING 0x0008000 -#define UV_HANDLE_UV_ALLOCED 0x0010000 -#define UV_HANDLE_SYNC_BYPASS_IOCP 0x0020000 -#define UV_HANDLE_ZERO_READ 0x0040000 -#define UV_HANDLE_TTY_RAW 0x0080000 -#define UV_HANDLE_EMULATE_IOCP 0x0100000 -#define UV_HANDLE_NON_OVERLAPPED_PIPE 0x0200000 -#define UV_HANDLE_TTY_SAVED_POSITION 0x0400000 -#define UV_HANDLE_TTY_SAVED_ATTRIBUTES 0x0800000 -#define UV_HANDLE_SHARED_TCP_SERVER 0x1000000 -#define UV_HANDLE_TCP_NODELAY 0x2000000 -#define UV_HANDLE_TCP_KEEPALIVE 0x4000000 +#define UV_HANDLE_CLOSING 0x00000001 +#define UV_HANDLE_CLOSED 0x00000002 +#define UV_HANDLE_BOUND 0x00000004 +#define UV_HANDLE_LISTENING 0x00000008 +#define UV_HANDLE_CONNECTION 0x00000010 +#define UV_HANDLE_CONNECTED 0x00000020 +#define UV_HANDLE_READING 0x00000040 +#define UV_HANDLE_ACTIVE 0x00000040 +#define UV_HANDLE_EOF 0x00000080 +#define UV_HANDLE_SHUTTING 0x00000100 +#define UV_HANDLE_SHUT 0x00000200 +#define UV_HANDLE_ENDGAME_QUEUED 0x00000400 +#define UV_HANDLE_BIND_ERROR 0x00001000 +#define UV_HANDLE_IPV6 0x00002000 +#define UV_HANDLE_PIPESERVER 0x00004000 +#define UV_HANDLE_READ_PENDING 0x00008000 +#define UV_HANDLE_UV_ALLOCED 0x00010000 +#define UV_HANDLE_SYNC_BYPASS_IOCP 0x00020000 +#define UV_HANDLE_ZERO_READ 0x00040000 +#define UV_HANDLE_TTY_RAW 0x00080000 +#define UV_HANDLE_EMULATE_IOCP 0x00100000 +#define UV_HANDLE_NON_OVERLAPPED_PIPE 0x00200000 +#define UV_HANDLE_TTY_SAVED_POSITION 0x00400000 +#define UV_HANDLE_TTY_SAVED_ATTRIBUTES 0x00800000 +#define UV_HANDLE_SHARED_TCP_SERVER 0x01000000 +#define UV_HANDLE_TCP_NODELAY 0x02000000 +#define UV_HANDLE_TCP_KEEPALIVE 0x04000000 +#define UV_HANDLE_TCP_SINGLE_ACCEPT 0x08000000 +#define UV_HANDLE_TCP_ACCEPT_STATE_CHANGING 0x10000000 void uv_want_endgame(uv_loop_t* loop, uv_handle_t* handle); void uv_process_endgames(uv_loop_t* loop); diff --git a/src/win/tcp.c b/src/win/tcp.c index 662090d0..c22d238a 100644 --- a/src/win/tcp.c +++ b/src/win/tcp.c @@ -152,6 +152,7 @@ int uv_tcp_init(uv_loop_t* loop, uv_tcp_t* handle) { handle->reqs_pending = 0; handle->func_acceptex = NULL; handle->func_connectex = NULL; + handle->processed_accepts = 0; loop->counters.tcp_init++; @@ -439,7 +440,7 @@ static void uv_tcp_queue_read(uv_loop_t* loop, uv_tcp_t* handle) { int uv_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb) { uv_loop_t* loop = handle->loop; - unsigned int i; + unsigned int i, simultaneous_accepts; uv_tcp_accept_t* req; assert(backlog > 0); @@ -469,14 +470,17 @@ int uv_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb) { handle->flags |= UV_HANDLE_LISTENING; handle->connection_cb = cb; + simultaneous_accepts = handle->flags & UV_HANDLE_TCP_SINGLE_ACCEPT ? 1 + : uv_simultaneous_server_accepts; + if(!handle->accept_reqs) { handle->accept_reqs = (uv_tcp_accept_t*) - malloc(uv_simultaneous_server_accepts * sizeof(uv_tcp_accept_t)); + malloc(simultaneous_accepts * sizeof(uv_tcp_accept_t)); if (!handle->accept_reqs) { uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); } - for (i = 0; i < uv_simultaneous_server_accepts; i++) { + for (i = 0; i < simultaneous_accepts; i++) { req = &handle->accept_reqs[i]; uv_req_init(loop, (uv_req_t*)req); req->type = UV_ACCEPT; @@ -533,7 +537,26 @@ int uv_tcp_accept(uv_tcp_t* server, uv_tcp_t* client) { req->accept_socket = INVALID_SOCKET; if (!(server->flags & UV_HANDLE_CLOSING)) { - uv_tcp_queue_accept(server, req); + /* Check if we're in a middle of changing the number of pending accepts. */ + if (!(server->flags & UV_HANDLE_TCP_ACCEPT_STATE_CHANGING)) { + uv_tcp_queue_accept(server, req); + } else { + /* We better be switching to a single pending accept. */ + assert(server->flags & UV_HANDLE_TCP_SINGLE_ACCEPT); + + server->processed_accepts++; + + if (server->processed_accepts >= uv_simultaneous_server_accepts) { + server->processed_accepts = 0; + /* + * All previously queued accept requests are now processed. + * We now switch to queueing just a single accept. + */ + uv_tcp_queue_accept(server, &server->accept_reqs[0]); + server->flags &= ~UV_HANDLE_TCP_ACCEPT_STATE_CHANGING; + server->flags |= UV_HANDLE_TCP_SINGLE_ACCEPT; + } + } } active_tcp_streams++; @@ -1069,3 +1092,37 @@ int uv_tcp_duplicate_socket(uv_tcp_t* handle, int pid, return 0; } + + +int uv_tcp_multiple_simultaneous_accepts(uv_tcp_t* handle, int enable) { + if (handle->flags & UV_HANDLE_CONNECTION) { + uv__set_artificial_error(handle->loop, UV_EINVAL); + return -1; + } + + /* Check if we're already in the desired mode. */ + if ((enable && !(handle->flags & UV_HANDLE_TCP_SINGLE_ACCEPT)) || + (!enable && handle->flags & UV_HANDLE_TCP_SINGLE_ACCEPT)) { + return 0; + } + + /* Don't allow switching from single pending accept to many. */ + if (enable) { + uv__set_artificial_error(handle->loop, UV_ENOTSUP); + return -1; + } + + /* Check if we're in a middle of changing the number of pending accepts. */ + if (handle->flags & UV_HANDLE_TCP_ACCEPT_STATE_CHANGING) { + return 0; + } + + handle->flags |= UV_HANDLE_TCP_SINGLE_ACCEPT; + + /* Flip the changing flag if we have already queueed multiple accepts. */ + if (handle->flags & UV_HANDLE_LISTENING) { + handle->flags |= UV_HANDLE_TCP_ACCEPT_STATE_CHANGING; + } + + return 0; +} \ No newline at end of file