diff --git a/lib/isc/netmgr/http.c b/lib/isc/netmgr/http.c index e604ee7d63..614bada711 100644 --- a/lib/isc/netmgr/http.c +++ b/lib/isc/netmgr/http.c @@ -2527,6 +2527,9 @@ isc_nm_listenhttp(isc_nm_t *mgr, isc_sockaddr_t *iface, int backlog, sock->tid = 0; sock->fd = (uv_os_sock_t)-1; + isc__nmsocket_barrier_init(sock); + atomic_init(&sock->rchildren, sock->nchildren); + atomic_store(&sock->listening, true); *sockp = sock; return (ISC_R_SUCCESS); @@ -2694,39 +2697,7 @@ isc__nm_http_stoplistening(isc_nmsocket_t *sock) { REQUIRE(VALID_NMSOCK(sock)); REQUIRE(sock->type == isc_nm_httplistener); - if (!atomic_compare_exchange_strong(&sock->closing, &(bool){ false }, - true)) { - UNREACHABLE(); - } - - if (!isc__nm_in_netthread()) { - isc__netievent_httpstop_t *ievent = - isc__nm_get_netievent_httpstop(sock->mgr, sock); - isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid], - (isc__netievent_t *)ievent); - } else { - REQUIRE(isc_nm_tid() == sock->tid); - isc__netievent_httpstop_t ievent = { .sock = sock }; - isc__nm_async_httpstop(NULL, (isc__netievent_t *)&ievent); - } -} - -void -isc__nm_async_httpstop(isc__networker_t *worker, isc__netievent_t *ev0) { - isc__netievent_httpstop_t *ievent = (isc__netievent_httpstop_t *)ev0; - isc_nmsocket_t *sock = ievent->sock; - - UNUSED(worker); - - REQUIRE(VALID_NMSOCK(sock)); - - atomic_store(&sock->listening, false); - atomic_store(&sock->closing, false); - atomic_store(&sock->closed, true); - if (sock->outer != NULL) { - isc_nm_stoplistening(sock->outer); - isc_nmsocket_close(&sock->outer); - } + isc__nmsocket_stop(sock); } static void diff --git a/lib/isc/netmgr/netmgr-int.h b/lib/isc/netmgr/netmgr-int.h index 2a51fe35ca..d5269753ac 100644 --- a/lib/isc/netmgr/netmgr-int.h +++ b/lib/isc/netmgr/netmgr-int.h @@ -333,6 +333,7 @@ typedef enum isc__netievent_type { netievent_privilegedtask, netievent_settlsctx, + netievent_sockstop, /* for multilayer sockets */ /* * event type values higher than this will be treated @@ -349,7 +350,6 @@ typedef enum isc__netievent_type { netievent_tcpdnsstop, netievent_tlsdnslisten, netievent_tlsdnsstop, - netievent_httpstop, netievent_resume, netievent_detach, @@ -1221,6 +1221,8 @@ struct isc_nmsocket { atomic_int_fast32_t active_child_connections; + isc_barrier_t barrier; + bool barrier_initialised; #ifdef NETMGR_TRACE void *backtrace[TRACE_SIZE]; int backtrace_size; @@ -1854,9 +1856,6 @@ isc__nm_http_verify_tls_peer_result_string(const isc_nmhandle_t *handle); void isc__nm_async_httpsend(isc__networker_t *worker, isc__netievent_t *ev0); -void -isc__nm_async_httpstop(isc__networker_t *worker, isc__netievent_t *ev0); - void isc__nm_async_httpclose(isc__networker_t *worker, isc__netievent_t *ev0); @@ -1923,6 +1922,9 @@ isc__nm_acquire_interlocked_force(isc_nm_t *mgr); * Actively wait for interlocked state. */ +void +isc__nm_async_sockstop(isc__networker_t *worker, isc__netievent_t *ev0); + void isc__nm_incstats(isc_nmsocket_t *sock, isc__nm_statid_t id); /*%< @@ -2015,6 +2017,27 @@ isc__nm_set_network_buffers(isc_nm_t *nm, uv_handle_t *handle); * Sets the pre-configured network buffers size on the handle. */ +void +isc__nmsocket_barrier_init(isc_nmsocket_t *listener); +/*%> + * Initialise the socket synchronisation barrier according to the + * number of children. + */ + +void +isc__nmsocket_stop(isc_nmsocket_t *listener); +/*%> + * Broadcast "stop" event for a listener socket across all workers and + * wait its processing completion - then, stop and close the underlying + * transport listener socket. + * + * The primitive is used in multi-layer transport listener sockets to + * implement shutdown properly: after the broadcasted events has been + * processed it is safe to destroy the shared data within the listener + * socket (including shutting down the underlying transport listener + * socket). + */ + /* * typedef all the netievent types */ @@ -2057,7 +2080,6 @@ NETIEVENT_SOCKET_QUOTA_TYPE(tlsdnsaccept); NETIEVENT_SOCKET_TYPE(tlsdnscycle); #ifdef HAVE_LIBNGHTTP2 -NETIEVENT_SOCKET_TYPE(httpstop); NETIEVENT_SOCKET_REQ_TYPE(httpsend); NETIEVENT_SOCKET_TYPE(httpclose); NETIEVENT_SOCKET_HTTP_EPS_TYPE(httpendpoints); @@ -2090,6 +2112,7 @@ NETIEVENT_TASK_TYPE(task); NETIEVENT_TASK_TYPE(privilegedtask); NETIEVENT_SOCKET_TLSCTX_TYPE(settlsctx); +NETIEVENT_SOCKET_TYPE(sockstop); /* Now declared the helper functions */ @@ -2131,7 +2154,6 @@ NETIEVENT_SOCKET_QUOTA_DECL(tlsdnsaccept); NETIEVENT_SOCKET_DECL(tlsdnscycle); #ifdef HAVE_LIBNGHTTP2 -NETIEVENT_SOCKET_DECL(httpstop); NETIEVENT_SOCKET_REQ_DECL(httpsend); NETIEVENT_SOCKET_DECL(httpclose); NETIEVENT_SOCKET_HTTP_EPS_DECL(httpendpoints); @@ -2163,6 +2185,7 @@ NETIEVENT_TASK_DECL(task); NETIEVENT_TASK_DECL(privilegedtask); NETIEVENT_SOCKET_TLSCTX_DECL(settlsctx); +NETIEVENT_SOCKET_DECL(sockstop); void isc__nm_udp_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result); diff --git a/lib/isc/netmgr/netmgr.c b/lib/isc/netmgr/netmgr.c index 57d5390c68..f2be97f6a3 100644 --- a/lib/isc/netmgr/netmgr.c +++ b/lib/isc/netmgr/netmgr.c @@ -964,12 +964,12 @@ process_netievent(isc__networker_t *worker, isc__netievent_t *ievent) { NETIEVENT_CASE(tlsdobio); NETIEVENT_CASE(tlscancel); - NETIEVENT_CASE(httpstop); NETIEVENT_CASE(httpsend); NETIEVENT_CASE(httpclose); NETIEVENT_CASE(httpendpoints); #endif NETIEVENT_CASE(settlsctx); + NETIEVENT_CASE(sockstop); NETIEVENT_CASE(connectcb); NETIEVENT_CASE(readcb); @@ -1082,7 +1082,6 @@ NETIEVENT_SOCKET_DEF(tlsdnscycle); NETIEVENT_SOCKET_DEF(tlsdnsshutdown); #ifdef HAVE_LIBNGHTTP2 -NETIEVENT_SOCKET_DEF(httpstop); NETIEVENT_SOCKET_REQ_DEF(httpsend); NETIEVENT_SOCKET_DEF(httpclose); NETIEVENT_SOCKET_HTTP_EPS_DEF(httpendpoints); @@ -1113,6 +1112,7 @@ NETIEVENT_TASK_DEF(task); NETIEVENT_TASK_DEF(privilegedtask); NETIEVENT_SOCKET_TLSCTX_DEF(settlsctx); +NETIEVENT_SOCKET_DEF(sockstop); void isc__nm_maybe_enqueue_ievent(isc__networker_t *worker, @@ -1302,6 +1302,11 @@ nmsocket_cleanup(isc_nmsocket_t *sock, bool dofree FLARG) { #endif INSIST(ISC_LIST_EMPTY(sock->tls.sendreqs)); + + if (sock->barrier_initialised) { + isc_barrier_destroy(&sock->barrier); + } + #ifdef NETMGR_TRACE LOCK(&sock->mgr->lock); ISC_LIST_UNLINK(sock->mgr->active_sockets, sock, active_link); @@ -2727,6 +2732,76 @@ isc_nm_stoplistening(isc_nmsocket_t *sock) { } } +void +isc__nmsocket_stop(isc_nmsocket_t *listener) { + isc__netievent_sockstop_t ievent = { .sock = listener }; + + REQUIRE(VALID_NMSOCK(listener)); + + if (!atomic_compare_exchange_strong(&listener->closing, + &(bool){ false }, true)) { + UNREACHABLE(); + } + + for (size_t i = 0; i < listener->nchildren; i++) { + isc__networker_t *worker = &listener->mgr->workers[i]; + isc__netievent_sockstop_t *ev; + + if (isc__nm_in_netthread() && i == (size_t)isc_nm_tid()) { + continue; + } + + ev = isc__nm_get_netievent_sockstop(listener->mgr, listener); + isc__nm_enqueue_ievent(worker, (isc__netievent_t *)ev); + } + + if (isc__nm_in_netthread()) { + isc__nm_async_sockstop(&listener->mgr->workers[0], + (isc__netievent_t *)&ievent); + } +} + +void +isc__nmsocket_barrier_init(isc_nmsocket_t *listener) { + REQUIRE(listener->nchildren > 0); + isc_barrier_init(&listener->barrier, listener->nchildren); + listener->barrier_initialised = true; +} + +void +isc__nm_async_sockstop(isc__networker_t *worker, isc__netievent_t *ev0) { + isc__netievent_sockstop_t *ievent = (isc__netievent_sockstop_t *)ev0; + isc_nmsocket_t *listener = ievent->sock; + UNUSED(worker); + + (void)atomic_fetch_sub(&listener->rchildren, 1); + isc_barrier_wait(&listener->barrier); + + if (listener->tid != isc_nm_tid()) { + return; + } + + if (!atomic_compare_exchange_strong(&listener->listening, + &(bool){ true }, false)) + { + UNREACHABLE(); + } + + INSIST(atomic_load(&listener->rchildren) == 0); + + listener->accept_cb = NULL; + listener->accept_cbarg = NULL; + listener->recv_cb = NULL; + listener->recv_cbarg = NULL; + + if (listener->outer != NULL) { + isc_nm_stoplistening(listener->outer); + isc__nmsocket_detach(&listener->outer); + } + + atomic_store(&listener->closed, true); +} + void isc__nm_connectcb(isc_nmsocket_t *sock, isc__nm_uvreq_t *uvreq, isc_result_t eresult, bool async) { diff --git a/lib/isc/netmgr/tlsstream.c b/lib/isc/netmgr/tlsstream.c index 9cb889f8fe..b09dd91838 100644 --- a/lib/isc/netmgr/tlsstream.c +++ b/lib/isc/netmgr/tlsstream.c @@ -342,9 +342,13 @@ tls_try_handshake(isc_nmsocket_t *sock, isc_result_t *presult) { isc__nmsocket_log_tls_session_reuse(sock, sock->tlsstream.tls); tlshandle = isc__nmhandle_get(sock, &sock->peer, &sock->iface); if (sock->tlsstream.server) { - result = sock->listener->accept_cb( - tlshandle, result, - sock->listener->accept_cbarg); + if (isc__nmsocket_closing(sock->listener)) { + result = ISC_R_CANCELED; + } else { + result = sock->listener->accept_cb( + tlshandle, result, + sock->listener->accept_cbarg); + } } else { tls_call_connect_cb(sock, tlshandle, result); } @@ -674,6 +678,13 @@ tlslisten_acceptcb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) { REQUIRE(VALID_NMSOCK(tlslistensock)); REQUIRE(tlslistensock->type == isc_nm_tlslistener); + if (isc__nmsocket_closing(handle->sock) || + isc__nmsocket_closing(tlslistensock) || + !atomic_load(&tlslistensock->listening)) + { + return (ISC_R_CANCELED); + } + /* * We need to create a 'wrapper' tlssocket for this connection. */ @@ -760,6 +771,10 @@ isc_nm_listentls(isc_nm_t *mgr, isc_sockaddr_t *iface, isc__nmsocket_attach(tlssock, &tlssock->outer->tlsstream.tlslistener); isc__nmsocket_detach(&tsock); INSIST(result != ISC_R_UNSET); + tlssock->nchildren = tlssock->outer->nchildren; + + isc__nmsocket_barrier_init(tlssock); + atomic_init(&tlssock->rchildren, tlssock->nchildren); if (result == ISC_R_SUCCESS) { atomic_store(&tlssock->listening, true); @@ -954,23 +969,7 @@ isc__nm_tls_stoplistening(isc_nmsocket_t *sock) { REQUIRE(VALID_NMSOCK(sock)); REQUIRE(sock->type == isc_nm_tlslistener); - if (!atomic_compare_exchange_strong(&sock->closing, &(bool){ false }, - true)) { - UNREACHABLE(); - } - - atomic_store(&sock->listening, false); - atomic_store(&sock->closed, true); - sock->recv_cb = NULL; - sock->recv_cbarg = NULL; - - INSIST(sock->tlsstream.tls == NULL); - INSIST(sock->tlsstream.ctx == NULL); - - if (sock->outer != NULL) { - isc_nm_stoplistening(sock->outer); - isc__nmsocket_detach(&sock->outer); - } + isc__nmsocket_stop(sock); } static void