From fdaa20977d3078c676b85534d9489f574b2069f8 Mon Sep 17 00:00:00 2001 From: evgeny Date: Mon, 12 Oct 2026 01:22:16 +0200 Subject: [PATCH] Recheck expired down lifetime when the last NCD owner releases it --- src/transport_layer/node_conn_direct.c | 4 +++- tests/test_etcp_server_lifecycle.c | 28 +++++++++++++++++++++++--- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/transport_layer/node_conn_direct.c b/src/transport_layer/node_conn_direct.c index 46a33110..128fc247 100644 --- a/src/transport_layer/node_conn_direct.c +++ b/src/transport_layer/node_conn_direct.c @@ -1155,7 +1155,9 @@ static void ncd_handle_close(struct NODE_CONN_DIRECT* h, int force) { if (!conn->links_up) { DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] last handle released while DOWN, retaining until ETCP timeout node=%016llx", (unsigned long long)entry->node_id); - ncd_finish(entry, 0); return; + ncd_finish(entry, 0); + if (etcp_conn_schedule_down_close(conn) < 0) etcp_connection_close(conn); + return; } if (entry->connect_timer) { uasync_cancel_timeout(entry->ua, entry->connect_timer); entry->connect_timer = NULL; } conn->fin_wait = 1; conn->fin_wait_clear_cb = ncd_fin_wait_cancelled; conn->fin_wait_clear_arg = entry; diff --git a/tests/test_etcp_server_lifecycle.c b/tests/test_etcp_server_lifecycle.c index 9dac3a89..554632c9 100644 --- a/tests/test_etcp_server_lifecycle.c +++ b/tests/test_etcp_server_lifecycle.c @@ -115,7 +115,7 @@ static void check_reconnect(void) { teardown(); DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "PASS multiple links, reconnect cancellation and fresh DOWN deadline"); } -/* Локальный handle не препятствует удалению; release внутри DOWN сохраняет таймаут. */ +/* Локальные handles защищают DOWN; release последнего учитывает уже прошедший срок. */ static void check_owner(int release) { setup(); struct ETCP_CONN* conn = ready_conn(0); struct NODE_CONN_DIRECT* handle = NULL; CHECK(node_conn_direct_open(&inst, 42, owner_cb, NULL, &handle, NULL) == NCD_REUSED); @@ -123,11 +123,32 @@ static void check_owner(int release) { conn->links->link_status = 0; etcp_on_link_down(conn, conn->links); CHECK(down == 1 && !deleted && conn->down_timer && !conn->close_requested); test_clock_run_for(inst.ua, ETCP_DOWN_TIMEOUT_TB + 10000); - CHECK(deleted == 1 && closed == (release ? 0 : 1) && !inst.connections->count); - if (!release) node_conn_direct_close(handle); + if (!release) { + CHECK(!deleted && !closed && !conn->down_timer && conn->down_deadline_tb && inst.connections->count == 1); + CHECK(node_conn_direct_handle_count(conn) == 1); + struct NODE_CONN_DIRECT* second = NULL; + CHECK(node_conn_direct_open(&inst, 42, owner_cb, NULL, &second, NULL) == NCD_REUSED); + CHECK(node_conn_direct_handle_count(conn) == 2); + node_conn_direct_close(handle); CHECK(node_conn_direct_handle_count(conn) == 1 && !conn->close_requested); + node_conn_direct_close(second); test_clock_run_for(inst.ua, 10); + } + CHECK(deleted == 1 && !closed && !inst.connections->count); teardown(); DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "PASS DOWN timeout with local owner release_on_down=%d", release); } +/* Закрытие handle до deadline не начинает новые 30 секунд ожидания. */ +static void check_release_before_deadline(void) { + setup(); struct ETCP_CONN* conn = ready_conn(0); struct NODE_CONN_DIRECT* handle = NULL; + CHECK(node_conn_direct_open(&inst, 42, NULL, NULL, &handle, NULL) == NCD_REUSED); + conn->links->link_status = 0; etcp_on_link_down(conn, conn->links); + uint64_t deadline = conn->down_deadline_tb; + test_clock_run_for(inst.ua, 100000); node_conn_direct_close(handle); + CHECK(!deleted && node_conn_direct_handle_count(conn) == 0 && conn->down_deadline_tb == deadline); + test_clock_run_for(inst.ua, 100000); CHECK(!deleted); + test_clock_run_for(inst.ua, 110000); CHECK(deleted == 1 && !inst.connections->count); + teardown(); DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "PASS last handle release preserves original DOWN deadline"); +} + /* RX продлевает watchdog, затем молчание даёт DOWN и ещё 30 секунд до DELETE. */ static void check_silent_peer(int tcp) { setup(); struct ETCP_CONN* conn = ready_conn(tcp); struct ETCP_LINK* link = conn->links; @@ -214,6 +235,7 @@ int main(void) { check_reconnect(); CHECK(u_get_allocated_count() == baseline); check_owner(0); CHECK(u_get_allocated_count() == baseline); check_owner(1); CHECK(u_get_allocated_count() == baseline); + check_release_before_deadline(); CHECK(u_get_allocated_count() == baseline); check_silent_peer(0); CHECK(u_get_allocated_count() == baseline); check_silent_peer(1); CHECK(u_get_allocated_count() == baseline); check_sleep(); CHECK(u_get_allocated_count() == baseline);