From fd82e7bf1bdce78b89b5a3c0e560f16ad1ccc1c8 Mon Sep 17 00:00:00 2001 From: evgeny Date: Mon, 26 Oct 2026 21:23:01 +0100 Subject: [PATCH] Tag DM history snapshots with conversation generation --- src/chat/chat_event.h | 2 +- src/dm/dm_core.c | 18 ++++++++++++++---- src/dm/dm_core.h | 4 +++- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/chat/chat_event.h b/src/chat/chat_event.h index b03a5664..68b0023c 100644 --- a/src/chat/chat_event.h +++ b/src/chat/chat_event.h @@ -62,7 +62,7 @@ extern "C" { #define CHAT_EVT_CALL_DECLINED 38 /* [call_id:8][reason:1] */ #define CHAT_EVT_CALL_STATS 39 /* [call_id:8][rtt:2][buffer:2][tempo:2][dropped:2][underruns:2][min:2][max:2][reserve:2] */ #define CHAT_EVT_CALL_ERROR 40 /* [call_id:8][err:1][text:var] */ -#define CHAT_EVT_DM_MESSAGES 41 /* [conv_id_len:1][conv_id:var][JSON:utf8] — сообщения беседы и состояния доставки */ +#define CHAT_EVT_DM_MESSAGES 41 /* [conv_id_len:1][conv_id:var][generation:8][JSON:utf8] — история указанного поколения */ #define CHAT_EVT_CALL_PATH 42 /* [call_id:8][text:var] — BGP-маршрут до пира звонка */ #define CHAT_EVT_RADIO_TALK 43 /* [group_id:8][src_node_id:8][stream_id:2][on:1] — кто-то начал/закончил говорить */ #define CHAT_EVT_CALL_CONNECTION 44 /* [call_id:8][CONN_TYPE:1][next_hop_node_id:8] — текущий путь отправки звонка */ diff --git a/src/dm/dm_core.c b/src/dm/dm_core.c index 465b53c6..81a52e4b 100644 --- a/src/dm/dm_core.c +++ b/src/dm/dm_core.c @@ -1310,19 +1310,29 @@ void dm_call_route_trampoline(void* arg) { /* Ограниченный JSON-снимок доставляется GUI асинхронно, без доступа UI к instance/SQLite. */ void dm_messages_trampoline(void* arg) { struct dm_messages_req* req = arg; - if (!req) return; + if (!req) { DEBUG_ERROR(DEBUG_CATEGORY_DM, "%s: missing history request", DM_ID); return; } struct UTUN_INSTANCE* inst = req->inst; char conv[64]; snprintf(conv, sizeof(conv), "%s", req->conv_id); + uint64_t generation = req->generation; u_free(req); if (!dm_of(inst)) { DEBUG_WARN(DEBUG_CATEGORY_DM, "%s: snapshot after stop", DM_ID); return; } + if (!generation) generation = dm_history_generation(inst, conv); + if (!dm_history_current(inst, conv, generation)) { + DEBUG_INFO(DEBUG_CATEGORY_DM, "%s: skipped stale history conv=%s generation=%llu", DM_ID, conv, + (unsigned long long)generation); return; + } size_t cl = strlen(conv), cap = 1024 * 1024, len = 0; - uint8_t* evt = u_malloc(1 + cl + cap); + uint8_t* evt = u_malloc(9 + cl + cap); if (!evt) { DEBUG_ERROR(DEBUG_CATEGORY_DM, "%s: snapshot allocation failed", DM_ID); return; } evt[0] = (uint8_t)cl; memcpy(evt + 1, conv, cl); - if (!dm_list_messages_json(inst, conv, 200, 0, (char*)evt + 1 + cl, cap, &len)) - chat_event_post(inst, CHAT_EVT_DM_MESSAGES, evt, (int)(1 + cl + len)); + memcpy(evt + 1 + cl, &generation, 8); + if (!dm_list_messages_json(inst, conv, 200, 0, (char*)evt + 9 + cl, cap, &len)) { + DEBUG_DEBUG(DEBUG_CATEGORY_DM, "%s: history snapshot conv=%s generation=%llu bytes=%zu", DM_ID, conv, + (unsigned long long)generation, len); + chat_event_post(inst, CHAT_EVT_DM_MESSAGES, evt, (int)(9 + cl + len)); + } u_free(evt); } diff --git a/src/dm/dm_core.h b/src/dm/dm_core.h index 0cffc420..3bd92a6e 100644 --- a/src/dm/dm_core.h +++ b/src/dm/dm_core.h @@ -161,10 +161,12 @@ struct dm_invite_req { void dm_invite_trampoline(void* arg); /* Запросить последние 200 сообщений со статусами. CHAT_EVT_DM_MESSAGES: - * [conv_id_len:1][conv_id:var][JSON:utf8]. Все чтения в uasync. */ + * [conv_id_len:1][conv_id:var][generation:8][JSON:utf8]. Все чтения в uasync. + * Ненулевое generation проверяется до чтения; 0 захватывает текущее поколение. GUI отбрасывает старый снимок. */ struct dm_messages_req { struct UTUN_INSTANCE* inst; char conv_id[64]; + uint64_t generation; /* Поколение выбранного PM; для запроса маршрута не используется. */ }; void dm_messages_trampoline(void* arg); /* Живой BGP-маршрут PM; group_id=0 в снимке означает недоступность. Все вызовы в uasync. */