From 519a2ee2426d10a494cbdc10bb0707115f96bccb Mon Sep 17 00:00:00 2001 From: evgeny Date: Fri, 23 Oct 2026 23:35:55 +0200 Subject: [PATCH] Cover UDP callback deletion, connection close and path replacement --- tests/test_etcp_init_liveness.c | 70 ++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 2 deletions(-) diff --git a/tests/test_etcp_init_liveness.c b/tests/test_etcp_init_liveness.c index 651e81fa..9c28544a 100644 --- a/tests/test_etcp_init_liveness.c +++ b/tests/test_etcp_init_liveness.c @@ -20,7 +20,7 @@ struct endpoint { }; static struct endpoint ep[2]; static struct UASYNC* ua; -static unsigned close_action, close_fired; +static unsigned close_action, close_fired, late_handshakes; static const char* pubkeys[] = { "b3193173def895bd0fcea6f86af077c7d77216f10395275f627ac18242ec0f01", "ce8871f07fa056c636d297115f231b08c29cdf94e0d440fce83a07c34416d36a" @@ -42,6 +42,10 @@ static ssize_t send_observer(socket_t fd, const void* bytes, size_t len, const s decoded = sc_decrypt(©, bytes, len - SC_PUBKEY_ENC_SIZE, plain, &size) == SC_OK; } CHECK(decoded && size >= 4); + if (link->etcp->close_requested && plain[3] >= ETCP_INIT_REQUEST && plain[3] <= ETCP_INIT_RESPONSE_NOINIT) { + late_handshakes++; + DEBUG_ERROR(DEBUG_CATEGORY_DEBUG, "Handshake after close request: code=%u path=%u", plain[3], link->local_link_id); + } if ((plain[3] == ETCP_INIT_REQUEST || plain[3] == ETCP_INIT_REQUEST_NOINIT) && !(plain[2] & 1)) e->init_zero++; if ((plain[3] == ETCP_INIT_RESPONSE || plain[3] == ETCP_INIT_RESPONSE_NOINIT) && (plain[2] & 1)) e->response_one++; if (e->block_id && (e->block_id == 256 || e->block_id == link->local_link_id)) return len; @@ -85,7 +89,7 @@ static void run_for(unsigned tb) { test_clock_run_for(ua, tb); } /* Только ядро и одна явно выбранная UDP-пара: BGP/NCD не создают обходных путей. */ static void setup(unsigned swapped, unsigned count) { - memset(ep, 0, sizeof(ep)); close_action = close_fired = 0; + memset(ep, 0, sizeof(ep)); close_action = close_fired = late_handshakes = 0; ua = uasync_create(); CHECK(ua); for (unsigned side = 0; side < 2; side++) { unsigned key = side ^ swapped; @@ -359,6 +363,66 @@ static void delete_in_callback(unsigned keepalive) { teardown(); } +struct transition_case { + struct ETCP_CONN *retained; + struct ETCP_LINK *replacement; + unsigned stage, action, fired; +}; +static void close_transition(struct ETCP_CONN *conn, struct ETCP_LINK *link, int old_state, int old_status, void *arg) { + struct transition_case *test = arg; + if (test->fired) return; + int selected = test->stage == 2 ? old_status && !link->link_status : + old_state == LINK_STATE_CONNECTED && link->link_state == (test->stage ? LINK_STATE_TRY_RECONNECT : LINK_STATE_HANDSHAKE); + if (!selected) return; + test->fired++; + DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "UDP transition callback stage=%u action=%u path=%u active_users=%u", + test->stage, test->action, link->local_link_id, link->active_users); + CHECK(link->active_users > 0 && conn->callbacks_running > 0); + if (test->action == 1) { etcp_connection_close(conn); return; } + struct ETCP_SOCKET *socket = link->conn; + struct sockaddr_storage remote = link->remote_addr; + etcp_link_close(link); + CHECK(link->closing && link->active_users > 0); /* Память заимствована на весь callback. */ + if (test->action == 2) { + test->replacement = etcp_link_new(conn, socket, &remote, 0); CHECK(test->replacement); + CHECK(test->replacement != link); /* Удалённый объект не переиспользуется внутри активного стека. */ + } +} +static void transition_callbacks(unsigned swapped, unsigned stage, unsigned action) { + setup(swapped, 2); + unsigned side = stage == 2 ? 1 : 0; + struct transition_case test = {.retained = ep[side].conn, .stage = stage, .action = action}; + CHECK(etcp_conn_ref_take(test.retained) == 0); + etcp_add_link_status_cbk(ep[side].inst, close_transition, &test); + if (stage == 2) { + ep[0].block_id = first_link(0)->local_link_id; + uint64_t end = get_time_tb() + 15000; + while (!test.fired && get_time_tb() < end) test_clock_poll(ua, 10); + } else if (stage == 1) etcp_link_enter_reinit(first_link(0)); + else etcp_link_enter_init(first_link(0)); + CHECK(test.fired == 1 && !late_handshakes); + if (test.replacement) { + CHECK(test.replacement->link_state == LINK_STATE_HANDSHAKE && !test.replacement->recv_keepalive); + CHECK(!test.replacement->keepalive_timer && test.replacement->init_timer && !test.replacement->active_users); + } + run_for(100); + CHECK(!late_handshakes && !test.retained->callbacks_running); + if (action == 1) CHECK(test.retained->state == 2 && !test.retained->links && !test.retained->normalizer); + else { + CHECK(test.retained->links_up && !test.retained->close_requested && !ep[side].down && !ep[side].reinit); + ep[0].block_id = ep[1].block_id = 0; + struct ll_entry *entry = ll_alloc_lldgram(2); CHECK(entry); + entry->dgram[0] = 253; entry->dgram[1] = 17; entry->len = 2; + CHECK(etcp_send(ep[0].conn, entry) == 0); run_for(10000); CHECK(ep[1].received == 1); + for (struct ETCP_LINK *link = test.retained->links; link; link = link->next) CHECK(!link->active_users); + } + etcp_remove_link_status_cbk(ep[side].inst, close_transition, &test); + etcp_conn_ref_free(test.retained); + DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "PASS UDP transition swapped=%u stage=%u action=%u: no stale use/handshake, alternate delivery", + swapped, stage, action); + teardown(); +} + /* При отказе показать конкретные живые блоки вместо одного несовпавшего счётчика. */ static void check_memory(size_t baseline) { if (u_get_allocated_count() == baseline) return; @@ -377,6 +441,8 @@ int main(void) { debug_set_category_level(DEBUG_CATEGORY_DEBUG, DEBUG_LEVEL_DEBUG); } size_t baseline = u_get_allocated_count(); + for (unsigned swapped = 0; swapped < 2; swapped++) for (unsigned stage = 0; stage < 3; stage++) + for (unsigned action = 0; action < 3; action++) { transition_callbacks(swapped, stage, action); check_memory(baseline); } for (unsigned established = 0; established < 2; established++) { request_boundaries(established); check_memory(baseline); } response_boundaries(); check_memory(baseline); for (unsigned swapped = 0; swapped < 2; swapped++) for (unsigned order = 0; order < 2; order++) {