From 0ef0553598969d91a3fdc35a71e611e76eb98ea7 Mon Sep 17 00:00:00 2001 From: evgeny Date: Thu, 22 Oct 2026 22:45:58 +0200 Subject: [PATCH] Coalesce useful TX wakeups and defer failure teardown safely --- src/transport_layer/etcp.c | 13 ++++++++++++- tests/test_etcp_windows.c | 1 + 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/transport_layer/etcp.c b/src/transport_layer/etcp.c index 5b68e4bc..0499953c 100644 --- a/src/transport_layer/etcp.c +++ b/src/transport_layer/etcp.c @@ -50,17 +50,28 @@ static void send_ack_req_cb(struct ll_queue* q, void* arg); static void etcp_conn_process_send_queue(struct ETCP_CONN* etcp); static void etcp_send_timer_cb(void* arg); static void etcp_connection_free_deferred(void* arg); +static void etcp_connection_close_deferred(void* arg); void etcp_fire_conn_status(struct ETCP_CONN* etcp, int status); struct ETCP_DGRAM* etcp_request_pkt(struct ETCP_CONN* etcp); /* Очереди и сетевые события только планируют проход: синхронные callbacks не входят в отправитель повторно. */ static void etcp_schedule_send(struct ETCP_CONN* conn, int delay_tb) { if (!conn || conn->close_requested || conn->state == 2 || conn->tx_timer) return; + if (!conn->initialized || conn->session_required || conn->reinit_pending) return; if (conn->tx_running && delay_tb == 1) return; + int work = conn->input_send_q->count || conn->input_queue->count || conn->ack_q->count; + for (struct ETCP_LINK* link = conn->links; !work && link; link = link->next) work = link->burst_active; + if (!work) return; conn->tx_timer = uasync_set_timeout(conn->instance->ua, delay_tb, conn, etcp_send_timer_cb, "etcp_tx"); if (!conn->tx_timer) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[%s] cannot schedule sender delay=%d", conn->log_name, delay_tb); - etcp_connection_close(conn); + // Вызывающий может ещё собирать линк/менять очередь: ресурсы освобождаются после выхода из его стека. + conn->close_requested = 1; + conn->close_token = uasync_call_soon(conn->instance->ua, conn, etcp_connection_close_deferred); + if (!conn->close_token) { + DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[%s] cannot defer mandatory close after TX timer failure", conn->log_name); + abort(); + } } } diff --git a/tests/test_etcp_windows.c b/tests/test_etcp_windows.c index 5434287b..331531cb 100644 --- a/tests/test_etcp_windows.c +++ b/tests/test_etcp_windows.c @@ -247,6 +247,7 @@ static void pacing_reset_close(void) { enqueue(100); CHECK(conn->tx_timer); etcp_conn_reinit_id(conn, "queued sender reset", conn->reset_id); CHECK(!conn->tx_timer && !conn->unacked_bytes && conn->reinit_pending); + etcp_conn_ready(conn); enqueue(100); CHECK(conn->tx_timer); CHECK(etcp_conn_ref_take(conn) == 0); etcp_connection_close(conn); CHECK(conn->state == 2 && !conn->tx_timer);