YggOpenDocLogImpl.mesa
Copyright Ó 1985, 1988 by Xerox Corporation. All rights reserved.
Last edited by:
Taft on April 10, 1983 3:16 pm
MBrown on January 30, 1984 1:48:20 pm PST
Hauser, March 7, 1985 2:57:05 pm PST
Carl Hauser, October 15, 1987 3:30:05 pm PDT
Bob Hagmann May 13, 1988 8:36:03 am PDT
DIRECTORY
Basics USING [bytesPerWord],
CardTab USING [Create, Fetch, Ref, Store],
SymTab USING [Create, Fetch, Ref, Store],
YggEnvironment,
YggDID,
YggLog,
YggOpenDoc,
YggFileLockFormat,
YggOpenDocLog,
YggDIDMap,
YggFilePageMgr,
YggLogControl,
Rope USING [InlineFlatten, ROPE, Text];
YggOpenDocLogImpl: MONITOR
IMPORTS CardTab, SymTab, YggEnvironment, YggOpenDoc, YggOpenDocLog, YggDIDMap, YggFilePageMgr, YggLog, YggLogControl, Rope
EXPORTS YggDID, YggOpenDocLog
SHARES Rope =
BEGIN OPEN YggOpenDocLog;
YggOpenDocLog.
LogCreate: PUBLIC PROCEDURE [openDoc: YggOpenDoc.OpenDoc, initialSize: PageCount, owner: YggEnvironment.OwnerName] =
BEGIN
textOwner: Rope.Text = Rope.InlineFlatten[owner];
did: YggEnvironment.DID;
length: CARDINAL = textOwner.length;
textBlock: YggLog.Block ← [ base: BASE[DESCRIPTOR[textOwner.text]], length: (length+Basics.bytesPerWord-1)/Basics.bytesPerWord];
record: FileLogRecord[create] ← [ fileID: 0, specifics: create [initialSize: initialSize, owner: [length: length, text: NULL]]];
did ← YggOpenDoc.GetDID[openDoc];
record.fileID ← StoreDIDInMaps[did: did, id: 0];
[] ← YggLog.Write[trans: YggOpenDoc.GetTransHandle[openDoc], logRecordPhaseType: redo, recordType: create, recordData: [base: @record, length: SIZE[FileLogRecord[create]], rest: @textBlock], force: TRUE];
END;
LogDelete: PUBLIC PROCEDURE [openDoc: YggOpenDoc.OpenDoc] =
BEGIN
did: YggEnvironment.DID;
record: FileLogRecord[delete] ← [fileID: NULL, specifics: delete []];
record.fileID ← LookupIDInMap[did: YggOpenDoc.GetDID[openDoc]];
IF record.fileID = 0 THEN record.fileID ← StoreDIDInMaps[did: did, id: 0];
[] ← YggLog.Write[trans: YggOpenDoc.GetTransHandle[openDoc], logRecordPhaseType: redo, recordType: delete, recordData: [base: @record, length: SIZE[FileLogRecord[delete]]]];
END;
LogSetSize: PUBLIC PROCEDURE [openDoc: YggOpenDoc.OpenDoc, old, new: PageCount] =
BEGIN
did: YggEnvironment.DID;
record: FileLogRecord[setSize] ← [fileID: NULL, specifics: setSize [old: old, new: new]];
record.fileID ← LookupIDInMap[did: YggOpenDoc.GetDID[openDoc]];
IF record.fileID = 0 THEN record.fileID ← StoreDIDInMaps[did: did, id: 0];
[] ← YggLog.Write[trans: YggOpenDoc.GetTransHandle[openDoc], logRecordPhaseType: redo, recordType: setSize, recordData: [base: @record, length: SIZE[FileLogRecord[setSize]]]];
END;
LogWritePages: PUBLIC PROCEDURE [openDoc: YggOpenDoc.OpenDoc, where: LONG POINTER, pageRun: PageRun, referencePattern: ReferencePattern] RETURNS [recordID: LogRecordID] =
BEGIN
did: YggEnvironment.DID;
dataBlock: YggLog.Block ← [base: where, length: pageRun.count*YggEnvironment.wordsPerPage];
record: FileLogRecord[writePages] ← [fileID: NULL, specifics: writePages [pageRun: pageRun, referencePattern: referencePattern, data: NULL]];
record.fileID ← LookupIDInMap[did: YggOpenDoc.GetDID[openDoc]];
IF record.fileID = 0 THEN record.fileID ← StoreDIDInMaps[did: did, id: 0];
[thisRecord: recordID] ← YggLog.Write[trans: YggOpenDoc.GetTransHandle[openDoc], logRecordPhaseType: redo, recordType: writePages, recordData: [base: @record, length: SIZE[FileLogRecord[writePages]], rest: @dataBlock]];
END;
LogReadPages: PUBLIC PROCEDURE [openDoc: YggOpenDoc.OpenDoc, where: LONG POINTER, pageRun: PageRun, recordID: LogRecordID] RETURNS [referencePattern: ReferencePattern] =
BEGIN
did: YggEnvironment.DID;
header: FileLogRecord[writePages];
status: YggLog.ReadProcStatus;
[status: status] ← YggLog.Read[thisRecord: recordID, to: [base: @header, length: SIZE[FileLogRecord[writePages]]]];
IF status=sourceExhausted THEN ERROR; -- wrong length log record
did ← YggOpenDoc.GetDID[openDoc];
IF LookupIDInMap[did: did] # header.fileID THEN ERROR; -- wrong file
IF pageRun.firstPage<header.pageRun.firstPage OR pageRun.firstPage+pageRun.count>header.pageRun.firstPage+header.pageRun.count
THEN ERROR; -- pageRun is not a subinterval of the one contained in the log record
[status: status] ← YggLog.Read[thisRecord: recordID, wordsToSkip: SIZE[FileLogRecord[writePages]] + CARDINAL[pageRun.firstPage-header.pageRun.firstPage] * YggEnvironment.wordsPerPage, to: [base: where, length: pageRun.count * YggEnvironment.wordsPerPage]];
IF status=sourceExhausted THEN ERROR; -- log record not as long as it says it is
RETURN [header.referencePattern];
END;
LogFileLock: PUBLIC PROCEDURE [openDoc: YggOpenDoc.OpenDoc] =
BEGIN
did: YggEnvironment.DID;
lockSubID: YggFileLockFormat.FileLockSubID = [file[]];
record: FileLogRecord[lock] ← [fileID: NULL, specifics: lock [lockSubID: LOOPHOLE[lockSubID]]];
record.fileID ← LookupIDInMap[did: YggOpenDoc.GetDID[openDoc]];
IF record.fileID = 0 THEN record.fileID ← StoreDIDInMaps[did: did, id: 0];
[] ← YggLog.Write[trans: YggOpenDoc.GetTransHandle[openDoc], logRecordPhaseType: redo, recordType: lock, recordData: [base: @record, length: SIZE[FileLogRecord[lock]]]];
END;
Internal procedures
FileAnalysisProc: YggLogControl.AnalysisProc --[record, type, trans]-- =
TRUSTED BEGIN
header: FileLogRecord;
wordsRead: CARDINAL;
exists: BOOLEANTRUE;
status: YggLog.ReadProcStatus;
IF type NOT IN DefinedFileRecord THEN ERROR;
Attempt to read the longest possible FileLogRecord, and make sure it is at least as long as the shortest possible FileLogRecord.
[status, wordsRead] ← YggLog.ReadForRecovery[
thisRecord: record, to: [base: @header, length: SIZE[FileLogRecord]]];
IF status = destinationFull THEN ERROR;
IF wordsRead<SIZE[FileLogRecord[delete]] THEN ERROR; -- log record too short
WITH r: header SELECT type FROM
setSize =>
BEGIN
fileHandle: YggDIDMap.Document ← YggDIDMap.Register[did: LookupDIDInMap[header.fileID]];
ssl: SetSizeList ← CONS[[r.old, r.new, trans, record, fileHandle], NARROW[YggDIDMap.GetRecoveryData[fileHandle]]];
IF wordsRead<SIZE[FileLogRecord[setSize]] THEN ERROR; -- log record too short
YggDIDMap.SetRecoveryData[fileHandle, ssl];
END;
ENDCASE => NULL;
END;
FileRecoveryProc: YggLog.RecoveryProc --[record, type, trans, outcome]-- = TRUSTED BEGIN
openDoc: YggOpenDoc.OpenDoc;
header: FileLogRecord;
wordsRead: CARDINAL;
exists: BOOLEANTRUE;
IF type NOT IN DefinedFileRecord THEN ERROR;
Attempt to read the longest possible FileLogRecord, and make sure it is at least as long as the shortest possible FileLogRecord.
[wordsRead: wordsRead] ← YggLog.ReadForRecovery[thisRecord: record, to: [base: @header, length: SIZE[FileLogRecord]]];
IF wordsRead<SIZE[FileLogRecord[delete]] THEN ERROR; -- log record too short
openDoc ← YggOpenDoc.Register[trans: trans, did: LookupDIDInMap[header.fileID]];
Recover the update only if the file still exists, under the assumption that if it does not exist then it must have been deleted by some later committed transaction. GetSize is an efficient way to check for the existence of a file because YggFilePageMgr caches the file's existence and size.
[] ← YggFilePageMgr.GetSize[YggOpenDoc.GetDocHandle[openDoc] !
YggFilePageMgr.NoSuchFile => {exists ← FALSE; CONTINUE};
Crock
File.Error => {IF type=delete THEN {exists ← FALSE; CONTINUE} ELSE REJECT}
];
IF exists THEN WITH r: header SELECT type FROM
create =>
BEGIN
owner is not actually needed for recovery, so omit this:
owner: OwnerName;
Fetch: SAFE PROCEDURE RETURNS [c: CHAR] = TRUSTED
{c ← r.owner.text[index]; index ← index+1};
index: CARDINAL ← 0;
IF wordsRead<SIZE[FileLogRecord[create]] THEN ERROR; -- log record too short
owner ← Rope.FromProc[len: r.owner.length, p: Fetch];
RecoverCreate[openDoc: openDoc, initialSize: r.initialSize, outcome: outcome];
END;
delete =>
BEGIN
IF wordsRead<SIZE[FileLogRecord[delete]] THEN ERROR; -- log record too short
RecoverDelete[openDoc: openDoc, outcome: outcome];
END;
setSize =>
BEGIN
IF wordsRead<SIZE[FileLogRecord[setSize]] THEN ERROR; -- log record too short
RecoverSetSize[openDoc: openDoc, old: r.old, new: r.new, recordID: record, outcome: outcome];
END;
writePages =>
BEGIN
IF wordsRead<SIZE[FileLogRecord[writePages]] THEN ERROR; -- log record too short
RecoverWritePages[openDoc: openDoc, recordID: record, pageRun: r.pageRun, outcome: outcome];
END;
lock =>
BEGIN
IF wordsRead<SIZE[FileLogRecord[lock]] THEN ERROR; -- log record too short
RecoverLock[openDoc: openDoc, lockSubID: r.lockSubID, outcome: outcome];
END;
ENDCASE => ERROR;
Unregister to balance the above Register, so the openDoc use count will be zero when recovery finished. Pretend that an update has occurred (even if it hasn't) so that the openDoc will stay around.
YggOpenDoc.SetMaxDeltaVersion[openDoc, 1];
YggOpenDoc.Unregister[openDoc];
END;
DID <-> log ID mapping.
Use the DID when we get off of UNIX/Cedar file systems. Remove this code then.
ROPE: TYPE ~ Rope.ROPE;
DID: PUBLIC TYPE ~ REF DIDRep;
DIDRep: PUBLIC TYPE ~ ROPE;
For Phase 0, the DIDRep is just a string that names a directory (without the trailing /).
didToIDMap: SymTab.Ref ← NIL;
idToDIDMap: CardTab.Ref ← NIL;
nextID: CARD ← 1000;
LookupIDInMap: ENTRY PROC [did: DID] RETURNS [id: CARD ← 0] ~ {
Given a DID, return the log id. A 0 return means not found.
found: BOOL;
val: REF;
DO
[found, val] ← SymTab.Fetch[x: didToIDMap, key: did^];
IF found THEN {
entry: REF CARD;
entry ← NARROW[val];
RETURN[entry^];
};
ENDLOOP;
};
LookupDIDInMap: ENTRY PROC [id: CARD] RETURNS [did: DID ← NIL] ~ {
Given an ID, return the DID. A NIL return means not found.
found: BOOL;
val: REF;
DO
[found, val] ← CardTab.Fetch[x: idToDIDMap, key: id];
IF found THEN {
entry: DIDRep;
entry ← NARROW[val];
did ← NEW[DIDRep];
did^ ← entry;
};
ENDLOOP;
};
StoreDIDInMaps: ENTRY PROC [did: DID, id: CARD] RETURNS [idUsed: CARD]~ {
Add this did and id.
IF id = 0 THEN {idUsed ← nextID; nextID ← nextID + 1;} ELSE idUsed ← id;
[] ← SymTab.Store[x: didToIDMap, key: did^, val: NEW[CARD ← idUsed]];
[] ← CardTab.Store[x: idToDIDMap, key: idUsed, val: NEW[ROPE ← did^]];
};
Module initialization
FOR type: DefinedFileRecord IN DefinedFileRecord DO
YggLog.RegisterRecoveryProc[type, FileRecoveryProc];
ENDLOOP;
YggLogControl.RegisterAnalysisProc[setSize, FileAnalysisProc];
didToIDMap ← SymTab.Create[mod: 129, case: TRUE];
idToDIDMap ← CardTab.Create[mod: 129];
END.
Hauser, March 7, 1985 2:56:28 pm PST
Added copyright.
Carl Hauser, October 4, 1985 1:27:39 pm PDT
Change "Log" to "AlpineLog", DIRECTORY