mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 19:45:09 +00:00
TX-DSP поток зовёт fexchange0(TXA_CHAN) через буферы FTXIn/FTXOut, и ровно туда же на каждом T/R лезет SetTXRun из потока контроллера. Гейт FTXActive их не разделял: поток проверяет его СНАРУЖИ, а внутрь блока входит позже. Три дефекта одной природы: * деструктор звал SetTXRun(False), который поток НЕ останавливает — он лишь опускает гейт и тут же сам сливает канал своими fexchange0, то есть лезет в TXA одновременно с ещё живым потоком, прямо перед сносом объекта. Теперь Destroy сразу зовёт Close: тот опускает гейт, будит поток, дожидается WaitFor и лишь потом закрывает каналы. Комментарий «останавливаем TX поток» врал; * слив на отпускании PTT шёл без всякой синхронизации — окно гонки примерно 1-1.5% на каждый T/R (блок ~150 мкс против периода 10.7 мс). Появился FTXLock по идиоме RX-стороны (FMainGated + FMainLock): поток берёт лок вокруг ProcessTXBlock и ПЕРЕЧИТЫВАЕТ гейт под ним, оба слива в SetTXRun идут под тем же локом. Ждать контроллеру не дольше одного блока TXA; * ветка ЗАПУСКА оставалась открытой: SetMOX не отсекает MOX=True поверх уже идущей передачи (прислать повтор вправе CAT, web и TCI), а SetTXRun(True) обнулял индексы mic-кольца и дёргал SetChannelState мимо лока. ★Худший исход не теоретический: TX-поток дописывает свой локальный tail поверх обнулённого, кольцо выглядит почти полным СТАРЫХ сэмплов, и они уходят в эфир пачкой. Теперь `if Run and FTXActive then Exit` плюс весь старт одной транзакцией под FTXLock, FTXActive публикуется последним. ★Перенастройку TXA в ChangeSampleRate под лок НЕ берём: там SetChannelState с dmode=1 ждёт фейда, а фейд прокручивают fexchange0 самого потока — под локом это клин на ~106 мс (тот самый старый фриз TX→RX). Синхронизация там своя: остановка канала. Второй известный кросс-поточный доступ к TXA — pscc из сетевого потока (PureSignal), так же устроено в Thetis. Попутно закрыт use-after-free: TX-поток создаёт SetTXRun, которому открытый движок не нужен, а гасил его только код ЗА гейтом `if not FInitialized then Exit` в Close — на неоткрытом движке поток переживал Free объекта. Ловилось не там, где сделано: следующий TThread.Create в процессе падал с AV (в стенде — на секции часов после пейсинга, раньше — на третьем TRadioController в FreeEngines). Остановка потока перенесена в начало Close, до гейта. Стенд: порядок секций теперь намеренный — часы идут ПОСЛЕ тяжёлых частей и служат сторожем починки потока (упадёт снова — увидим там). Новая проверка повторного MOX с ★негативным контролем: без гейта «было 3000, стало 0». Для неё в движке появилось диагностическое свойство TXMicFill. 276/276, test/cat 50/50, GUI (--ws=qt6) и демон собираются. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Bkwwyj7xVRrqnSVEseTRfV