Stream member files with router backpressure and parallel transfer handles

This commit is contained in:
evgeny committed 2026-10-05 23:36:49 +02:00
1 parent f7e1752fc2
commit 25e2ffeea7
13 files changed
+918 -207

No files matched your search

+70 -9
View File
@@ -60,7 +60,8 @@ static void router_close_finalize(void* arg);
static void router_conn_free_queues(struct ETCP_ROUTER_CONN* rconn);
static int router_conn_reset(struct ETCP_ROUTER_CONN* rconn);
static void free_entry(struct ll_entry* e) { queue_dgram_free(e); queue_entry_free(e); }
static void router_handle_ack(struct UTUN_INSTANCE* inst, struct ETCP_ROUTER_CONN* rconn, uint32_t seq, uint64_t src_node_id, uint16_t ack_ts);
static void router_handle_ack(struct UTUN_INSTANCE* inst, struct ETCP_ROUTER_CONN* rconn, uint32_t seq,
uint64_t src_node_id, uint16_t ack_ts, int receive_busy);
static void router_forward_transit(struct UTUN_INSTANCE* inst, struct ll_entry* entry, struct SVC_ROUTE_HDR* hdr);
static void router_handle_data_packet(struct ETCP_ROUTER_CONN* rconn, struct ETCP_CONN* conn, uint32_t seq, const uint8_t* wire, size_t wire_len);
static int router_retransmit_one(struct ETCP_ROUTER_CONN* rconn, struct ROUTER_INFLIGHT* inf);
@@ -488,6 +489,7 @@ static void router_send_queue_free(void* arg) { queue_free(arg); }
// Отменить таймеры/waiter и освободить все очереди rconn (общий код для close/restart).
static void router_conn_free_queues(struct ETCP_ROUTER_CONN* rconn) {
struct UTUN_INSTANCE* inst = rconn->inst;
if (rconn->receive_pump) { uasync_call_soon_cancel(inst->ua, rconn->receive_pump); rconn->receive_pump = NULL; }
if (rconn->handshake_timer) { uasync_cancel_timeout(inst->ua, rconn->handshake_timer); rconn->handshake_timer = NULL; }
if (rconn->ack_timer) { uasync_cancel_timeout(inst->ua, rconn->ack_timer); rconn->ack_timer = NULL; }
if (rconn->idle_ack_timer) { uasync_cancel_timeout(inst->ua, rconn->idle_ack_timer); rconn->idle_ack_timer = NULL; }
@@ -538,6 +540,8 @@ static int router_conn_reset(struct ETCP_ROUTER_CONN* rconn) {
queue_set_callback(rconn->incoming_q, router_incoming_q_cb, rconn);
queue_set_threshold(rconn->incoming_q, 0, 0);
rconn->incoming_data_ready = 0;
rconn->receive_blocked = 0;
rconn->peer_receive_blocked = 0;
rconn->recv_conn = NULL;
rconn->rtt = 0; rconn->rtt_jitter = 0;
@@ -605,6 +609,7 @@ static void router_send_kick(struct ETCP_ROUTER_CONN* rconn) {
return;
}
int retry = rconn->retrans_seq != rconn->retrans_end;
if (rconn->peer_receive_blocked && !retry) return;
if (!retry && queue_entry_count(rconn->send_q) == 0) return;
if (!retry && queue_entry_count(rconn->inflight_q) >= (int)router_effective_max_inflight(rconn)) {
rconn->send_blocked = 1;
@@ -638,6 +643,7 @@ static void router_send_drain_cb(struct ll_queue* q, void* arg) {
router_send_kick(rconn);
return;
}
if (rconn->peer_receive_blocked) return;
if (queue_entry_count(rconn->inflight_q) >= (int)router_effective_max_inflight(rconn)) {
rconn->send_blocked = 1;
return;
@@ -960,7 +966,7 @@ static void router_send_ack(struct ETCP_ROUTER_CONN* rconn) {
hdr->src_node_id = inst->node_id;
hdr->seq = rconn->rx_seq;
hdr->svc_id = rconn->svc_id;
hdr->flags = 0;
hdr->flags = rconn->receive_blocked ? ROUTER_FLAG_RECEIVE_BUSY : 0;
hdr->reset_id = rconn->reset_id;
hdr->peer_reset_id = rconn->peer_reset_id;
hdr->challenge = 0;
@@ -998,7 +1004,7 @@ static void router_schedule_ack(struct ETCP_ROUTER_CONN* rconn) {
uasync_cancel_timeout(rconn->inst->ua, rconn->idle_ack_timer);
rconn->idle_ack_timer = NULL;
}
if (rconn->rx_seq == rconn->last_sent_ack_seq) return;
if (rconn->rx_seq == rconn->last_sent_ack_seq && !rconn->receive_blocked) return;
uint64_t now = get_time_tb();
uint64_t elapsed = now - rconn->last_ack_sent_tb;
if (elapsed >= ROUTER_ACK_INTERVAL_TB) {
@@ -1015,12 +1021,12 @@ static void router_ack_timer_cb(void* arg) {
struct ETCP_ROUTER_CONN* rconn = (struct ETCP_ROUTER_CONN*)arg;
if (rconn->closed) return;
rconn->ack_timer = NULL;
if (rconn->rx_seq != rconn->last_sent_ack_seq)
if (rconn->rx_seq != rconn->last_sent_ack_seq || rconn->receive_blocked)
router_ack_do_send(rconn);
if (rconn->rx_seq != rconn->last_sent_ack_seq) {
if (rconn->rx_seq != rconn->last_sent_ack_seq || rconn->receive_blocked) {
if (!rconn->ack_timer)
rconn->ack_timer = uasync_set_timeout(rconn->inst->ua,
ROUTER_ACK_INTERVAL_TB, rconn, router_ack_timer_cb, "router_ack");
rconn->receive_blocked ? ROUTER_ACK_BUSY_TB : ROUTER_ACK_INTERVAL_TB, rconn, router_ack_timer_cb, "router_ack");
} else if (!rconn->idle_ack_timer) {
rconn->idle_ack_timer = uasync_set_timeout(rconn->inst->ua,
ROUTER_ACK_IDLE_TB, rconn, router_idle_ack_timer_cb, "router_idle_ack");
@@ -1105,6 +1111,21 @@ static void router_try_assembly(struct ETCP_ROUTER_CONN* rconn, struct ETCP_CONN
while (1) {
struct ll_entry* entry = queue_find_data_by_index(rconn->recv_q, &next);
if (!entry) break;
int (*ready)(struct UTUN_INSTANCE*, uint64_t, uint64_t, const uint8_t*, size_t) =
rconn->inst->router_bindings.receive_ready[rconn->svc_id];
if (ready && !ready(rconn->inst, rconn->group_id, rconn->remote_node_id,
entry->dgram + SVC_ROUTE_HDR_SIZE, entry->len - SVC_ROUTE_HDR_SIZE)) {
if (!rconn->receive_blocked) DEBUG_DEBUG(DEBUG_CATEGORY_ETCPROUTE,
"router receive paused: svc=%u peer=%llx next_seq=%u buffered=%u", rconn->svc_id,
(unsigned long long)rconn->remote_node_id, next, rconn->recv_q->count);
rconn->receive_blocked = 1;
if (!rconn->ack_timer) rconn->ack_timer = uasync_set_timeout(rconn->inst->ua,
ROUTER_ACK_BUSY_TB, rconn, router_ack_timer_cb, "router_receive_busy");
return;
}
if (rconn->receive_blocked) DEBUG_DEBUG(DEBUG_CATEGORY_ETCPROUTE,
"router receive resumed: svc=%u peer=%llx next_seq=%u", rconn->svc_id, (unsigned long long)rconn->remote_node_id, next);
rconn->receive_blocked = 0;
queue_remove_data(rconn->recv_q, entry);
uint64_t peer_id = rconn->peer_reset_id;
int ret = router_deliver(rconn, conn ? conn : loopback_conn(rconn->inst), entry->dgram, entry->len);
@@ -1156,7 +1177,7 @@ static void router_incoming_q_cb(struct ll_queue* q, void* arg) {
// Обработать ACK: обновить RTT/jitter, снять подтверждённые inflight-пакеты, при необходимости
// возобновить отправку (снять send_blocked).
static void router_handle_ack(struct UTUN_INSTANCE* inst, struct ETCP_ROUTER_CONN* rconn,
uint32_t seq, uint64_t src_node_id, uint16_t ack_ts) {
uint32_t seq, uint64_t src_node_id, uint16_t ack_ts, int receive_busy) {
(void)src_node_id;
if (!rconn) return;
if ((int32_t)(seq - rconn->tx_sent) > 0) {
@@ -1165,6 +1186,17 @@ static void router_handle_ack(struct UTUN_INSTANCE* inst, struct ETCP_ROUTER_CON
rconn->c_stale_ack++;
return;
}
if ((int32_t)(seq - rconn->tx_acked) < 0) { rconn->c_stale_ack++; return; }
if (receive_busy != rconn->peer_receive_blocked) DEBUG_DEBUG(DEBUG_CATEGORY_ETCPROUTE,
"router peer receive busy=%d svc=%u peer=%llx ack=%u", receive_busy, rconn->svc_id, (unsigned long long)src_node_id, seq);
int resumed = rconn->peer_receive_blocked && !receive_busy;
rconn->peer_receive_blocked = (uint8_t)receive_busy;
if (receive_busy) {
rconn->last_ack_changed_tb = get_time_tb(); rconn->no_ack_count = 0;
rconn->retrans_seq = rconn->retrans_end;
router_cancel_send_waiter(rconn);
}
if (resumed) router_send_kick(rconn);
if ((int32_t)(seq - rconn->tx_acked) <= 0) { rconn->c_stale_ack++; return; }
if (ack_ts != 0) {
uint16_t new_rtt = get_current_timestamp() - ack_ts;
@@ -1276,6 +1308,12 @@ static void router_handle_data_packet(struct ETCP_ROUTER_CONN* rconn, struct ETC
rconn->c_dup_dropped++; return;
}
if (rconn->incoming_q->count + rconn->recv_q->count >= (unsigned)rconn->max_inflight + 1) {
DEBUG_WARN(DEBUG_CATEGORY_ETCPROUTE, "router receive window full: svc=%u peer=%llx next=%u seq=%u buffered=%u",
rconn->svc_id, (unsigned long long)rconn->remote_node_id, rconn->rx_seq, seq,
rconn->incoming_q->count + rconn->recv_q->count);
return;
}
struct ll_entry* qe = queue_entry_new(4);
if (!qe) return;
*(uint32_t*)qe->data = seq;
@@ -1331,7 +1369,7 @@ static void etcp_router_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry)
size_t payload_len = entry->len - SVC_ROUTE_HDR_SIZE;
if (!h->reset_id || (payload_len && (control || h->challenge))
|| (!payload_len && control != 0 && control != ROUTER_FLAG_START && control != ROUTER_FLAG_RST
&& control != (ROUTER_FLAG_START | ROUTER_FLAG_RST) && control != ROUTER_FLAG_CLOSE)) {
&& control != (ROUTER_FLAG_START | ROUTER_FLAG_RST) && control != ROUTER_FLAG_CLOSE && control != ROUTER_FLAG_RECEIVE_BUSY)) {
DEBUG_WARN(DEBUG_CATEGORY_ETCPROUTE, "router recv: malformed flags=%02x seq=%u len=%zu epoch=%016llx",
h->flags, h->seq, payload_len, (unsigned long long)h->reset_id);
free_entry(entry); return;
@@ -1362,7 +1400,7 @@ static void etcp_router_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry)
} else if (!payload_len) {
MF_ROUTE_DEBUG(h->svc_id, "ACK rx peer=%llx next_seq=%u old_acked=%u sent=%u inflight=%d",
(unsigned long long)h->src_node_id, h->seq, rconn->tx_acked, rconn->tx_sent, queue_entry_count(rconn->inflight_q));
if (!h->challenge) router_handle_ack(inst, rconn, h->seq, h->src_node_id, h->timestamp);
if (!h->challenge) router_handle_ack(inst, rconn, h->seq, h->src_node_id, h->timestamp, control == ROUTER_FLAG_RECEIVE_BUSY);
} else {
rconn->last_recv_pkt_ts = h->timestamp;
rconn->last_recv_pkt_local_tb = get_time_tb(); rconn->last_recv_updated = 1;
@@ -1436,10 +1474,33 @@ int etcp_router_unbind(struct UTUN_INSTANCE* inst, uint8_t svc_id) {
return -1;
}
inst->router_bindings.callbacks[svc_id] = NULL;
inst->router_bindings.receive_ready[svc_id] = NULL;
DEBUG_INFO(DEBUG_CATEGORY_ETCPROUTE, "svc_id=%u", svc_id);
return 0;
}
void etcp_router_set_receive_ready(struct UTUN_INSTANCE* inst, uint8_t svc_id,
int (*ready)(struct UTUN_INSTANCE*, uint64_t, uint64_t, const uint8_t*, size_t)) {
if (!inst) { DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "router receive readiness: NULL instance"); return; }
inst->router_bindings.receive_ready[svc_id] = ready;
}
static void router_receive_pump(void* arg) {
struct ETCP_ROUTER_CONN* rconn = arg;
rconn->receive_pump = NULL;
if (rconn->closed) return;
// Физический recv_conn мог исчезнуть за время дискового I/O; сервис получает стабильный instance.
router_try_assembly(rconn, loopback_conn(rconn->inst));
if (!rconn->closed) router_schedule_ack(rconn);
}
void etcp_router_resume_receive(struct UTUN_INSTANCE* inst, uint64_t group, uint64_t peer, uint8_t svc_id) {
struct ETCP_ROUTER_CONN* rconn = inst ? router_conn_find(inst, group, peer, svc_id) : NULL;
if (!rconn || rconn->closed || !rconn->receive_blocked || rconn->receive_pump) return;
rconn->receive_pump = uasync_call_soon(inst->ua, rconn, router_receive_pump);
if (!rconn->receive_pump) DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "router receive resume allocation failed svc=%u", svc_id);
}
// Отправить сервисный пакет. entry->dgram[0] = svc_id, остальное — payload. Берёт на себя
// освобождение entry в любом исходе. dst==self — loopback-доставка.
int etcp_route_send(struct UTUN_INSTANCE* inst, uint64_t group_id, uint64_t dst_node_id, struct ll_entry* entry, int force, int mode) {
+13
View File
@@ -77,6 +77,7 @@ struct SVC_ROUTE_HDR {
#define ROUTER_FLAG_ENCRYPTED 0x04 // payload зашифрован AES-128-CCM (nonce+tag+crc)
#define ROUTER_FLAG_SIGNED 0x08 // пакет содержит Ed25519 подпись (64 байта после payload)
#define ROUTER_FLAG_CLOSE 0x02 // нормальное закрытие conn
#define ROUTER_FLAG_RECEIVE_BUSY 0x01 // ACK: сервис приостановил приём, seq не подтверждает отложенные пакеты
// ====================================================================
// 2. Внутренние структуры и константы (etcp_router.c, control_server.c)
@@ -164,6 +165,9 @@ struct ETCP_ROUTER_CONN {
// Очередь приёма входящего трафика (между сетью и recv_q)
struct ll_queue* incoming_q; // FIFO очередь входящих пакетов
uint8_t incoming_data_ready; // 1 = данные в incoming_q и callback был вызван
uint8_t receive_blocked; // сервис временно не принимает следующую кодограмму
uint8_t peer_receive_blocked; // последний ACK пира объявил паузу приёма
void* receive_pump; // отложенное продолжение приёма
uint32_t c_pkts_sent; // успешные отправки данных
uint32_t c_pkts_send_err; // ошибки отправки
@@ -182,6 +186,7 @@ struct ETCP_ROUTER_CONN {
#define ROUTER_MAX_INFLIGHT 256 // макс пакетов в полёте (для контроля inflight)
#define ROUTER_ACK_INTERVAL_TB 100 // интервал ACK: 10ms в timebase (0.1ms)
#define ROUTER_ACK_IDLE_TB 5000 // idle таймаут: 500ms
#define ROUTER_ACK_BUSY_TB 1000 // 100ms: живой приёмник ждёт диск, а не потерял транспорт
#define ROUTER_MAX_SEND_Q_PACKETS 64 // порог backpressure на send_q
#define ROUTER_SEND_WATCHDOG_TB 5000 // DEBUG-watchdog: 500ms проверка инварианта drain
@@ -212,6 +217,7 @@ struct ROUTER_INFLIGHT {
// Bindings для сервисов внутри etcp_router (аналогично ETCP_BINDINGS)
struct ETCP_ROUTER_BINDINGS {
etcp_recv_fn callbacks[SVC_ROUTE_MAX_BINDINGS];
int (*receive_ready[SVC_ROUTE_MAX_BINDINGS])(struct UTUN_INSTANCE*, uint64_t, uint64_t, const uint8_t*, size_t);
};
// ====================================================================
@@ -229,6 +235,13 @@ int etcp_router_bind(struct UTUN_INSTANCE* inst, uint8_t svc_id, etcp_recv_fn c
// Снять обработчик; само по себе не закрывает логические каналы сервиса.
int etcp_router_unbind(struct UTUN_INSTANCE* inst, uint8_t svc_id);
// Проверяется до доставки/ACK. 0 оставляет пакет в recv_q, сохраняя порядок; CLOSE не блокируется.
// ready получает заимствованный проверенный payload и работает в uasync без I/O. NULL снимает ограничение.
void etcp_router_set_receive_ready(struct UTUN_INSTANCE* inst, uint8_t svc_id,
int (*ready)(struct UTUN_INSTANCE*, uint64_t, uint64_t, const uint8_t*, size_t));
// Продолжение отложено; канал не создаётся, waiter/arg сервиса не сохраняются.
void etcp_router_resume_receive(struct UTUN_INSTANCE* inst, uint64_t group, uint64_t peer, uint8_t svc_id);
// Отправить сервисный пакет (авто-conn, seq, inflight-контроль через send_q).
// Принимает владение entry во всех исходах, включая ошибку.
// entry->dgram = svc_id[1] || payload; 0=принято в очередь, <0=ошибка, не подтверждение доставки.