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; 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.firstPageheader.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; FileAnalysisProc: YggLogControl.AnalysisProc --[record, type, trans]-- = TRUSTED BEGIN header: FileLogRecord; wordsRead: CARDINAL; exists: BOOLEAN _ TRUE; status: YggLog.ReadProcStatus; IF type NOT IN DefinedFileRecord THEN ERROR; [status, wordsRead] _ YggLog.ReadForRecovery[ thisRecord: record, to: [base: @header, length: SIZE[FileLogRecord]]]; IF status = destinationFull THEN ERROR; IF wordsRead 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 NULL; END; FileRecoveryProc: YggLog.RecoveryProc --[record, type, trans, outcome]-- = TRUSTED BEGIN openDoc: YggOpenDoc.OpenDoc; header: FileLogRecord; wordsRead: CARDINAL; exists: BOOLEAN _ TRUE; IF type NOT IN DefinedFileRecord THEN ERROR; [wordsRead: wordsRead] _ YggLog.ReadForRecovery[thisRecord: record, to: [base: @header, length: SIZE[FileLogRecord]]]; IF wordsRead BEGIN IF wordsRead BEGIN IF wordsRead BEGIN IF wordsRead BEGIN IF wordsRead BEGIN IF wordsRead ERROR; YggOpenDoc.SetMaxDeltaVersion[openDoc, 1]; YggOpenDoc.Unregister[openDoc]; END; ROPE: TYPE ~ Rope.ROPE; DID: PUBLIC TYPE ~ REF DIDRep; DIDRep: PUBLIC TYPE ~ ROPE; didToIDMap: SymTab.Ref _ NIL; idToDIDMap: CardTab.Ref _ NIL; nextID: CARD _ 1000; LookupIDInMap: ENTRY PROC [did: DID] RETURNS [id: CARD _ 0] ~ { 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] ~ { 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]~ { 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^]]; }; 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. ώ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 YggOpenDocLog. Internal procedures Attempt to read the longest possible FileLogRecord, and make sure it is at least as long as the shortest possible FileLogRecord. Attempt to read the longest possible FileLogRecord, and make sure it is at least as long as the shortest possible FileLogRecord. 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} ]; 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; owner _ Rope.FromProc[len: r.owner.length, p: Fetch]; 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. DID <-> log ID mapping. Use the DID when we get off of UNIX/Cedar file systems. Remove this code then. For Phase 0, the DIDRep is just a string that names a directory (without the trailing /). Given a DID, return the log id. A 0 return means not found. Given an ID, return the DID. A NIL return means not found. Add this did and id. Module initialization 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 Κ – "cedar" style˜šœ™IcodešœB™B—šœ™Jšœ™Jšœ)™)K™$K™,K™'—unitšΟk ˜ Kšœœ˜Kšœœ˜*Kšœœ˜)Kšœ˜K˜K˜K˜ Kšœ˜Kšœ˜K˜ Kšœ˜Kšœ˜Kšœœ˜'—šœ˜Kšœs˜zKšœ˜Kšœ˜ Kšœœ˜—J™šœ™šΟn œœ œY˜tKš˜K˜1Kšœ˜Kšœœ˜$Kšœœœ œO˜€Kšœ*œMœ˜€K˜!Kšœ0˜0Kšœœ+œœ˜ΜKšœ˜—šž œœ œ ˜;Kš˜Kšœ˜Kšœ)œ˜EKšœ?˜?KšœJ˜JKšœœ˜­Kšœ˜—šž œœ œ5˜QKš˜Kšœ˜Kšœ*œ+˜YKšœ?˜?KšœJ˜JKšœœ˜―Kšœ˜—š ž œœ œ&œœ7œ˜ͺKš˜Kšœ˜Kšœ[˜[Kšœ-œUœ˜Kšœ?˜?KšœJ˜JKšœ§œ0˜ΫKšœ˜—š ž œœ œ&œœ+œ'˜©Kš˜Kšœ˜K˜"K˜KšœQœ˜sKšœœœΟc˜AKšœ!˜!Kšœ)œœŸ ˜Ešœ,œN˜~Kš œŸF˜S—KšœBœ œ”˜€KšœœœŸ*˜QKšœ˜!Kšœ˜—šž œœ œ ˜=Kš˜Kšœ˜Kšœ6˜6Kšœ'œœ˜_Kšœ?˜?KšœJ˜JKšœœ˜©Kšœ˜——J˜J˜šœ™šžœŸœ˜HKš ˜ K˜Kšœ œ˜Kšœœœ˜Kšœ˜Kš œœœœœ˜,Jšœ€™€Kšœ^œ˜uJšœœœ˜'Kš œ œœœŸ˜Mšœ œ˜˜ Kš˜KšœX˜XKšœœ,œ)˜rKš œ œœœŸ˜NKšœ+˜+Kšœ˜—Kšœœ˜—Kšœ˜—šžœŸ"œ˜XKšœ˜K˜Kšœ œ˜Kšœœœ˜Kš œœœœœ˜,Jšœ€™€Kšœ`œ˜vKš œ œœœŸ˜MKšœP˜PJšœ’™’šœ>™>Kšœ'œœ™8K™Kš œœ œ œœœœ™JKšœ™—š œœœ œ˜.˜ Kš˜šœ8™8Jšœ™šœœœ™1Jšœ+™+—Jšœœ™—š œ œœœŸ˜MJšœ5™5—K˜NKšœ˜—˜ Kš˜Kš œ œœœŸ˜MK˜2Kšœ˜—˜ Kš˜Kš œ œœœŸ˜NK˜]Kšœ˜—˜ Kš˜Kš œ œœœŸ˜QK˜\Kšœ˜—˜Kš˜Kš œ œœœŸ˜KK˜HKšœ˜—Kšœœ˜—JšœΖ™ΖK˜*K˜Kšœ˜——J˜J˜head™K™OK™K™Kšœœœ˜K˜Kšœ œœ˜šœ œœ˜K™Y—K˜Kšœœ˜K˜Kšœœ˜K˜K˜K˜š ž œœœœœœ˜?Kšœœ1™˜>Kšœ+œ˜1Kšœ&˜&Lšœ˜—™$K™—™+KšœΟr ™&—K™K™—…— ζ2