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);
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
netievent_privilegedtask,
netievent_settlsctx,
+ netievent_sockstop, /* for multilayer sockets */
/*
* event type values higher than this will be treated
netievent_tcpdnsstop,
netievent_tlsdnslisten,
netievent_tlsdnsstop,
- netievent_httpstop,
netievent_resume,
netievent_detach,
atomic_int_fast32_t active_child_connections;
+ isc_barrier_t barrier;
+ bool barrier_initialised;
#ifdef NETMGR_TRACE
void *backtrace[TRACE_SIZE];
int backtrace_size;
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);
* 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);
/*%<
* 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
*/
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);
NETIEVENT_TASK_TYPE(privilegedtask);
NETIEVENT_SOCKET_TLSCTX_TYPE(settlsctx);
+NETIEVENT_SOCKET_TYPE(sockstop);
/* Now declared the helper functions */
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);
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);
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);
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);
NETIEVENT_TASK_DEF(privilegedtask);
NETIEVENT_SOCKET_TLSCTX_DEF(settlsctx);
+NETIEVENT_SOCKET_DEF(sockstop);
void
isc__nm_maybe_enqueue_ievent(isc__networker_t *worker,
#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);
}
}
+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) {
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.
*/
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);
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