diff --git a/src/connection.h b/src/connection.h index 60ed4572d30..6ebd3936aef 100644 --- a/src/connection.h +++ b/src/connection.h @@ -404,8 +404,8 @@ static inline int connIsClosing(connection *conn) { return conn->type->is_closing(conn); } -/* Shared is_closing implementation for socket-based connections. */ -int connSocketIsClosing(connection *conn); +/* Shared is_closing implementation for TCP socket-based connections. */ +int connTcpSocketIsClosing(connection *conn); /* Associate a private data pointer with the connection */ static inline void connSetPrivateData(connection *conn, void *data) { diff --git a/src/server.c b/src/server.c index 0d592cdb5aa..106cf07bd42 100644 --- a/src/server.c +++ b/src/server.c @@ -1228,6 +1228,9 @@ void getExpensiveClientsInfo(size_t *in_usage, size_t *out_usage) { static bool clientsCronTcpIsClosing(client *c) { if (!c->conn) return false; + /* If the fd is still watched by the event loop, it detects the close and frees the client itself. */ + if (connHasReadHandler(c->conn) || connHasWriteHandler(c->conn)) return false; + if (!connIsClosing(c->conn)) return false; if (server.verbosity <= LL_VERBOSE) { @@ -1291,10 +1294,10 @@ static void clientsCron(int clients_this_cycle) { * The protocol is that they return non-zero if the client was * terminated. */ if (clientsCronHandleTimeout(c, now)) continue; + if (clientsCronTcpIsClosing(c)) continue; if (clientsCronResizeQueryBuffer(c)) continue; if (clientsCronResizeOutputBuffer(c, now)) continue; if (clientsCronTrackExpensiveClients(c, curr_peak_mem_usage_slot)) continue; - if (clientsCronTcpIsClosing(c)) continue; /* Iterating all the clients in getMemoryOverheadData() is too slow and * in turn would make the INFO command too slow. So we perform this diff --git a/src/socket.c b/src/socket.c index 55143e2d026..58cef717d04 100644 --- a/src/socket.c +++ b/src/socket.c @@ -422,17 +422,20 @@ static int connSocketGetType(void) { return CONN_TYPE_SOCKET; } -int connSocketIsClosing(connection *conn) { - if (aeGetFileEvents(server.el, conn->fd) != AE_NONE) return false; +int connTcpSocketIsClosing(connection *conn) { #if defined(__linux__) struct tcp_info info; socklen_t infolen = sizeof(info); - if (getsockopt(conn->fd, IPPROTO_TCP, TCP_INFO, &info, &infolen) != 0 || infolen < sizeof(info)) return false; // Cannot retrieve TCP info + if (getsockopt(conn->fd, IPPROTO_TCP, TCP_INFO, &info, &infolen) != 0 || + infolen < offsetof(struct tcp_info, tcpi_state) + sizeof(info.tcpi_state)) + return false; /* Cannot retrieve TCP info, or the state field was not returned. */ return (info.tcpi_state == TCP_CLOSE_WAIT || info.tcpi_state == TCP_CLOSE); #elif defined(__APPLE__) struct tcp_connection_info info; socklen_t infolen = sizeof(info); - if (getsockopt(conn->fd, IPPROTO_TCP, TCP_CONNECTION_INFO, &info, &infolen) != 0 || infolen < sizeof(info)) return false; // Cannot retrieve TCP info + if (getsockopt(conn->fd, IPPROTO_TCP, TCP_CONNECTION_INFO, &info, &infolen) != 0 || + infolen < offsetof(struct tcp_connection_info, tcpi_state) + sizeof(info.tcpi_state)) + return false; /* Cannot retrieve TCP info, or the state field was not returned. */ return (info.tcpi_state == TCPS_CLOSE_WAIT || info.tcpi_state == TCPS_CLOSED); #else /* Unsupported platform: zombie connection detection is not available. */ @@ -488,7 +491,7 @@ static ConnectionType CT_Socket = { /* Miscellaneous */ .connIntegrityChecked = NULL, - .is_closing = connSocketIsClosing, + .is_closing = connTcpSocketIsClosing, }; int connBlock(connection *conn) { diff --git a/src/tls.c b/src/tls.c index e4240250205..3167f513e26 100644 --- a/src/tls.c +++ b/src/tls.c @@ -2022,7 +2022,7 @@ static ConnectionType CT_TLS = { /* Miscellaneous */ .connIntegrityChecked = connTLSIsIntegrityChecked, - .is_closing = connSocketIsClosing, + .is_closing = connTcpSocketIsClosing, };