From ce12f058d99352f6f20c5cb3d4cee21817b75d8e Mon Sep 17 00:00:00 2001 From: evgeny Date: Sat, 12 Sep 2026 16:08:14 +0300 Subject: [PATCH] tcp_io: fix error_cb busy-spin on graceful EPOLLHUP; socks_proxy: close local socket on both FINs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - error_cb (tcp_io): при graceful HUP (SO_ERROR==0) после уже прочитанного FIN сокет оставался в epoll и level-triggered EPOLLHUP крутил цикл на 100% CPU. Теперь сокет закрывается через tcp_conn_push_close. - socks_proxy on_fin_cb: при обоих обменянных FIN локальный сокет закрывается (send_close + push_close), симметрично exit-стороне (tcp_proxy_server). --- lib/tcp_io.c | 10 +++++++++- opencode.json | 5 +++++ src/proxy/socks_proxy.c | 10 +++++++++- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/lib/tcp_io.c b/lib/tcp_io.c index 025847b4..57696632 100644 --- a/lib/tcp_io.c +++ b/lib/tcp_io.c @@ -417,7 +417,15 @@ static void error_cb(socket_t sock, void* arg) { if (!tc || tc->sock == SOCKET_INVALID) return; int so_err = 0; socklen_t so_len = sizeof(so_err); if (getsockopt(tc->sock, SOL_SOCKET, SO_ERROR, &so_err, &so_len) == 0 && so_err == 0) { - if (tc->fin_remote) return; + // Peer полностью закрыл свой сокет (EPOLLHUP) после того, как FIN уже прочитан (fin_remote=1). + // EPOLLHUP — level-triggered и «always reported»: если оставить сокет в epoll, каждая итерация + // epoll_wait мгновенно вернёт HUP и error_cb зациклится на 100% CPU. Закрываем сокет. + if (tc->fin_remote) { + DEBUG_DEBUG(DEBUG_CATEGORY_SOCKET, "tcp_io: async HUP (graceful) fd=%d — peer fully closed after FIN, closing", + (int)tc->sock); + tcp_conn_push_close(tc); + return; + } // Грациозное закрытие peer (FIN, EPOLLHUP) при приостановленном чтении: FIN пришёл не как // EPOLLIN (чтение отключено backpressure-ом), а как HUP. Возобновляем чтение, чтобы // read_cb дослил остаток и прочитал FIN (recv()==0 → fin_remote → отложенный on_fin). diff --git a/opencode.json b/opencode.json index 76001349..f52377c5 100644 --- a/opencode.json +++ b/opencode.json @@ -6,6 +6,11 @@ "type": "local", "command": ["/home/user/.local/bin/uvx", "kwin-mcp"], "enabled": true + }, + "gdb-mcp": { + "type": "local", + "command": ["/home/user/.local/bin/uvx", "--with", "mcp<2", "gdb-mcp"], + "enabled": true } } } diff --git a/src/proxy/socks_proxy.c b/src/proxy/socks_proxy.c index 3b20e1b6..ee4a822b 100644 --- a/src/proxy/socks_proxy.c +++ b/src/proxy/socks_proxy.c @@ -525,7 +525,15 @@ static void on_fin_cb(struct tcp_conn* tc, void* arg) { DEBUG_DEBUG(DEBUG_CATEGORY_PROXY, "socks_proxy: local FIN deferred (tx_buf pending) sid=%08x", c->stream_id); return; } - if (!tc->fin_local && !tc->write_buf && !tc->write_queue->head) { + if (tc->fin_local) { + // оба FIN обменяны: мы уже shutdown-нули свой write (fin_local), peer прислал FIN. + // Закрываем локальный сокет, симметрично exit-стороне (tcp_proxy_server.c on_fin_cb). + DEBUG_DEBUG(DEBUG_CATEGORY_PROXY, "socks_proxy: both FINs done → close local socket sid=%08x", c->stream_id); + send_close(c); + tcp_conn_push_close(tc); + return; + } + if (!tc->write_buf && !tc->write_queue->head) { DEBUG_DEBUG(DEBUG_CATEGORY_PROXY, "socks_proxy: local FIN → relay FIN sid=%08x", c->stream_id); send_fin(c); if (c->fin_remote && !c->close_sent && !c->close_pending) send_close(c);