Browse Source

fix: reconnect test — clear_queue callback deadlock + conn_ok timing

- etcp_conn_reset: queue_resume_callback after clear_queue (input_queue,
  input_send_q, input_wait_ack) to prevent suspended callback deadlock
- is_connection_established: check conn->initialized (reliable across
  reinit) instead of link->initialized (never cleared on reinit)
- test: force etcp_conn_reinit after server recreation so conn_ok
  waits for fresh INIT handshake
- test: guard send_packets() with is_connection_established in reconnect
  phases to avoid sending data while conn is down
etcp-inflight-fix
Evgeny 4 months ago
parent
commit
9a3eb1ce80
  1. 5
      src/etcp.c
  2. 30
      tests/test_etcp_reconnect.c

5
src/etcp.c

@ -391,6 +391,11 @@ void etcp_conn_reset(struct ETCP_CONN* etcp) {
clear_queue(etcp->recv_q); clear_queue(etcp->recv_q);
clear_queue(etcp->ack_q); clear_queue(etcp->ack_q);
// clear_queue leaves callback_suspended=1, resume to prevent deadlock
queue_resume_callback(etcp->input_queue);
queue_resume_callback(etcp->input_send_q);
queue_resume_callback(etcp->input_wait_ack);
// В etcp_conn_reset(), после очистки очередей добавьте: // В etcp_conn_reset(), после очистки очередей добавьте:
struct ETCP_LINK* l = etcp->links; struct ETCP_LINK* l = etcp->links;
while (l) { while (l) {

30
tests/test_etcp_reconnect.c

@ -117,11 +117,7 @@ static int is_connection_established(struct UTUN_INSTANCE* inst) {
if (!inst) return 0; if (!inst) return 0;
struct ETCP_CONN* conn = inst->connections; struct ETCP_CONN* conn = inst->connections;
while (conn) { while (conn) {
struct ETCP_LINK* link = conn->links; if (conn->initialized) return 1;
while (link) {
if (link->initialized) return 1;
link = link->next;
}
conn = conn->next; conn = conn->next;
} }
return 0; return 0;
@ -203,9 +199,6 @@ static void monitor(void* arg) {
server_instance->running = 0; server_instance->running = 0;
utun_instance_destroy(server_instance); utun_instance_destroy(server_instance);
server_instance = NULL; server_instance = NULL;
printf("--- uasync after server destroy ---\n");
debug_set_level(DEBUG_LEVEL_INFO);
uasync_print_resources(ua, "AFTER_SERVER_DESTROY");
printf("Recreating server instance...\n"); printf("Recreating server instance...\n");
server_instance = utun_instance_create(ua, server_config_path); server_instance = utun_instance_create(ua, server_config_path);
if (!server_instance || utun_instance_init(server_instance) < 0) { if (!server_instance || utun_instance_init(server_instance) < 0) {
@ -214,6 +207,9 @@ static void monitor(void* arg) {
return; return;
} }
printf("Server recreated (node_id=%llx)\n", (unsigned long long)server_instance->node_id); printf("Server recreated (node_id=%llx)\n", (unsigned long long)server_instance->node_id);
// Force client to reinit — otherwise conn_ok stays true on old initialized=1
{ struct ETCP_CONN* c = client_instance->connections;
while (c) { etcp_conn_reinit(c); c = c->next; } }
restart_action_done = 1; restart_action_done = 1;
} }
drain_received(0); drain_received(0);
@ -234,6 +230,7 @@ static void monitor(void* arg) {
packets_received = 0; packets_received = 0;
drain_received(0); drain_received(0);
} }
if (is_connection_established(client_instance))
send_packets(); send_packets();
drain_received(1); drain_received(1);
if (packets_received >= TOTAL_PACKETS) { if (packets_received >= TOTAL_PACKETS) {
@ -268,9 +265,6 @@ static void monitor(void* arg) {
client_instance->running = 0; client_instance->running = 0;
utun_instance_destroy(client_instance); utun_instance_destroy(client_instance);
client_instance = NULL; client_instance = NULL;
printf("--- uasync after client destroy ---\n");
debug_set_level(DEBUG_LEVEL_INFO);
uasync_print_resources(ua, "AFTER_CLIENT_DESTROY");
printf("Recreating client instance...\n"); printf("Recreating client instance...\n");
client_instance = utun_instance_create(ua, client_config_path); client_instance = utun_instance_create(ua, client_config_path);
if (!client_instance || utun_instance_init(client_instance) < 0) { if (!client_instance || utun_instance_init(client_instance) < 0) {
@ -299,6 +293,7 @@ static void monitor(void* arg) {
packets_received = 0; packets_received = 0;
drain_received(0); drain_received(0);
} }
if (is_connection_established(client_instance))
send_packets(); send_packets();
drain_received(1); drain_received(1);
if (packets_received >= TOTAL_PACKETS) { if (packets_received >= TOTAL_PACKETS) {
@ -348,11 +343,6 @@ int main(void) {
} }
printf("Client ready (node_id=%llx)\n", (unsigned long long)client_instance->node_id); printf("Client ready (node_id=%llx)\n", (unsigned long long)client_instance->node_id);
// Enable diagnostic output for timer investigation
debug_set_level(DEBUG_LEVEL_INFO);
debug_set_category_level(DEBUG_CATEGORY_UASYNC, DEBUG_LEVEL_INFO);
debug_set_category_level(DEBUG_CATEGORY_TIMERS, DEBUG_LEVEL_INFO);
packet_timeout_id = uasync_set_timeout(ua, 300, NULL, monitor, "test_monitor"); packet_timeout_id = uasync_set_timeout(ua, 300, NULL, monitor, "test_monitor");
global_timeout_id = uasync_set_timeout(ua, TEST_TIMEOUT_MS * 10, NULL, test_timeout, "test_timeout"); global_timeout_id = uasync_set_timeout(ua, TEST_TIMEOUT_MS * 10, NULL, test_timeout, "test_timeout");
printf("Starting main loop\n"); printf("Starting main loop\n");
@ -372,16 +362,8 @@ int main(void) {
if (packet_timeout_id) uasync_cancel_timeout(ua, packet_timeout_id); if (packet_timeout_id) uasync_cancel_timeout(ua, packet_timeout_id);
if (global_timeout_id) uasync_cancel_timeout(ua, global_timeout_id); if (global_timeout_id) uasync_cancel_timeout(ua, global_timeout_id);
printf("--- uasync before final cleanup ---\n");
debug_set_level(DEBUG_LEVEL_INFO);
uasync_print_resources(ua, "BEFORE_FINAL_CLEANUP");
if (server_instance) { server_instance->running = 0; utun_instance_destroy(server_instance); server_instance = NULL; } if (server_instance) { server_instance->running = 0; utun_instance_destroy(server_instance); server_instance = NULL; }
if (client_instance) { client_instance->running = 0; utun_instance_destroy(client_instance); client_instance = NULL; } if (client_instance) { client_instance->running = 0; utun_instance_destroy(client_instance); client_instance = NULL; }
printf("--- uasync before uasync_destroy ---\n");
uasync_print_resources(ua, "BEFORE_UASYNC_DESTROY");
if (ua) { uasync_destroy(ua, 0); ua = NULL; } if (ua) { uasync_destroy(ua, 0); ua = NULL; }
cleanup_temp_configs(); cleanup_temp_configs();

Loading…
Cancel
Save