From 6f8164d7cd7c269e61464bee66fa5f28a9ec3e04 Mon Sep 17 00:00:00 2001 From: evgeny Date: Mon, 26 Oct 2026 14:07:51 +0100 Subject: [PATCH] Record call audio rebind counter defect and ownership plan --- doc/call_audit_findings.txt | 92 +++++++++++++++++++++++++++++++++++++ doc/tasks.md | 12 +++++ 2 files changed, 104 insertions(+) diff --git a/doc/call_audit_findings.txt b/doc/call_audit_findings.txt index 5ce5fb49..dd69ea1b 100644 --- a/doc/call_audit_findings.txt +++ b/doc/call_audit_findings.txt @@ -3,6 +3,9 @@ Текущий статус: дефект допуска второго исходящего звонка исправлен после согласования. Штатная регрессия, четыре C++ voice-набора и полный check202/202 PASS. Разделы1–4 сохраняют историю диагностики, внедрение — в разделе5. +При продолжении подтверждён новый дефект: старые TX-задачи после destroy/init +call_audio уменьшают pending новой привязки и обходят лимит8. Реализация этого +дефекта не менялась; аудит остановлен перед исправлением. Подробности — раздел6. Полная проверка аудио, видео и платформенных разделов не завершена. База проверки: f79b9261, чистое рабочее дерево после предыдущего push stream. @@ -195,3 +198,92 @@ call-заголовков не менялись (API/ABI/wire прежние), s проверенному8eb3d102, diff --check и оформление новых файлов в пределах150. Отметки call и прежние stream18[V] сохранены. Основной namespace прежний, all/default/lo.disable_ipv6=1. После runtime менялись только отчёты/задачи. + +6. Аудиостек: устаревшие задачи уменьшают pending новой привязки — 26.10.2026 + +Полностью прочитаны call_audio.c652, call_jitter.cpp265, call_delay.cpp240, +call_timeline.cpp174 и call_tones.c165 (всего1496 строк), а также соответствующие +заголовки, call_clock.h и inline call_jitter_window.h. Уточнения контрактов ещё +не завершены: найдено расхождение учёта TX при повторной привязке к instance. + +call_audio_init (строка110) задаёт g_inst=inst и g_tx_pending=0. У старых +posted tasks при этом нет отдельного поколения привязки. call_audio_send +(строка82) уменьшает g_tx_pending, даже когда tx->generation уже не совпадает +с g_tx_generation и отправка старого кадра запрещена. После init это уже +счётчик новых задач: он может стать0 при восьми ещё не исполненных новых кадрах. +Следующие восемь feed принимаются, хотя лимит должен остановить их. + +Генерация звонка решает другую задачу: start/release меняют g_tx_generation, +поэтому старый Opus не отправляется. Она не является эпохой привязки init/destroy. +При обычной смене звонка без reinit старые задачи по-прежнему занимают pending, +и их исполнение должно уменьшать счётчик. Простое условие current перед +декрементом оставило бы эти слоты навсегда занятыми — это неверное исправление. + +Детерминированное воспроизведение + +/tmp/call-audit-tx-pending.cpp: минимальный UTUN_INSTANCE с настоящим UASYNC, +реальные call_audio/Opus/CallDelay/SoundTouch/tones из штатной CMake-библиотеки. +uasync и порядок posted callbacks настоящие; состояние/счётчики call_audio не +читаются и не подменяются. Нет сетевого звонка, микрофона или пользовательской БД. +Сетевую границу call_send_media заменяет наблюдатель числа кадров/ID; +chat_setting отключает compressor/AEC, get_time_tb фиксирован, чтобы исключить TTL. + +reinit: +1) init/start_prepared41, принять8 PCM-кадров; их posted tasks пока не выполнять. +2) Поставить собственный uasync callback extra после этих8 задач. +3) stop/destroy/init того же instance/uasync, start_prepared42, принять8 новых кадров. +4) uasync исполняет8 старых задач: отправка запрещена, но pending новой привязки + уменьшается8->0. Штатный DEBUG-снимок показывает pending0 при восьми ожидающих новых кадрах. +5) extra пытается принять ещё8 кадров нового звонка, пока первые8 не исполнены: + принято8, ожидалось0. Одновременно ожидают исполнения16 кадров звонка42. +6) Два прохода uasync исполняют также задачи, опубликованные из extra: + sent_current16, sent_old0, allocations0->0; отрицательная проверка exit1. + +Контроль fresh: extra поставлен перед8 кадрами нового звонка, старых задач нет. +pending8, extra_accepted0, sent_current8, sent_old0, allocations0->0, exit0. +Контроль same_owner: stop/start без destroy/init. Старые8 занимают лимит, +initial_new0, после их исполнения extra принимает8; sent_current8, old0, +allocations0->0, exit0. Это подтверждает необходимость различать привязку и звонок. + +Сборка через готовые штатные CMake flags/utun_voice/utun/uasync/Opus, +без ручной компиляции C-реализаций; /tmp/call-audit-tx-pending.mk. +Build PASS: /tmp/call-audit-tx-pending-build.log. +Логи /tmp/call-audit-tx-pending-{fresh,same_owner,reinit}.log прочитаны целиком; +call=debug показывает pending, число posts и причины отказов. Первый вариант +стенда выполнял один poll и не исполнял задачи из extra до проверки доставки; +исправлен только стенд (два poll), после чего fresh/same_owner PASS, reinit FAIL. + +Предлагаемое исправление + +Использовать уже применённый в radio_audio шаблон: отдельная монотонная эпоха +привязки instance, сохранённая в каждой TX-задаче, помимо генерации звонка. +Менять её при фактическом init/destroy; повтор init того же живого владельца +оставлять идемпотентным. Обнуление pending относится к новой привязке. +В send вычислять owner по эпохе привязки и inst; уменьшать pending только +для своего owner, включая задачи старого звонка того же owner. Отправка +дополнительно требует текущего звонка/состояния/возраста. Статистика старой +привязки не должна попадать в новую. Radio-код прочитан в этих точках; +менять его или формат posted_task/uasync для этого исправления не требуется. + +Предел8 относится к активной привязке; старые неотправляемые задачи могут +доживать в прежнем uasync до callback/destroy. Число физических старых+новых +аллокаций при нескольких последовательных reinit — отдельное свойство, +данный стенд не доказывает общего лимита8 для всех исторических привязок. + +Постоянная проверка должна сохранить все три сценария, добавить разные +instance/uasync и повторное использование адреса instance, удаление старых +задач при uasync_destroy без callback, повтор init и обычную смену звонка. +Нельзя перенести старое уменьшение счётчика или просроченную статистику в новый owner. +Затем выполнить C++ voice-профиль и общий check; GUI/Android lifecycle проверять отдельно. + +Существующий профиль call_jitter/delay/timeline/voice_tx повторён:4/4 PASS, +3,01с; /tmp/call-audit-voice-baseline.log. Он новый сценарий reinit не покрывает. +Desktop runLoop штатно снимает call_audio при остановке uasync; текущий GUI +не воспроизводился, сбой реального звонка не заявляется. Android/GUI и +ASAN/UBSan/LSan в этом продолжении не запускались. Полный check202 из раздела5 +относится к прежнему исправлению допуска, а не к этому расхождению. + +Аудит остановлен по AGENTS.md:10. Production C/C++ и штатные тесты не менялись; +добавлены предупреждение call_audio.h/статус doc и задача. Заголовки clock, +delay,jitter,jitter_window,timeline,tones теперь[~], без преждевременного[V]. +Итог0[V]/10[~]/11[ ]; headless и видео ещё не проверялись. Архитектурный документ[~]. diff --git a/doc/tasks.md b/doc/tasks.md index e977c091..319b1703 100644 --- a/doc/tasks.md +++ b/doc/tasks.md @@ -35,6 +35,18 @@ Полный аудит остальных контрактов остаётся частичным; wire/формат БД не менялись. Подробности: [member_merkle_audit_findings.txt](member_merkle_audit_findings.txt). +[ ] **call_audio: изолировать pending TX-задачи разных привязок instance** — подтверждено 26.10.2026. + init обнуляет g_tx_pending, но callback старой привязки уменьшает уже новый счётчик. + Детерминированный тест реальных Opus/uasync после stop/destroy/init на том же uasync: + pending0 при8 ожидающих новых кадрах, ещё8 принимаются, отправлено16 вместо лимита8; old0,allocations0->0. + fresh и stop/start без reinit PASS; исходный voice-профиль4/4 PASS нового сценария не покрывает. + Нужна отдельная эпоха привязки, как в radio_audio: decrement только своего owner, + включая старые звонки того же owner; current-call generation отдельно управляет отправкой. + Проверить reinit/старый uasync destroy/повтор init/смену звонка и позднюю статистику. + Production не менялся, аудит остановлен по AGENTS.md; аппаратный GUI/Android не воспроизводился. + Подробности: [call_audit_findings.txt](call_audit_findings.txt),раздел6; + логи /tmp/call-audit-tx-pending-{fresh,same_owner,reinit}.log. + [+] **call: запрещать второй исходящий вызов при незавершённом обычном звонке** — исправлено 26.10.2026. call_start до session/CM/INVITE отклоняет новый ID при обычной занятости с ENDED/BUSY. Уже зарегистрированный ID, включая ENDED до очистки, отклоняется без события/изменения старого звонка.