Коллеги,
у меня завелась ошибка: иногда после закрытия документа и повторного открытия (в рамках одного запуска ББ) сохраненные изменения пропадали. Т.е. открываешь, изменяешь, закрываешь, открываешь - бац! изменения пропали. При этом в файле - измененный документ (видно через odcread). Немало времени и ядреных слов я потратил на восстановление пропащих авторских правок.
В чем же дело?
ББ гарантирует, что одному файлу соответствует один и тот же Files.File; зная это, я предполагал, что где-то в системе нет-нет, да и притаится паразитный указатель на файл, который не ликвидируется после закрытия окна с документом. У меня в ББ много "примочек" к редактору, которые кэшируют читателей TextModels.Reader (а внутри них есть иногда указатель на файл, из которого прочитан текст) и сканеры TextMappers.Scanner (а внутри них есть читатель). Я перерыл свои модули, перепроверил кэши, все аккуратно очистил - безрезультатно.
Кажется, ошибка была в TextModels. Процедура PROCEDURE (text: TextModels.Model) NewReader (rd: TextModels.Reader) позволяет, для экономии памяти, передать rd, который будет переиспользован - т.е. можно взять чтеца, попользоваться, потом переподключить его к другому тексту и пользоваться дальше. Это описано в документации и немало используется в ББ. И вот я выяснил, что при переподключении важное поле Reader.run не переинициализируется. Это видно из анализа процедур TextModels.StdModel.NewReader и TextModels.StdReader.SetPos. А именно Reader.run после чтения текста из файла содержит указатель на TextModels.Piece, который содержит указатель на Files.File - это как раз и получается "фантомная" файловая переменная, которая незримо "висит" и не дает ББ видеть обновленную версию файла.
Вот проверочный модуль. Он пишет в журнал имя "зацепленного" файла, а также показывает стек вызовов, в котором можно также убедиться в сохранении лишнего указателя.
Код:
MODULE BugReaders;
IMPORT SYSTEM, TextModels, Views, Models, Files, Services, Log;
TYPE
Reader = POINTER TO RECORD
eot*: BOOLEAN;
attr*: INTEGER;
char*: CHAR;
view*: Views.View;
w*, h*: INTEGER;
base: INTEGER; (* base = Base() *)
pos: INTEGER; (* pos = Pos() *)
era: INTEGER;
run: ANYPTR; (* era = base.era => Pos(run) + off = pos *)
off: INTEGER; (* era = base.era => 0 <= off < run.len *)
reader: Files.Reader (* file reader cache *)
END;
Piece = POINTER TO RECORD
prev, next, len, attr: INTEGER;
file: Files.File
END;
File = POINTER TO RECORD
type-: Files.Type;
init: BOOLEAN;
state: INTEGER;
name: ARRAY 256 OF CHAR;
END;
PROCEDURE Hack (rd: TextModels.Reader);
VAR nm: ARRAY 64 OF CHAR; rdr: Reader; pc: Piece; f: File;
BEGIN
Services.GetTypeName(rd, nm);
IF nm # "TextModels.StdReader" THEN Log.String("~(rd IS TextModels.StdReader"); Log.Ln
ELSE
rdr := SYSTEM.VAL(Reader, rd);
IF rdr.run = NIL THEN Log.String(".run = NIL"); Log.Ln
ELSE
Services.GetTypeName(rdr.run, nm);
IF nm # "TextModels.Piece" THEN Log.String("~(rd.run IS TextModels.Piece"); Log.Ln
ELSE
pc := SYSTEM.VAL(Piece, rdr.run);
IF pc.file = NIL THEN Log.String("rd.run.file = NIL"); Log.Ln
ELSE
f := SYSTEM.VAL(File, pc.file);
Log.String("rd.run.file.name = "); Log.String(f.name); Log.Ln
END
END
END
END
END Hack;
PROCEDURE Do*;
VAR rd: TextModels.Reader; t: Models.Model; v: Views.View;
BEGIN
v := Views.OldView(Files.dir.This('System/Rsrc/'), 'Menus.odc');
t := v.ThisModel();
WITH t: TextModels.Model DO
rd := TextModels.dir.New().NewReader(rd);
Hack(rd);
rd := t.NewReader(NIL); rd.Read;
Hack(rd);
rd := TextModels.dir.New().NewReader(rd);
Hack(rd);
HALT(0)
END
END Do;
END BugReaders.
(!) BugReaders.Do
После BugReaders.Do в журнале - дважды имя файла System/Rsrc/Menus.odc, хотя должно быть лишь один.
А вот поправка в TextModels.StdModel.NewReader, которая, кажется, должна и исправляет ошибку:
Код:
PROCEDURE (t: StdModel) NewReader (old: Reader): Reader;
VAR rd: StdReader;
BEGIN
StdInit(t);
IF (old # NIL) & (old IS StdReader) THEN rd := old(StdReader) ELSE NEW(rd) END;
IF rd.base # t THEN
rd.base := t; rd.era := -1; (*==>*) rd.run := NIL; (*<==*) rd.SetPos(0)
ELSIF rd.pos > t.len THEN
rd.SetPos(t.len)
END;
rd.eot := FALSE;
RETURN rd
END NewReader;
С этой поправкой BugReaders.Do, как и ожидается, выдает имя файла System/Rsrc/Menus.odc только один раз. И у меня вроде пропали фантомные файлы.
Что скажете? Прошу поразмыслить, попробовать, и в конечном итоге - предлагаю включить поправку в ББ.