Browse Source

tcp_io: fix error_cb busy-spin on graceful EPOLLHUP; socks_proxy: close local socket on both FINs

- 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).
v2
evgeny 3 weeks ago
parent
commit
ce12f058d9
  1. 10
      lib/tcp_io.c
  2. 5
      opencode.json
  3. 10
      src/proxy/socks_proxy.c

10
lib/tcp_io.c

@ -417,7 +417,15 @@ static void error_cb(socket_t sock, void* arg) {
if (!tc || tc->sock == SOCKET_INVALID) return; if (!tc || tc->sock == SOCKET_INVALID) return;
int so_err = 0; socklen_t so_len = sizeof(so_err); 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 (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 пришёл не как // Грациозное закрытие peer (FIN, EPOLLHUP) при приостановленном чтении: FIN пришёл не как
// EPOLLIN (чтение отключено backpressure-ом), а как HUP. Возобновляем чтение, чтобы // EPOLLIN (чтение отключено backpressure-ом), а как HUP. Возобновляем чтение, чтобы
// read_cb дослил остаток и прочитал FIN (recv()==0 → fin_remote → отложенный on_fin). // read_cb дослил остаток и прочитал FIN (recv()==0 → fin_remote → отложенный on_fin).

5
opencode.json

@ -6,6 +6,11 @@
"type": "local", "type": "local",
"command": ["/home/user/.local/bin/uvx", "kwin-mcp"], "command": ["/home/user/.local/bin/uvx", "kwin-mcp"],
"enabled": true "enabled": true
},
"gdb-mcp": {
"type": "local",
"command": ["/home/user/.local/bin/uvx", "--with", "mcp<2", "gdb-mcp"],
"enabled": true
} }
} }
} }

10
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); DEBUG_DEBUG(DEBUG_CATEGORY_PROXY, "socks_proxy: local FIN deferred (tx_buf pending) sid=%08x", c->stream_id);
return; 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); DEBUG_DEBUG(DEBUG_CATEGORY_PROXY, "socks_proxy: local FIN → relay FIN sid=%08x", c->stream_id);
send_fin(c); send_fin(c);
if (c->fin_remote && !c->close_sent && !c->close_pending) send_close(c); if (c->fin_remote && !c->close_sent && !c->close_pending) send_close(c);

Loading…
Cancel
Save