You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

12 KiB

Задачи по проекту

Сюда пишется список задач с короткой аннотацией. Если задача большая и имеет ТЗ - то ТЗ оформляется отдельным файлом, а сюда помещается аннотация и ссылка на ТЗ.

Текущая задача

[ ] DM: прямой p2p чат с любым пользователем группы — отдельная DM-подсистема. ТЗ и архитектура: /doc/dm_arch.md. Статус: реализовано (dm_core/dm_mailbox/dm_crypto), тесты test_dm (крипто) и test_dm_e2e (интеграция). Остались задачи ниже.

[ ] Звонок (P2P аудио) — голос 1:1, из CHAT-группы или p2p-чата (DM). Отдельный каталог src/call/. Сигналинг + conn_mgr + аудиопоток (Opus) с backpressure (локальный буфер отправки при переполнении роутера) + адаптивный джиттер-буфер (SoundTouch, ускорение до 1.6x) + тоны (глитч/завершение) + watchdog 20с. Android: переключение источника (трубка/громкая/гарнитура). Архитектура: /doc/chat_call_arch.md. Статус: спроектировано, ждёт реализации.

Рефакторинг: per-instance chat/dm (однопоточный test_dm_e2e)

Сделано (глобальные синглтоны переведены на UTUN_INSTANCE):

  • chat_setting → inst->chat_settings (struct chat_setting_state, state-level API для парсера конфига + instance-level для runtime).
  • chat_event → inst->chat_event_handler (обработчик теперь получает inst).
  • chat_core (g_cc) → inst->chat_core (struct chat_core_ctx*).
  • chat_sync (g_cs) → inst->chat_sync.
  • member_sync callback-списки (g_props_cbks/g_apply_cbks) → в chat_core_ctx.
  • chat_join (g_keys) → в chat_core_ctx.
  • chat_msg media-счётчики/backfill → в chat_core_ctx.
  • dm_core (g_dm) → inst->dm; dm_mailbox (g_mb) → inst->dm_mailbox.
  • chat_headless_control (g_hc) → inst->headless.
  • chat_whisper — сигнатуры init/available/trigger принимают inst.
  • API протащено через inst (chat_core_/dm_), обновлены call-сайты в src/ (headless, topo, media_delivery, auto_socket, config_parser).
  • test_dm_e2e.c переписан на однопоточный: один uasync, три utun_instance (A,B,C), master state-machine, без fork. Фаза 2 (B offline) — utun_instance_destroy(B1) + пересоздание B2.
  • Попутно исправлен 1-байтовый overflow в dm_mailbox.c: mb_route_send (u_malloc(1+body_len) → 1+1+body_len).

Статус сборки: make -C src (libutun.a + бинарник utun) и make -C tests — OK.

Осталось (по порядку)

[+] test_dm_e2e: зависание в utun_instance_destroy(B1) (фаза P_B_OFFLINE) — сделано. Причин было несколько (не одна): 1. lib/u_async.c: если ближайший таймер уже истёк на входе в uasync_poll, get_next_timeout возвращал {0,0}, timeout_ms=-1 → epoll_wait(-1) блокировался навсегда, а process_timeouts (вызывается только после epoll_wait) не запускался. Фикс: if (timeout_ms == -1 && heap не пуст) timeout_ms = 0. 2. routing_layer/etcp_router.c: router_send_conn с group_id=0 (глобальная маршрутизация DM/mailbox) не находил маршрут (topo_groups_find(0)==NULL). Фикс: fallback на instance_find_conn(inst, remote) при group==NULL. 3. dm/dm_core.c: dm_on_conn_status PULL-ил только беседы с подключившимся пиром; B2 подключается к storage (C), а беседа — с A. Фикс: на подъём любого соединения PULL для всех бесед, чей пир не подключён напрямую. 4. dm/dm_mailbox.c: не было ACK после PULL_RESP → storage не чистил dm_mail. Фикс: PULL_RESP теперь несёт sender, получатель шлёт MB_SUBCMD_ACK. 5. utun_instance.c: dm_core_init ставил deliver-cb до dm_mailbox_init → mailbox не находился, deliver-cb не регистрировался. Фикс: mailbox init раньше core. test_dm_e2e проходит (несколько прогонов), test_dm тоже.

[+] chatgui-android: переезд на per-instance API — сделано. instance_lite.c/h (+instance_lite_get_instance()), jni_bridge.c, standby.c, headless_control.c, voice_recorder.c, attachment_sender.c, video_sender.c, photo_sender.c — все call-сайты переведены на inst. libutun_lite компилируется, jni_bridge.c — синтакс-чисто. (headless-линковка сломана ПРЕДЫДУЩЕ — instance_lite.c ссылается на jni_bridge-функции, не включённые в headless-сборку; отдельная задача.)

[+] test_chat_join: segfault (регресс рефакторинга) — сделано. chat_join хранил join_keys в chat_core_ctx (CC(inst)), а тест использует chat_join без chat_core → NULL-deref. Фикс: join_keys вынесен в UTUN_INSTANCE.join_keys (у chat_join собственный init/destroy).

