From e03cd0b7ebe196cc5c373daa5981ddaddf572bdc Mon Sep 17 00:00:00 2001 From: John Fremlin Date: Sun, 17 Dec 2017 22:43:00 -0500 Subject: [PATCH 1/2] Immediately stop trying to accept more connections if listener disabled This is a refined version of the logic previously in #578 The rationale is that the consumer of sockets may wish to temporarily delay accepting for some reason (e.g. being out of file-descriptors). The kernel will then queue them up. The kernel queue is bounded and programs like NodeJS will actually try to quickly accept and then close (as the current behaviour before this PR). However, it seems that libevent should allow the user to choose whether to accept and respond correctly if the listener is disabled. --- listener.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/listener.c b/listener.c index 942fa451..2835df17 100644 --- a/listener.c +++ b/listener.c @@ -424,6 +424,11 @@ listener_read_cb(evutil_socket_t fd, short what, void *p) return; } --lev->refcnt; + if (!lev->enabled) { + /* the callback could have disabled the listener */ + UNLOCK(lev); + return; + } } err = evutil_socket_geterror(fd); if (EVUTIL_ERR_ACCEPT_RETRIABLE(err)) { From cb6995cf786855c082db542dd00b5fafa2ee4b4a Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Thu, 4 Jan 2018 19:26:50 +0300 Subject: [PATCH 2/2] test/listener: cover immediate-close logic --- test/regress_listener.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/test/regress_listener.c b/test/regress_listener.c index 32f23074..070e5e34 100644 --- a/test/regress_listener.c +++ b/test/regress_listener.c @@ -230,6 +230,44 @@ end: ; } +static void +regress_listener_immediate_close(void *arg) +{ + struct basic_test_data *data = arg; + struct event_base *base = data->base; + struct evconnlistener *listener = NULL; + struct sockaddr_in sin; + struct sockaddr_storage ss; + ev_socklen_t slen = sizeof(ss); + int count = 1; + unsigned int flags = LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE; + int fd1 = -1, fd2 = -1; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(0x7f000001); /* 127.0.0.1 */ + sin.sin_port = 0; /* "You pick!" */ + + /* Start a listener with a bogus socket. */ + listener = evconnlistener_new_bind(base, acceptcb, &count, + flags, -1, (struct sockaddr *)&sin, sizeof(sin)); + tt_assert(listener); + + tt_assert(getsockname(evconnlistener_get_fd(listener), + (struct sockaddr*)&ss, &slen) == 0); + + evutil_socket_connect_(&fd1, (struct sockaddr*)&ss, slen); + evutil_socket_connect_(&fd2, (struct sockaddr*)&ss, slen); + + event_base_dispatch(base); + + tt_int_op(count, ==, 0); + +end: + if (listener) + evconnlistener_free(listener); +} + #ifdef EVENT__HAVE_SETRLIMIT static void regress_listener_error_unlock(void *arg) @@ -290,6 +328,9 @@ struct testcase_t listener_testcases[] = { { "close_accepted_fd", regress_listener_close_accepted_fd, TT_FORK|TT_NEED_BASE, &basic_setup, NULL, }, + { "immediate_close", regress_listener_immediate_close, + TT_FORK|TT_NEED_BASE, &basic_setup, NULL, }, + END_OF_TESTCASES, };