mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 17:27:32 +00:00
fix(tci): пара «клиент+приёмник» только по факту передачи; перебор имён .part
[P1] FTxClient/FTxRx писались ДО Invoke, и команда, которая ничего не сделала,
всё равно их перебивала. Клиент, уже передающий с приёмника 1, шлёт
trx:0,true,tci — передатчик занят, SyncSetTRX не делает ничего (Started=False),
а маркеры ИДУЩЕЙ передачи с этого мига уходят под номером 0. MSHV такие блоки
отбрасывает (network.cpp:231), то есть передача просто замолкает. Теперь пара
назначается после Invoke и только при Started — тем же признаком, по которому
назначается хозяин эфира. Тот же гейт закрывает близнеца: trx:<N>,true без
',tci' поверх своей же передачи больше не снимает источник модуляции. Снятие
(',false') работает как прежде.
[P2] Имя временного файла (pid + счётчик) уникально внутри процесса, но не
между запусками: «.part», оставшийся от прошлой жизни (публиковать было
нечем), плюс повторно выданный системой pid дают EEXIST на создании — и
задание пропадало молча. TCICreateTempNear перебирает до 64 имён, но только
пока ошибка — «имя занято»: нет прав или каталога перебором не лечится.
Стенд test/tci: 219 проверок (было 217). Новое — занятое имя «.part» записи не
теряет (стенд занимает ровно то имя, которое возьмёт писатель) и пустая
команда TRX не меняет номер приёмника в маркерах. Негативный контроль на обе
правки. Попутно в тесте поправлены два комментария, описывавшие прежнюю
реализацию публикации.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+38
-3
@@ -353,6 +353,11 @@ function TCIRecordPath(const BaseDir, Req: string): string;
|
||||
имя приходит из сети. THandle(-1) — не вышло. }
|
||||
function TCICreateNewFile(const Path: string): THandle;
|
||||
|
||||
{ Очередное имя временного файла рядом с целью: <путь>.<pid>-<N>.part. Каждый
|
||||
вызов даёт следующее. Наружу открыто ради стенда — ему нужно занять ровно то
|
||||
имя, которое возьмёт писатель. }
|
||||
function TCIRecTempName(const Path: string): string;
|
||||
|
||||
{ Погасить писателя: закрыть приём заданий, дождаться ВСЕЙ очереди и
|
||||
освободить объект (Destroy = Terminate + WaitFor). Зовут при гибели
|
||||
адаптера, ради этого писатель и стал управляемым. W обнуляется в любом
|
||||
@@ -972,6 +977,37 @@ begin
|
||||
[Path, Integer(GetProcessID), InterLockedIncrement(RecTmpSeq)]);
|
||||
end;
|
||||
|
||||
function TCILastErrorIsExists: Boolean;
|
||||
// «Такое имя уже есть» — единственная ошибка создания, которую лечит другое имя.
|
||||
begin
|
||||
{$IFDEF UNIX}
|
||||
Result := fpGetErrno = ESysEEXIST;
|
||||
{$ELSE}
|
||||
Result := (GetLastError = ERROR_ALREADY_EXISTS) or
|
||||
(GetLastError = ERROR_FILE_EXISTS);
|
||||
{$ENDIF}
|
||||
end;
|
||||
|
||||
function TCICreateTempNear(const Path: string; out Tmp: string): THandle;
|
||||
// Временный файл рядом с целью, с перебором имён.
|
||||
// ★pid + счётчик уникальны внутри процесса, но не между запусками: «.part»,
|
||||
// оставшийся от прошлой жизни (публиковать было нечем — см. TCIPublishFile),
|
||||
// плюс повторно выданный системой pid дают EEXIST, и задание пропадало бы
|
||||
// молча. Занятое имя — не повод терять запись, берём следующее.
|
||||
var i: Integer;
|
||||
begin
|
||||
for i := 1 to 64 do
|
||||
begin
|
||||
Tmp := TCIRecTempName(Path);
|
||||
Result := TCICreateNewFile(Tmp);
|
||||
if Result <> THandle(-1) then Exit;
|
||||
// Не «имя занято» (нет прав, нет каталога) — перебор не поможет.
|
||||
if not TCILastErrorIsExists then Break;
|
||||
end;
|
||||
Tmp := '';
|
||||
Result := THandle(-1);
|
||||
end;
|
||||
|
||||
function TCIWriteAll(H: THandle; const Buf; Count: Integer): Boolean;
|
||||
// ★Результат записи проверяем, и не «= Count», а циклом. FileWrite (как и
|
||||
// THandleStream.Write под ним) возвращает ЧИСЛО записанных байт и при ошибке
|
||||
@@ -1276,9 +1312,8 @@ begin
|
||||
// Так клиент, увидевший файл, всегда прав: раньше по имени сначала
|
||||
// появлялась пустышка на 0 байт, и на медленном диске её было видно всю
|
||||
// запись, а до того — огрызок с заголовком на полную длину.
|
||||
Ok := False;
|
||||
Tmp := TCIRecTempName(J.Path);
|
||||
HT := TCICreateNewFile(Tmp);
|
||||
Ok := False;
|
||||
HT := TCICreateTempNear(J.Path, Tmp);
|
||||
if HT <> THandle(-1) then
|
||||
begin
|
||||
try
|
||||
|
||||
Reference in New Issue
Block a user