DIRECTORY AlpineEnvironment, Basics USING [bytesPerWord], FileInstance, FileLockFormat, FileLog, FilePageMgr, Log, Rope USING [InlineFlatten, Text]; FileLogImpl: PROGRAM IMPORTS FileInstance, FileLog, FilePageMgr, Log, Rope EXPORTS FileLog SHARES Rope = BEGIN OPEN FileLog; LogCreate: PUBLIC PROCEDURE [fileInstance: FileInstance.Handle, initialSize: PageCount, owner: AlpineEnvironment.OwnerName] = BEGIN textOwner: Rope.Text = Rope.InlineFlatten[owner]; length: CARDINAL = textOwner.length; textBlock: Log.Block _ [ base: BASE[DESCRIPTOR[textOwner.text]], length: (length+Basics.bytesPerWord-1)/Basics.bytesPerWord]; record: FileLogRecord[create] _ [ volumeID: NULL, fileID: NULL, specifics: create [initialSize: initialSize, owner: [length: length, text: NULL]]]; [volumeID: record.volumeID, fileID: record.fileID] _ FileInstance.GetVolumeIDAndFileID[fileInstance]; [] _ Log.Write[trans: FileInstance.GetTransHandle[fileInstance], recordType: create, recordData: [base: @record, length: SIZE[FileLogRecord[create]], rest: @textBlock], force: TRUE]; END; LogDelete: PUBLIC PROCEDURE [fileInstance: FileInstance.Handle] = BEGIN record: FileLogRecord[delete] _ [volumeID: NULL, fileID: NULL, specifics: delete []]; [volumeID: record.volumeID, fileID: record.fileID] _ FileInstance.GetVolumeIDAndFileID[fileInstance]; [] _ Log.Write[trans: FileInstance.GetTransHandle[fileInstance], recordType: delete, recordData: [base: @record, length: SIZE[FileLogRecord[delete]]]]; END; LogSetSize: PUBLIC PROCEDURE [fileInstance: FileInstance.Handle, old, new: PageCount] = BEGIN record: FileLogRecord[setSize] _ [volumeID: NULL, fileID: NULL, specifics: setSize [old: old, new: new]]; [volumeID: record.volumeID, fileID: record.fileID] _ FileInstance.GetVolumeIDAndFileID[fileInstance]; [] _ Log.Write[trans: FileInstance.GetTransHandle[fileInstance], recordType: setSize, recordData: [base: @record, length: SIZE[FileLogRecord[setSize]]]]; END; LogWritePages: PUBLIC PROCEDURE [fileInstance: FileInstance.Handle, where: LONG POINTER, pageRun: PageRun, referencePattern: ReferencePattern] RETURNS [recordID: LogRecordID] = BEGIN dataBlock: Log.Block _ [base: where, length: pageRun.count*AlpineEnvironment.wordsPerPage]; record: FileLogRecord[writePages] _ [volumeID: NULL, fileID: NULL, specifics: writePages [pageRun: pageRun, referencePattern: referencePattern, data: NULL]]; [volumeID: record.volumeID, fileID: record.fileID] _ FileInstance.GetVolumeIDAndFileID[fileInstance]; [thisRecord: recordID] _ Log.Write[trans: FileInstance.GetTransHandle[fileInstance], recordType: writePages, recordData: [base: @record, length: SIZE[FileLogRecord[writePages]], rest: @dataBlock]]; END; LogReadPages: PUBLIC PROCEDURE [fileInstance: FileInstance.Handle, where: LONG POINTER, pageRun: PageRun, recordID: LogRecordID] RETURNS [referencePattern: ReferencePattern] = BEGIN header: FileLogRecord[writePages]; status: Log.ReadProcStatus; [status: status] _ Log.Read[thisRecord: recordID, to: [base: @header, length: SIZE[FileLogRecord[writePages]]]]; IF status=sourceExhausted THEN ERROR; -- wrong length log record IF FileInstance.GetFileID[fileInstance] # 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] _ Log.Read[thisRecord: recordID, wordsToSkip: SIZE[FileLogRecord[writePages]] + CARDINAL[pageRun.firstPage-header.pageRun.firstPage] * AlpineEnvironment.wordsPerPage, to: [base: where, length: pageRun.count * AlpineEnvironment.wordsPerPage]]; IF status=sourceExhausted THEN ERROR; -- log record not as long as it says it is RETURN [header.referencePattern]; END; LogWriteLeaderPage: PUBLIC PROCEDURE [fileInstance: FileInstance.Handle, where: LONG POINTER] RETURNS [recordID: LogRecordID] = BEGIN dataBlock: Log.Block _ [base: where, length: AlpineEnvironment.wordsPerPage]; record: FileLogRecord[writeLeaderPage] _ [volumeID: NULL, fileID: NULL, specifics: writeLeaderPage [data: NULL]]; [volumeID: record.volumeID, fileID: record.fileID] _ FileInstance.GetVolumeIDAndFileID[fileInstance]; [thisRecord: recordID] _ Log.Write[trans: FileInstance.GetTransHandle[fileInstance], recordType: writeLeaderPage, recordData: [base: @record, length: SIZE[FileLogRecord[writeLeaderPage]], rest: @dataBlock]]; END; LogReadLeaderPage: PUBLIC PROCEDURE [fileInstance: FileInstance.Handle, where: LONG POINTER, recordID: LogRecordID] = BEGIN header: FileLogRecord[writeLeaderPage]; status: Log.ReadProcStatus; [status: status] _ Log.Read[thisRecord: recordID, to: [base: @header, length: SIZE[FileLogRecord[writeLeaderPage]]]]; IF status=sourceExhausted THEN ERROR; -- wrong length log record IF FileInstance.GetFileID[fileInstance] # header.fileID THEN ERROR; -- wrong file [status: status] _ Log.Read[thisRecord: recordID, wordsToSkip: SIZE[FileLogRecord[writeLeaderPage]], to: [base: where, length: AlpineEnvironment.wordsPerPage]]; IF status#normal THEN ERROR; -- log record not as long as it says it is END; LogFileLock: PUBLIC PROCEDURE [fileInstance: FileInstance.Handle] = BEGIN lockSubID: FileLockFormat.FileLockSubID = [file[]]; record: FileLogRecord[lock] _ [volumeID: NULL, fileID: NULL, specifics: lock [lockSubID: LOOPHOLE[lockSubID]]]; [volumeID: record.volumeID, fileID: record.fileID] _ FileInstance.GetVolumeIDAndFileID[fileInstance]; [] _ Log.Write[trans: FileInstance.GetTransHandle[fileInstance], recordType: lock, recordData: [base: @record, length: SIZE[FileLogRecord[lock]]]]; END; FileRecoveryProc: Log.RecoveryProc --[record, type, trans, outcome]-- = BEGIN fileInstance: FileInstance.Handle; header: FileLogRecord; wordsRead: CARDINAL; exists: BOOLEAN _ TRUE; IF type NOT IN DefinedFileRecord THEN ERROR; [wordsRead: wordsRead] _ Log.ReadForRecovery[thisRecord: record, to: [base: @header, length: SIZE[FileLogRecord]]]; IF wordsRead {exists _ FALSE; CONTINUE}]; IF exists THEN WITH r: header SELECT type FROM create => BEGIN IF wordsRead BEGIN IF wordsRead BEGIN IF wordsRead BEGIN IF wordsRead BEGIN IF wordsRead BEGIN IF wordsRead ERROR; FileInstance.SetMaxDeltaVersion[fileInstance, 1]; FileInstance.Unregister[fileInstance]; END; FOR type: DefinedFileRecord IN DefinedFileRecord DO Log.RegisterRecoveryProc[type, FileRecoveryProc]; ENDLOOP; END.  FileLogImpl.mesa Last edited by: Taft on April 10, 1983 3:16 pm MBrown on January 30, 1984 1:48:20 pm PST FileLog. Internal procedures 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 FilePageMgr caches the file's existence and size. 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 fileInstance use count will be zero when recovery finished. Pretend that an update has occurred (even if it hasn't) so that the fileInstance will stay around. Module initialization Κ/– "cedar" style˜Jšœ™šœ™Jšœ™Jšœ)™)—unitšΟk ˜ Icode˜Lšœœ˜L˜ L˜L˜L˜ L˜Lšœœ˜!—šœ ˜Lšœ.˜5Lšœ˜Lšœ˜ Lšœœ ˜—J™™šΟn œœ œb˜}Lš˜L˜1Lšœœ˜$Lšœœœ œO˜}Lšœ,œ œMœ˜“L˜eLšœyœ+œœ˜ΆLšœ˜—šž œœ œ&˜ALš˜Lšœ+œ œ˜UL˜eLšœyœ˜—Lšœ˜—šž œœ œ;˜WLš˜Lšœ,œ œ+˜iL˜eLšœzœ˜™Lšœ˜—š ž œœ œ,œœ7œ˜°Lš˜L˜[Lšœ/œ œUœ˜L˜eLšœ‘œ0˜ΕLšœ˜—š ž œœ œ,œœ+œ'˜―Lš˜L˜"L˜LšœNœ˜pLšœœœΟc˜ALšœ6œœŸ ˜Ršœ,œN˜~Lš œŸF˜S—Lšœ?œ œš˜ƒLšœœœŸ*˜QLšœ˜!Lšœ˜—š žœœ œ,œœœ˜Lš˜L˜MLšœ4œ œ$œ˜qL˜eLšœ–œ5˜ΟLšœ˜—š žœœ œ,œœ˜uLš˜L˜'L˜LšœNœ#˜uLšœœœŸ˜ALšœ6œœŸ ˜RLšœ?œ]˜ LšœœœŸ*˜HLšœ˜—šž œœ œ&˜CLš˜L˜3Lšœ)œ œœ˜oL˜eLšœwœ˜“Lšœ˜——J˜J˜šœ™šžœŸ"œ˜GLš˜L˜"L˜Lšœ œ˜Lšœœœ˜Lš œœœœœ˜,Jšœ€™€Lšœ]œ˜sLš œ œœœŸ˜ML˜eJšœŸ™Ÿ˜CLšœ$œœ˜6—š œœœ œ˜.˜ Lš˜šœ8™8Jšœ™šœœœ™1Jšœ+™+—Jšœœ™—š œ œœœŸ˜MJšœ5™5—L˜XLšœ˜—˜ Lš˜Lš œ œœœŸ˜ML˜