From: Vsevolod Stakhov Date: Sat, 21 Mar 2026 13:36:18 +0000 (+0000) Subject: [Fix] Fix CPU busy-loop in fuzzy TCP client due to EV_WRITE not being cleared X-Git-Tag: 4.0.0~20 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=06dba4495b170ea4733609dabf60a52b122d4e77;p=thirdparty%2Frspamd.git [Fix] Fix CPU busy-loop in fuzzy TCP client due to EV_WRITE not being cleared When the TCP write queue drained, EV_WRITE was left armed on the socket watcher. Since a connected TCP socket is always writable, libev fired EV_WRITE continuously causing 100% CPU usage. The UDP path correctly switched to EV_READ only after writing. - Drop EV_WRITE when write queue empties in fuzzy_tcp_write_handler - Only arm EV_WRITE on connection established if data is actually queued - Remove redundant per-IO fuzzy_tcp_check_pending_timeouts call (timer callback already handles this), which amplified the spin with O(N) hash scans and GHashTable allocations on every iteration --- diff --git a/src/plugins/fuzzy_check.c b/src/plugins/fuzzy_check.c index 73ebf9cf2f..f1788cfd2c 100644 --- a/src/plugins/fuzzy_check.c +++ b/src/plugins/fuzzy_check.c @@ -1161,9 +1161,9 @@ fuzzy_tcp_io_handler(int fd, short what, gpointer ud) rspamd_upstream_ok(conn->server); - /* Now wait for both read and write events */ + /* Start reading; only arm EV_WRITE if there's queued data */ rspamd_ev_watcher_reschedule(conn->event_loop, &conn->ev, - EV_READ | EV_WRITE); + g_queue_is_empty(conn->write_queue) ? EV_READ : EV_READ | EV_WRITE); msg_debug("fuzzy_tcp: after reschedule - fd=%d, ev.io.fd=%d", conn->fd, (int) conn->ev.io.fd); @@ -1179,11 +1179,6 @@ fuzzy_tcp_io_handler(int fd, short what, gpointer ud) fuzzy_tcp_read_handler(conn); } - /* Check for timed out pending requests */ - if (conn->connected) { - fuzzy_tcp_check_pending_timeouts(conn->rule, conn->last_activity); - } - FUZZY_TCP_RELEASE(conn); } @@ -1267,8 +1262,8 @@ fuzzy_tcp_write_handler(struct fuzzy_tcp_connection *conn) } } - /* Queue is empty, no more data to write */ - /* Might want to disable EV_WRITE here if needed */ + /* Queue is empty, switch to read-only to avoid busy-looping on EV_WRITE */ + rspamd_ev_watcher_reschedule(conn->event_loop, &conn->ev, EV_READ); } /**