Browse Source

etcp: строгая последовательность коллбэков при закрытии (NEW→UP↔DOWN→DELETE)

- close: финальный per-connection DOWN только если был UP, до state=2
- etcp_cbk_fire / etcp_fire_conn_status / etcp_link_ready_callback / etcp_on_up: guard state==2
- документация контракта в etcp_api.h / etcp.h
proxy
evgeny 6 days ago
parent
commit
3a4daf6e77
  1. 13
      src/transport_layer/etcp.c
  2. 15
      src/transport_layer/etcp.h
  3. 20
      src/transport_layer/etcp_api.h

13
src/transport_layer/etcp.c

@ -291,6 +291,7 @@ struct ETCP_CONN* etcp_connection_create(struct UTUN_INSTANCE* instance, char* n
void etcp_fire_conn_status(struct ETCP_CONN* etcp, int status) {
if (!etcp || !etcp->instance) return;
if (etcp->state == 2 && status != ETCP_CONN_STATUS_DELETE) return; // при state==2 допустим только DELETE
static const char* names[] = { "NEW", "UP", "DOWN", "DELETE" };
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "[%s] Connection status changed to %s", etcp->log_name,
(status >= 0 && status < (int)(sizeof(names)/sizeof(names[0]))) ? names[status] : "?");
@ -300,6 +301,7 @@ void etcp_fire_conn_status(struct ETCP_CONN* etcp, int status) {
void etcp_cbk_fire(struct ETCP_CONN* conn, int event) {
if (!conn) return;
if (conn->state == 2) return; // после DELETE per-connection события не идут
static const char* names[] = { "INIT", "REINIT", "UP", "DOWN", "NODE_CHANGED" };
int idx = 0, e = event; while (e >>= 1) idx++;
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[%s] callback event: %s", conn->log_name, (idx >= 0 && idx < (int)(sizeof(names)/sizeof(names[0]))) ? names[idx] : "?");
@ -310,6 +312,7 @@ void etcp_cbk_fire(struct ETCP_CONN* conn, int event) {
}
static void etcp_on_up(struct ETCP_CONN* etcp) {
if (etcp->state == 2) return; // defensive: после DELETE UP не стреляет
int total_links = 0; for (struct ETCP_LINK* l = etcp->links; l; l = l->next) total_links++;
uint64_t elapsed_ms = (get_time_tb() - etcp->setup_start_tb) / 10;
struct strbuf sb = strbuf_new(); int any = 0;
@ -400,8 +403,14 @@ void etcp_connection_close(struct ETCP_CONN* etcp) {
// === PHASE 1: detach from external world ===
/* Финальный DOWN если соединение ещё UP (переход UP→DOWN до state=2):
строгая последовательность ... → UP → DOWN → DELETE, без дублей. */
if (etcp->links_up != 0) {
etcp->links_up = 0;
etcp_cbk_fire(etcp, ETCP_CBK_EVENT_DOWN);
}
etcp->state = 2; // deleted — blocks ref_take and etcp_send immediately
if (etcp->links_up != 0) etcp->links_up = 0;
if (etcp->peer_sleep_phase) {
etcp->peer_sleep_phase = 0;
@ -411,7 +420,6 @@ void etcp_connection_close(struct ETCP_CONN* etcp) {
etcp_fire_conn_status(etcp, ETCP_CONN_STATUS_DELETE);
DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "[%s] close: state=2 conn=%p peer=%016llx (DELETE fired)", etcp->log_name, (void*)etcp, (unsigned long long)etcp->peer_node_id);
etcp_cbk_fire(etcp, ETCP_CBK_EVENT_DOWN);
// Cancel active timers
if (etcp->retrans_timer) { uasync_cancel_timeout(etcp->instance->ua, etcp->retrans_timer); etcp->retrans_timer = NULL; }
@ -1066,6 +1074,7 @@ static void ack_response_timer_cb(void* arg) {// проверяем неотпр
static void etcp_link_ready_callback(struct ETCP_CONN* etcp) {
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "");
if (!etcp) return;
if (etcp->state == 2) return; // удалённый conn не поднимается
if (etcp->tx_state==0) {
DEBUG_WARN(DEBUG_CATEGORY_ETCP, "ETCP not ready, skip link state change");

15
src/transport_layer/etcp.h

@ -230,6 +230,21 @@ struct ETCP_CONN {
// Functions
struct ETCP_CONN* etcp_connection_create(struct UTUN_INSTANCE* instance, char* name);
/**
* @brief Закрыть соединение: фаза-1 detach + отложенная фаза-2 очистка.
*
* Порядок коллбэков при закрытии (строгая последовательность):
* 1) если соединение ещё UP (links_up != 0) — финальный per-connection DOWN
* (ETCP_CBK_EVENT_DOWN), затем links_up = 0;
* 2) state = 2;
* 3) conn-status DELETE (ETCP_CONN_STATUS_DELETE) — единственный статус при
* state==2 (по нему подписчики снимают conn из своих списков);
* 4) cleanup: таймеры, routing, линки, очередь, deferred u_free.
*
* После state==2 per-connection события (etcp_cbk_fire) и conn-status
* NEW/UP/DOWN не вызываются.
*/
void etcp_connection_close(struct ETCP_CONN* etcp);
void etcp_conn_queue_set_ready(struct ETCP_CONN* conn); // move pending->connections, fire ready cbks

20
src/transport_layer/etcp_api.h

@ -85,7 +85,12 @@ extern "C" {
#define ETCP_RT_ID_DM_MAILBOX 0x35 // dm_mailbox — offline-хранение
#define ETCP_RT_ID_CALL 0x36 // call — P2P аудио-звонок
// Connection status events (instance-level callback)
// Connection status events (instance-level callback).
// Строгая последовательность: NEW -> UP <-> DOWN -> DELETE.
// NEW — один раз при создании соединения.
// UP — переход links_up 0→1 (UP после UP не вызывается).
// DOWN — переход links_up 1→0 (DOWN после DOWN не вызывается).
// DELETE — один раз при закрытии, финальный; единственный статус, допустимый при state==2.
#define ETCP_CONN_STATUS_NEW 0 // соединение создано
#define ETCP_CONN_STATUS_UP 1 // соединение поднялось
#define ETCP_CONN_STATUS_DOWN 2 // соединение упало
@ -127,7 +132,10 @@ struct ETCP_CONN;
struct UTUN_INSTANCE;
struct TOPO_GROUP_NODE;
// Per-connection callback event types (bitmask)
// Per-connection callback event types (bitmask).
// Вызываются только при state != 2 (после DELETE события не идут).
// UP/DOWN зеркалят conn-status UP/DOWN; при закрытии (если соединение было UP)
// отправляется один финальный DOWN до state=2.
#define ETCP_CBK_EVENT_INIT (1 << 0)
#define ETCP_CBK_EVENT_REINIT (1 << 1)
#define ETCP_CBK_EVENT_UP (1 << 2)
@ -149,7 +157,9 @@ struct etcp_cbk_entry {
* @param event Битовая маска события (ETCP_CBK_EVENT_*)
*
* @note Вызываются все коллбэки соединения, у которых event_mask
* пересекается с переданным event
* пересекается с переданным event.
* @note Не вызывается при state==2 (соединение удалено) — после DELETE
* per-connection события не рассылаются.
*/
void etcp_cbk_fire(struct ETCP_CONN* conn, int event);
@ -329,7 +339,9 @@ int etcp_unbind(struct UTUN_INSTANCE* inst, uint8_t id);
* @param fn Коллбэк (сигнатура etcp_conn_status_fn)
* @param arg Пользовательский аргумент
*
* @note status = ETCP_CONN_STATUS_NEW / UP / DOWN / DELETE
* @note status = ETCP_CONN_STATUS_NEW / UP / DOWN / DELETE.
* Строгая последовательность NEW -> UP <-> DOWN -> DELETE; DELETE —
* единственный статус, отправляемый при state==2 (при закрытии).
*/
void etcp_add_conn_status_cbk(struct UTUN_INSTANCE* inst, etcp_conn_status_fn fn, void* arg);

Loading…
Cancel
Save