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)) { 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, };