Cover UDP callback deletion, connection close and path replacement
This commit is contained in:
1 parent
6d6ec7e725
commit
519a2ee242
1 file changed
+68
-2
@@ -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++) {
|
||||
|
||||
Reference in new issue
Block a user