[ ] BGP-гонка: дропнутый REQUEST_TABLE (отдельная задача, как договорились).

**Место:** `topo_group_new_conn()` в `src/routing_layer/topo_group.c:571`. Дедуп-ветка
«conn already in senders_list» (строки 579–587) делает `return` БЕЗ повторной отправки
`topo_group_send_table_request()`.

**Зачем дедуп:** тот же `ETCP_CONN` стреляет `ETCP_CONN_STATUS_UP` дважды (UDP-линк,
затем TCP-линк). Повторная обработка задваивает `active_conn_count`, из-за чего
переподключение не стартует. Поэтому `return` в дедупе — правильный.

**В чём гонка:** REQUEST_TABLE — это то, что заставляет пира отдать свою таблицу
(NODEINFO/узлы группы). Сценарий «разнесённого старта»:
1. A↔C соединение поднято. A шлёт C `REQUEST_TABLE` для группы G (`topo_group_new_conn`
   → `topo_group_send_table_request`, `topo_group.c:602`).
2. У C группа G ещё **не создана** (создаётся позже, по мере загрузки каналов/синка).
   C получает `REQUEST_TABLE`, но `topo_group_handle_request_table` не находит G →
   дропает запрос.
3. C наконец создаёт G (`topo_groups_create_group` → обход connections →
   `topo_group_new_conn(G, conn_A)` на стороне C) → C шлёт A свой `REQUEST_TABLE`.
   A отвечает своей таблицей → **C узнаёт A**.
4. Но A больше не шлёт C `REQUEST_TABLE` → **A так и не узнаёт C** в группе G.
Итог: при разнесённом старте A не видит C в CHAT-группе (односторонняя видимость).

**Фикс (предполагаемый):** в дедуп-ветке не просто `return`, а предварительно
идемпотентно `topo_group_send_table_request(group, conn)`:
```c
if (((struct TOPO_GROUP_CONN_ITEM*)se->data)->conn == conn) {
    topo_group_send_table_request(group, conn);   /* повторно, идемпотентно */
    DEBUG_INFO(..., "conn already in senders_list, re-request table (%s)", conn->log_name);
    return;
}
```
`topo_group_send_table_request` не ведёт состояния — просто шлёт REQUEST_TABLE,
пир отвечает снимком таблицы (идемпотентно). Лишний дубль таблицы безвреден.

**Замечание:** в однопоточном `test_dm_e2e` (и `test_chat_join_e2e`) из-за
детерминированного порядка группа успевает создаться до прихода REQUEST_TABLE —
поэтому тест проходит. Реальная гонка остаётся для асинхронных сетей. Отдельный
воспроизводимый тест для этой гонки пока не написан.

[ ] «packet undecryptable» на линках C (вторичное). A, узнав B через BGP, через topo_group_connect пытается поднять прямой A↔B линк (seskey 4777737b), но B не настроен принимать A → crypto-шум каждые ~1с. Разобраться с авто-подключением (не пытаться соединяться с узлами, для которых нет [client]/ncd-конфигурации, либо обрабатывать неудачу без спама).

[+] Обновить GUI call-сайты tools/chatgui (Qt, desktop) — сделано. - В chat_core.h вынесены trampoline-структуры из .c: update_my_name_arg, save_ui_state_arg, chat_setting_arg (были в chat_profile.c/chat_core.c). - gui_bridge.h/impl: gui_bridge_set_inst()/gui_bridge_get_inst() (+g_inst). - utun_node.cpp: gui_bridge_set_inst(m_instance), chat_event_set_handler(m_instance,…), chat_core_sync_my_addresses(m_instance). - Обновлены ~45 call-сайтов в 16 файлах (mainwindow, messagelist, messagedelegate, memberlistmodel, memberpropsdialog, accountlist, invitedialog, inviteby, joindialog, settingsdialog, channelsettingsdialog, soundsettingspage, audiodevicesettingspage, storagesettingspage, statuspage, connmonitorwindow). - Сборка cmake --build . --target vibechat — OK. Попутно: chat_sync_connect_from_invite писал cs->pending_* на вызывающем (GUI) потоке — перенёс в cm_invite_trampoline (uasync-поток), убрав гонку; заодно исправил утечку inv->addrs_data и добавил null-check cs в трамплине.

[+] Прогон всех тестов — сделано. make clean && make -j4 OK; все тесты проходят (кроме test_auto_socket_dynamic — пропуск «requires root»). test_dm_e2e стабилен в нескольких прогонах. В test_dm_e2e при teardown остаётся timer-leak (~68 узлов, router_no_route), не влияет на результат — стоит разобрать отдельно.

Открытые флаки

[ ] test_etcp_reconnect — флаки под параллельной нагрузкой make check -j4: phase 4 (reconnect после server restart) таймаутит (sent=480 recv=0), при одиночном запуске стабильно TEST PASSED. Наш код (chat_join/chat_sync) его не трогает.