DIRECTORY Basics, ConstArith, ConvertUnsafe, YggDummyProcess, Rope, SafeStorage, YggCoordinator, YggEnvironment, YggImport, YggInline, YggInternal, YggLog, YggLogBasic, YggLogControl, YggLogInline, YggLogRep, YggRecoveryPassProcs, YggRestartFile, YggWorker, YggTransactionMap; YggLogImpl: CEDAR MONITOR IMPORTS ConstArith, ConvertUnsafe, YggDummyProcess, SafeStorage, YggImport, YggInline, YggLog, YggLogBasic, YggLogInline, YggRecoveryPassProcs, YggRestartFile, YggTransactionMap EXPORTS YggEnvironment, YggInternal, YggLog, YggLogControl = BEGIN VolumeID: TYPE = YggEnvironment.VolumeID; FileStore: TYPE = YggEnvironment.FileStore; TransID: TYPE = YggEnvironment.TransID; nullTransID: TransID = YggEnvironment.nullTransID; PageNumber: TYPE = YggEnvironment.PageNumber; PageCount: TYPE = YggEnvironment.PageCount; WordNumber: TYPE = YggLogBasic.WordNumber; WordCount: TYPE = YggLogBasic.WordCount; RecordID: TYPE = YggLog.RecordID; nullRecordID: RecordID = YggLog.nullRecordID; Block: TYPE = YggLog.Block; RecordType: TYPE = YggLog.RecordType; LogRecordPhaseType: TYPE = YggLog.LogRecordPhaseType; ClientProgrammingError: ERROR = CODE; InternalProgrammingError: ERROR = CODE; WriteFailed: PUBLIC ERROR = CODE; TransObject: PUBLIC TYPE = YggWorker.Object; logDeviceCharacteristics: PUBLIC YggEnvironment.DeviceCharacteristics; wordsPerPage: PUBLIC CARD; -- words in a Log page bytesPerPage: PUBLIC CARD; -- bytes in a Log page lastRecordWithUndo: RecordID _ nullRecordID; Format: PUBLIC PROC [ logVolume: VolumeID, logFile, restartFile: YggEnvironment.DID, myFileStore: FileStore] = { logSize: PageCount; thisCheckpointBeginRecord: RecordID; YggLogBasic.EstablishLogFile[logFile]; logSize _ YggLogBasic.LogFileSize[]; logDeviceCharacteristics _ YggImport.DeviceCharacteristicsForFileStore[myFileStore]; wordsPerPage _ logDeviceCharacteristics.wordsPerPage; bytesPerPage _ wordsPerPage * Basics.bytesPerWord; YggRestartFile.EstablishRestartFile[restartFile]; YggLogBasic.OpenForPut[nextPage: 0, version: 0, nextRecord: nullRecordID]; YggLogBasic.Release[beforeRecord: nullRecordID]; YggLogBasic.Release[beforeRecord: WriteNoopRecords[logSize/2].followingRecord]; [] _ WriteNoopRecords[logSize - logSize/2]; thisCheckpointBeginRecord _ YggLogBasic.RecordIDOfNextPut[]; IF YggLogBasic.WordNumberFromRecordID[thisCheckpointBeginRecord] # YggLog.nullRecordID THEN ERROR InternalProgrammingError; YggLogBasic.Release[beforeRecord: thisCheckpointBeginRecord]; [] _ WriteCheckpointCompleteRecord[ startAnalysisRecord: thisCheckpointBeginRecord, keepRecord: thisCheckpointBeginRecord, length: 128, -- fix this myFileStore: myFileStore]; YggLogBasic.CloseForPut[]; }; WriteNoopRecords: PUBLIC PROC [nPages: PageCount] RETURNS [followingRecord: RecordID] = { noopRecordBody: YggLogRep.NoopRecord _ []; FOR page: PageCount _ 0, page+1 UNTIL page = nPages DO TRUSTED{[followingRecord: followingRecord] _ YggLogBasic.Put[from: [base: @noopRecordBody, length: SIZE[YggLogRep.NoopRecord]]];}; ENDLOOP; RETURN [followingRecord]; }; WriteCheckpointCompleteRecord: PROC [ startAnalysisRecord, keepRecord: RecordID, length: INT, myFileStore: FileStore] RETURNS [thisRecord: RecordID] = { checkpointCompleteRecordBody: YggLogRep.CheckpointCompleteRecord _ [startAnalysisRecordID: startAnalysisRecord, keepRecordID: keepRecord]; TRUSTED { ConvertUnsafe.AppendRope[ to: LOOPHOLE[LONG[@(checkpointCompleteRecordBody.myFileStore)]], from: myFileStore]; [thisRecord: thisRecord] _ YggLogBasic.Put[from: [base: @checkpointCompleteRecordBody, length: length], writeID: TRUE, force: TRUE]; }; YggRestartFile.WriteRestartRecord[ YggLogBasic.WordNumberFromRecordID[thisRecord], thisRecord]; }; Recover: PUBLIC PROC [logVolume: VolumeID, logFile, restartFile: YggEnvironment.DID] = { myFileStore: FileStore; DiscoverWhereToStartAnalysisPass: PROC [] RETURNS [ checkpointWord: WordNumber, checkpointRecord, startAnalysisRecord: RecordID] = { CheckpointCompleteRecordFromWord: PROC [checkpointWord: WordNumber] RETURNS [ ok: BOOL, checkpointRecord, startAnalysisRecord: RecordID] = { { checkpointCompleteRecordBody: YggLogRep.CheckpointCompleteRecord; notStartOfRecord: BOOL; currentRecord: RecordID; [notStartOfRecord: notStartOfRecord, currentRecord: currentRecord] _ YggLogBasic.OpenRecordStreamFromWord[checkpointWord ! YggLogBasic.InvalidPage => GOTO failReturn ]; IF notStartOfRecord THEN GOTO failReturn; IF YggLogBasic.CheckCurrentRecord[].truncated THEN GOTO failReturn; TRUSTED { IF YggLogBasic.GetCurrentRecord[currentRecord, [base: @checkpointCompleteRecordBody, length: SIZE[YggLogRep.CheckpointCompleteRecord]]].status = destinationFull OR checkpointCompleteRecordBody.type # checkpointComplete THEN GOTO failReturn; myFileStore _ ConvertUnsafe.ToRope[ from: LOOPHOLE[LONG[@checkpointCompleteRecordBody.myFileStore]]]; }; checkpointRecord _ checkpointCompleteRecordBody.thisRecordID; startAnalysisRecord _ checkpointCompleteRecordBody.startAnalysisRecordID; ok _ TRUE; EXITS failReturn => { ok _ FALSE }; }; YggLogBasic.CloseRecordStream[]; RETURN [ok, checkpointRecord, startAnalysisRecord]; };--CheckpointCompleteRecordFromWord FindNewestCheckpointCompleteRecord: PROC [] RETURNS [w: WordNumber] = { bigRecordID: RecordID = YggLogInline.RecordIDFromWordNumber[ YggInline.WordsFromPages[YggLogBasic.LogFileSize[], logDeviceCharacteristics]]; firstWord: WordNumber = YggLogBasic.LocateFirstRecord[]; checkpointRecord, currentRecord: RecordID; foundCheckpointRecord: BOOL _ FALSE; notStartOfRecord: BOOL; [notStartOfRecord: notStartOfRecord, currentRecord: currentRecord] _ YggLogBasic.OpenRecordStreamFromWord[firstWord]; IF notStartOfRecord THEN ERROR InternalProgrammingError; DO nextRecord: RecordID; endOfLog, truncatedRecord: BOOL; currentType: RecordType _ PeekCurrentType[currentRecord]; [endOfLog, truncatedRecord, nextRecord] _ YggLogBasic.AdvanceRecordStream[]; IF currentType = checkpointComplete AND NOT truncatedRecord THEN { checkpointRecord _ currentRecord; foundCheckpointRecord _ TRUE; }; IF endOfLog THEN EXIT; currentRecord _ nextRecord; ENDLOOP; IF NOT foundCheckpointRecord THEN ERROR InternalProgrammingError; TRUSTED { w _ YggLogInline.WordsFromSubtract[larger: checkpointRecord, smaller: IF ConstArith.Compare[checkpointRecord, bigRecordID] # less THEN bigRecordID ELSE nullRecordID]; }; RETURN; };--FindNewestCheckpointCompleteRecord checkpointRecordFromRestartFile: RecordID; ok: BOOL; [checkpointWord, checkpointRecordFromRestartFile] _ YggRestartFile.ReadRestartRecord[]; [ok, checkpointRecord, startAnalysisRecord] _ CheckpointCompleteRecordFromWord[checkpointWord]; IF NOT ok OR checkpointRecord # checkpointRecordFromRestartFile THEN { checkpointWord _ FindNewestCheckpointCompleteRecord[]; [ok, checkpointRecord, startAnalysisRecord] _ CheckpointCompleteRecordFromWord[checkpointWord]; IF NOT ok THEN ERROR InternalProgrammingError; }; RETURN [checkpointWord, checkpointRecord, startAnalysisRecord]; };--DiscoverWhereToStartAnalysisPass checkpointWord: WordNumber; checkpointRecord: RecordID; startAnalysisRecord: RecordID; [] _ YggLogBasic.EstablishLogFile[logFile]; YggRestartFile.EstablishRestartFile[restartFile]; [checkpointWord, checkpointRecord, startAnalysisRecord] _ DiscoverWhereToStartAnalysisPass[]; YggRecoveryPassProcs.CalledBeforeAnalysisPass[myFileStore, logVolume]; AnalysisPass[checkpointWord, checkpointRecord, startAnalysisRecord]; YggRecoveryPassProcs.CalledAfterAnalysisPass[]; YggLogBasic.Force[followingRecord: YggLogBasic.RecordIDOfNextPut[]]; YggRecoveryPassProcs.CalledBeforeUpdatePass[]; UpdatePass[checkpointWord, checkpointRecord, startAnalysisRecord, YggLogBasic.RecordIDOfNextPut[]]; YggRecoveryPassProcs.CalledAfterUpdatePass[]; [] _ Checkpoint[nullRecordID, nullRecordID, myFileStore]; YggLogBasic.AssertNormalOperation[]; };--Recover AnalysisPass: PROC [ checkpointWord: WordNumber, checkpointRecord, firstRecord: RecordID] = { currentRecord, nextRecord: RecordID; TurnOffCheckpointRegistration[]; { notStartOfRecord: BOOL; [notStartOfRecord: notStartOfRecord, currentRecord: currentRecord] _ YggLogBasic.OpenRecordStreamFromCheckpoint[checkpointWord: checkpointWord, checkpointRecord: checkpointRecord, firstRecord: firstRecord]; IF notStartOfRecord THEN ERROR InternalProgrammingError; }; DO currentRecordType: RecordType _ PeekCurrentType[currentRecord]; -- no errors analysisProc: YggLogControl.AnalysisProc; IF currentRecordType IN AnalysisProcsIndex AND (analysisProc _ analysisProcs[currentRecordType]) # NIL AND NOT YggLogBasic.CheckCurrentRecord[].truncated THEN { transID: TransID _ PeekCurrentTrans[currentRecord]; analysisProc[currentRecord, currentRecordType, transID]; }; { endOfLog, truncatedRecord: BOOL; [endOfLog: endOfLog, truncatedRecord: truncatedRecord, currentRecord: nextRecord] _ YggLogBasic.AdvanceRecordStream[]; IF truncatedRecord THEN NoteTruncatedRecord[currentRecord]; IF endOfLog THEN EXIT; }; currentRecord _ nextRecord; ENDLOOP; IF NOT checkpointState.checkpointSeen THEN ERROR InternalProgrammingError; { nextPageToWrite: PageNumber; versionOfNextPageToWrite: YggLogRep.PageVersion; [nextPageToWrite, versionOfNextPageToWrite] _ YggLogBasic.GetCurrentPageAndVersion[]; YggLogBasic.CloseRecordStream[]; YggLogBasic.OpenForPut[nextPage: nextPageToWrite, version: 1-versionOfNextPageToWrite, nextRecord: nextRecord]; YggLogBasic.Release[beforeRecord: checkpointState.keepRecord]; YggLogBasic.Force[followingRecord: WriteNoopRecords[nPages: YggLogBasic.maxInvalidLogPages].followingRecord]; YggLogBasic.CloseForPut[]; YggLogBasic.OpenForPut[nextPage: nextPageToWrite, version: versionOfNextPageToWrite, nextRecord: nextRecord] }; };--AnalysisPass RoundUpToPages: PROC [recordID: RecordID] RETURNS [RecordID] = { words: CARDINAL = YggLogInline.WordInPageFromRecordID[recordID, YggLog.wordsPerPage]; RETURN [ IF words= 0 THEN recordID ELSE YggLogInline.AddC[recordID, YggLog.wordsPerPage-words]]; }; firstTruncatedRecord, lastTruncatedRecord: LIST OF RecordID _ NIL; NoteTruncatedRecord: PROC [record: RecordID] = { new: LIST OF RecordID _ CONS [first: record, rest: NIL]; IF lastTruncatedRecord # NIL THEN lastTruncatedRecord.rest _ new; lastTruncatedRecord _ new; IF firstTruncatedRecord = NIL THEN firstTruncatedRecord _ new; }; CheckpointCompleteAnalysisProc: PROC [ record: RecordID, type: RecordType, trans: TransID] = TRUSTED { checkpointCompleteRecordBody: YggLogRep.CheckpointCompleteRecord; IF YggLogBasic.GetCurrentRecord[currentRecord: record, to: [base: @checkpointCompleteRecordBody, length: SIZE[YggLogRep.CheckpointCompleteRecord]]].status = destinationFull OR checkpointCompleteRecordBody.type # checkpointComplete THEN ERROR; checkpointState.keepRecord _ checkpointCompleteRecordBody.keepRecordID; checkpointState.checkpointSeen _ TRUE; }; CheckpointState: TYPE = RECORD [ checkpointSeen: BOOL _ FALSE, keepRecord: RecordID ]; checkpointState: CheckpointState; UpdatePass: PROC [checkpointWord: WordNumber, checkpointRecord, firstRecord, nextRecordToWrite: RecordID] = { currentTruncatedRecord: LIST OF RecordID _ firstTruncatedRecord; notStartOfRecord: BOOL; currentRecord: RecordID; expectTruncatedRecord: BOOL; TurnOffRecoveryRegistration[]; [notStartOfRecord: notStartOfRecord, currentRecord: currentRecord] _ YggLogBasic.OpenRecordStreamFromCheckpoint[checkpointWord: checkpointWord, checkpointRecord: checkpointRecord, firstRecord: firstRecord]; IF notStartOfRecord THEN ERROR InternalProgrammingError; DO expectTruncatedRecord _ FALSE; { currentRecordType: RecordType; recoveryProc: YggLog.RecoveryProc; IF currentTruncatedRecord # NIL THEN TRUSTED { SELECT ConstArith.Compare[currentRecord, currentTruncatedRecord.first] FROM less => NULL; equal => { expectTruncatedRecord _ TRUE; currentTruncatedRecord _ currentTruncatedRecord.rest; GOTO GetNext }; greater => ERROR InternalProgrammingError; ENDCASE => ERROR; }; TRUSTED { SELECT ConstArith.Compare[currentRecord, nextRecordToWrite] FROM less => NULL; equal => EXIT; greater => ERROR InternalProgrammingError; ENDCASE => ERROR; }; IF (currentRecordType _ PeekCurrentType[currentRecord]) IN RecoveryProcsIndex AND (recoveryProc _ recoveryProcs[currentRecordType]) # NIL THEN { transID: TransID _ PeekCurrentTrans[currentRecord]; transHandle: YggTransactionMap.TransHandle = YggTransactionMap.GetTransHandle[transID]; IF transHandle # YggTransactionMap.nullTransHandle THEN { s: YggTransactionMap.TransState _ YggTransactionMap.StateDuringRecovery[transHandle]; state: YggLog.TransState _ IF s = committed THEN committed ELSE IF s = aborted THEN aborted ELSE ready; recoveryProc[currentRecord, currentRecordType, transHandle, state]; }; }; GOTO GetNext; EXITS GetNext => { endOfLog, truncatedRecord: BOOL; nextRecord: RecordID; [endOfLog: endOfLog, truncatedRecord: truncatedRecord, currentRecord: nextRecord] _ YggLogBasic.AdvanceRecordStream[]; IF endOfLog AND (nextRecord # nextRecordToWrite) OR (truncatedRecord # expectTruncatedRecord) THEN ERROR InternalProgrammingError; currentRecord _ nextRecord; }; }; ENDLOOP; }; PeekCurrentType: PROC [thisRecord: RecordID] RETURNS [recordType: RecordType] = { recordTypeHeader: YggLogRep.RecordTypeHeader; status: YggLog.ReadProcStatus; TRUSTED {[status: status] _ YggLogBasic.GetCurrentRecord[currentRecord: thisRecord, to: [base: @recordTypeHeader, length: YggLogRep.RecordTypeHeader.SIZE]]; }; IF status = sourceExhausted THEN ERROR InternalProgrammingError; RETURN [recordTypeHeader.type]; }; PeekCurrentTrans: PROC [thisRecord: RecordID] RETURNS [TransID] = { transactionHeader: YggLogRep.TransactionHeader; status: YggLog.ReadProcStatus; TRUSTED {[status: status] _ YggLogBasic.GetCurrentRecord[currentRecord: thisRecord, to: [base: @transactionHeader, length: YggLogRep.TransactionHeader.SIZE]]; }; IF status = sourceExhausted THEN RETURN [nullTransID] ELSE RETURN [transactionHeader.transID]; }; ReadForRecovery: PUBLIC PROC [ thisRecord: RecordID, wordsToSkip: CARDINAL _ 0, to: Block] RETURNS [status: YggLog.ReadProcStatus, wordsRead: CARDINAL] = { IF wordsToSkip > LAST[CARDINAL] - SIZE[YggLogRep.TransactionHeader] THEN wordsToSkip _ LAST[CARDINAL] ELSE wordsToSkip _ wordsToSkip + SIZE[YggLogRep.TransactionHeader]; TRUSTED {[status: status, wordsRead: wordsRead] _ YggLogBasic.GetCurrentRecord[currentRecord: thisRecord, to: [base: NIL, length: wordsToSkip, rest: @to]]; }; wordsRead _ wordsRead - SIZE[YggLogRep.TransactionHeader]; }; Write: PUBLIC PROC [trans: YggWorker.Handle, logRecordPhaseType: LogRecordPhaseType, recordType: RecordType, recordData: Block, force: BOOL] RETURNS [thisRecord, followingRecord: RecordID] = { recordHeader: YggLogRep.TransactionHeader _ [type: recordType, transID: trans.transID]; TRUSTED {[thisRecord, followingRecord] _ YggLogBasic.Put[ from: [base: @recordHeader, length: SIZE[YggLogRep.TransactionHeader], rest: @recordData], force: force, writeID: FALSE]; }; IF logRecordPhaseType = undo OR logRecordPhaseType = redoUndo THEN lastRecordWithUndo _ thisRecord; }; CoordinatorWrite: PUBLIC PROC [ trans: YggCoordinator.Handle, recordType: RecordType, recordData: Block, force: BOOL] RETURNS [thisRecord, followingRecord: RecordID] = { recordHeader: YggLogRep.TransactionHeader _ [type: recordType, transID: trans.transID]; TRUSTED {[thisRecord, followingRecord] _ YggLogBasic.Put[ from: [base: @recordHeader, length: SIZE[YggLogRep.TransactionHeader], rest: @recordData], force: force, writeID: FALSE]; }; }; Force: PUBLIC PROC [followingRecord: RecordID] = { IF followingRecord # nullRecordID THEN YggLogBasic.Force[followingRecord]; }; Read: PUBLIC PROC [ thisRecord: RecordID, wordsToSkip: CARDINAL, to: YggLog.Block] RETURNS [status: YggLog.ReadProcStatus, wordsRead: CARDINAL] = { IF wordsToSkip > LAST[CARDINAL] - SIZE[YggLogRep.TransactionHeader] THEN wordsToSkip _ LAST[CARDINAL] ELSE wordsToSkip _ wordsToSkip + SIZE[YggLogRep.TransactionHeader]; TRUSTED {[status: status, wordsRead: wordsRead] _ YggLogBasic.Get[ thisRecord: thisRecord, to: [base: NIL, length: wordsToSkip, rest: @to]]; }; wordsRead _ wordsRead - SIZE[YggLogRep.TransactionHeader]; }; noMoreRecoveryRegistration: BOOL _ FALSE; TurnOffRecoveryRegistration: ENTRY PROC [] = INLINE { noMoreRecoveryRegistration _ TRUE; }; RecoveryProcsIndex: TYPE = RecordType [noop .. lock]; RecoveryProcsArray: TYPE = ARRAY RecoveryProcsIndex OF YggLog.RecoveryProc; recoveryProcs: REF RecoveryProcsArray = SafeStorage.GetSystemZone[].NEW[RecoveryProcsArray _ ALL [NIL]]; RegisterRecoveryProc: PUBLIC ENTRY PROC [ recordType: RecordType, recoveryProc: YggLog.RecoveryProc] = { IF noMoreRecoveryRegistration THEN RETURN WITH ERROR ClientProgrammingError; IF recordType NOT IN RecoveryProcsIndex THEN RETURN WITH ERROR InternalProgrammingError; recoveryProcs[recordType] _ recoveryProc; }; noMoreCheckpointRegistration: BOOL _ FALSE; TurnOffCheckpointRegistration: ENTRY PROC [] = INLINE { noMoreCheckpointRegistration _ TRUE; }; IsCheckpointProcCallable: ENTRY PROC [] RETURNS [BOOL] = INLINE { RETURN [noMoreCheckpointRegistration]; }; AnalysisProcsIndex: TYPE = RecordType [noop .. lock]; AnalysisProcsArray: TYPE = ARRAY AnalysisProcsIndex OF YggLogControl.AnalysisProc; analysisProcs: REF AnalysisProcsArray = SafeStorage.GetSystemZone[].NEW[AnalysisProcsArray _ ALL [NIL]]; RegisterAnalysisProc: PUBLIC ENTRY PROC [ recordType: RecordType, analysisProc: YggLogControl.AnalysisProc] = { IF noMoreCheckpointRegistration THEN RETURN WITH ERROR ClientProgrammingError; IF recordType NOT IN AnalysisProcsIndex THEN RETURN WITH ERROR InternalProgrammingError; analysisProcs[recordType] _ analysisProc; }; checkpointProcs: ARRAY [0 .. maxCheckpointProcs) OF YggLogControl.CheckpointProc _ ALL [NIL]; maxCheckpointProcs: INT = 5; nCheckpointProcs: INT [0 .. maxCheckpointProcs] _ 0; RegisterCheckpointProc: PUBLIC ENTRY PROC [ checkpointProc: YggLogControl.CheckpointProc] = { IF noMoreCheckpointRegistration THEN RETURN WITH ERROR ClientProgrammingError; IF nCheckpointProcs = maxCheckpointProcs THEN RETURN WITH ERROR InternalProgrammingError; checkpointProcs[nCheckpointProcs] _ checkpointProc; nCheckpointProcs _ nCheckpointProcs + 1; }; ForkCheckpointYggDummyProcess: PUBLIC PROC [myFileStore: FileStore, wakeupPeriodInSeconds: NAT] = TRUSTED { YggDummyProcess.Detach[FORK CheckpointYggDummyProcess[myFileStore, wakeupPeriodInSeconds]]; }; CheckpointYggDummyProcess: PROC [myFileStore: FileStore, wakeupPeriodInSeconds: NAT] = { keepRecord, startAnalysisRecord: RecordID _ nullRecordID; YggDummyProcess.SetPriority[YggDummyProcess.priorityForeground]; DO YggDummyProcess.Pause[YggDummyProcess.SecondsToTicks[wakeupPeriodInSeconds]]; [keepRecord, startAnalysisRecord] _ Checkpoint[keepRecord, startAnalysisRecord, myFileStore]; SafeStorage.ReclaimCollectibleObjects[suspendMe: TRUE, traceAndSweep: FALSE]; ENDLOOP; }; Checkpoint: PROC [ previousKeepRecord, previousStartAnalysisRecord: RecordID, myFileStore: FileStore] RETURNS [keepRecord, startAnalysisRecord: RecordID] = { onlineLogLength: WordNumber = YggInline.WordsFromPages[YggLogBasic.LogFileSize[], logDeviceCharacteristics]; differentRecordIDsSeen: BOOL _ FALSE; NoticeDifferentRecordIDs: PROC [old, new: RecordID] = { comp: Basics.Comparison; TRUSTED {comp _ ConstArith.Compare[old, new]; }; SELECT comp FROM less => differentRecordIDsSeen _ TRUE; greater => ERROR InternalProgrammingError; ENDCASE; }; keepRecord _ startAnalysisRecord _ YggLogBasic.RecordIDOfNextPut[]; IF NOT IsCheckpointProcCallable[] THEN ERROR InternalProgrammingError; FOR i: INT [0 .. maxCheckpointProcs] IN [0 .. nCheckpointProcs) DO thisKeepRecord, thisStartAnalysisRecord: RecordID; [thisKeepRecord, thisStartAnalysisRecord] _ checkpointProcs[i][]; keepRecord _ YggLogInline.Min[thisKeepRecord, keepRecord]; startAnalysisRecord _ YggLogInline.Min[thisStartAnalysisRecord, startAnalysisRecord] ENDLOOP; NoticeDifferentRecordIDs[previousKeepRecord, keepRecord]; NoticeDifferentRecordIDs[previousStartAnalysisRecord, startAnalysisRecord]; IF differentRecordIDsSeen THEN { [] _ WriteCheckpointCompleteRecord[ startAnalysisRecord: startAnalysisRecord, keepRecord: keepRecord, length: 128, -- fix this myFileStore: myFileStore]; YggLogBasic.Release[beforeRecord: keepRecord]; previousKeepRecord _ keepRecord; previousStartAnalysisRecord _ startAnalysisRecord; }; RETURN [keepRecord, startAnalysisRecord]; }; RegisterAnalysisProc[ recordType: checkpointComplete, analysisProc: CheckpointCompleteAnalysisProc]; END. CHANGE LOG Changed by MBrown on April 1, 1983 1:25 pm Changed by MBrown on June 16, 1983 9:35 am Changed by MBrown on June 27, 1983 10:06 am Changed by MBrown on January 30, 1984 10:20:09 am PST YggLogImpl.mesa Copyright Σ 1987, 1988 by Xerox Corporation. All rights reserved. Derived from AlpineLogImpl Last edited by MBrown on January 30, 1984 10:19:45 am PST Carl Hauser, October 15, 1987 1:08:41 pm PDT Bob Hagmann May 6, 1988 4:14:52 pm PDT Write a noop record on each page of the log file. A noop record is precisely one page long. Write a CheckpointBegin record, then a CheckpointComplete record pointing to it (and the restart record in the restart file). If all has gone well, the CheckpointBegin record was written as first record on page 0 of the log file. This proc has a large frame. This call waits both for a log force and for a restart file write before returning. Updates: Recover.myFileStore. Returns ok = FALSE if any error detected. Do a sequential scan of log, starting at YggLogBasic.LocateFirstRecord[]. This writes log records. To make them available for reading by the update pass, they must be forced. Scans log, calling procs registered for analysis pass of recovery. Analysis complete. If no checkpoint seen, we are in big trouble. Overwrite enough pages past the end to ensure that we see no inconsistency due to out-of-order log page writes. Then open log for writing at correct position. We use this to find the keepRecord value in the LAST checkpoint record. FilePageMgr.ForceOutEverything[]; This is a YggLog.ReadProc. YggLog writing Exported to YggLog YggLog reading Exported to YggLog Recovery procs Exported to YggLog Analysis procs, checkpoint procs Exported to YggLogControl Writes a checkpointCompleteRecord only if it contains a value different than the previous checkpointCompleteRecord. Checkpoint process is now implemented here, not in InitProcsImpl(!). Fixed bug in AnalysisPass. After analysis is complete, we must write a few noop records to clean up the log tail, but we failed to call LogBasic.Release[keepRecord] to tell lower-level log routines that overwriting those pages is ok. Eliminated bug/feature in AnalysisPass that reclaimed "extra" log pages occupied by a multi-page truncated record found at end of log. This never really worked (it has caused several restart failures) and the LogBasic abstraction does not allow for its clean implementation. How much is it worth? Added call to LogBasic.AssertNormalOperation at end of restart; this permits restart to write closer to the beginning of the log than normal clients are allowed to. Cedar 5.0: don't mention Environment. Hauser, March 8, 1985 10:50:52 am PST Nodified, added copyright. Carl Hauser, October 4, 1985 1:34:48 pm PDT Change "Log" to "AlpineLog" Κ*˜šœ™IcodešœB™BK™—šœ™Jšœ*™*K™,K™&—˜šΟk ˜ Jšœ˜Jšœ ˜ Jšœ˜Jšœ˜J˜Jšœ ˜ Jšœ˜Jšœ˜Jšœ ˜ Jšœ ˜ Jšœ ˜ Jšœ˜Jšœ ˜ Jšœ˜Jšœ ˜ Jšœ ˜ Jšœ˜Jšœ˜Jšœ ˜ Jšœ˜J˜——šœ  ˜š˜Jšœ©˜©—š˜Jšœ4˜4—J˜Jš˜J˜Jšœ œ˜)Jšœ œ˜+Jšœ œ˜'Jšœ2˜2Jšœ œ˜-Jšœ œ˜+Jšœ œ˜*Jšœ œ˜(Jšœ œ˜!Jšœ-˜-Jšœœ˜Jšœ œ˜%Jšœœ˜5J˜Jšœœœ˜%Jšœœœ˜'Jšœ œœœ˜!J˜Jšœ œœ˜,J˜Jšœœ&˜FJšœœœΟc˜2Jšœœœž˜2J˜Jšœ,˜,J˜J˜šΟnœœœ]˜pJ˜J˜$Jšœ&˜&Jšœ$˜$JšœT˜TJšœ5˜5Jšœ2˜2Jšœ1˜1JšœM™MJšœ™JšœJ˜JJšœ0˜0JšœO˜OJ˜+JšœI™IJšœ3™3Jšœ<˜Jšœ™šœ)™)J˜JšœA˜AJšœœ˜J˜˜DšœP˜PJšœ˜——Jšœœœ ˜)Jšœ,œœ ˜Cš ˜ šœR˜TJšœœ@˜NJšœ7œœ ˜L—˜#Jšœœœ.˜A—J˜—J˜=J˜IJšœœ˜ š˜Jšœœ˜—J˜—Jšœ ˜ Jšœ-˜3Jšœž"˜$J˜—šŸ"œœœ˜GJšœI™Išœ<˜—Jšœœœ˜8J˜—š˜Jšœ@ž ˜LJšœ)˜)šœœ˜.Jšœ4œ˜;šœ,œ˜5J˜3J˜8J˜——šœœ˜"˜SJšœ"˜"—Jšœœ$˜;Jšœ œœ˜J˜—J˜Jšœ˜—JšœA™AJšœœ œœ˜JJšœN™NJšœP™PšœP˜PJšœU˜UJšœ ˜ šœ1˜1J˜=—Jšœ>˜>šœ"˜"JšœJ˜J—Jšœ˜šœ1˜1J˜:—J˜—Jšœž˜J˜—šŸœœœ˜@JšœœF˜Ušœ˜Jšœ œ œ9˜W—J˜J˜—Jšœ+œœ œ˜BJ˜šŸœœ˜0Jš œœœ œœ˜8Jšœœœ ˜AJ˜Jšœœœ˜>J˜J˜—šŸœœ˜&J˜?JšœG™GJšœA˜Ašœ8˜:˜%Jšœœ@˜N—Jšœ7œœ˜B—J˜GJšœ!œ˜&J˜J˜—šœœœ˜ Jšœœœ˜J˜J˜—J˜!J˜šŸ œœ˜-J˜?Jšœœœ!˜@Jšœœ˜J˜Jšœœ˜J˜˜DJšœJ˜JJ˜>—Jšœœœ˜8š˜Jšœœ˜J˜J˜Jšœ"˜"šœœ˜.šœA˜KJšœœ˜ ˜ Jšœœ˜J˜5Jšœ ˜—Jšœ œ˜*Jšœœ˜—J˜—š ˜ šœ6˜@Jšœœ˜ Jšœ œ˜Jšœ œ˜*Jšœœ˜—J˜—šœ6œ˜QJšœ4œœ˜>J˜3JšœW˜Wšœ1œ˜9JšœU˜Ušœœœ ˜:Jšœœ œ œ˜,—J˜CJ˜—J˜—Jšœ ˜ š˜˜ Jšœœ˜7˜SJšœ"˜"——šœ œ"˜3Jšœ*œœ˜N—J˜J˜—J˜Jšœ˜—J™!J˜J˜—šŸœœœ˜QJšœ-˜-Jšœ˜šœS˜SJšœAœ˜K—Jšœœœ˜@Jšœ˜J˜J˜—šŸœœœ˜CJšœ/˜/Jšœ˜šœS˜SJšœCœ˜M—Jšœœœ˜5Jšœœ˜(J˜J˜—šŸœœœ˜Jšœ#œ˜;Jšœ,œ˜@Jšœ™š œœœœ˜HJšœœœ˜—š˜Jšœœ˜>—˜1šœ7˜7Jšœ œ&˜4——Jšœœ˜:J˜——headšœ™Jšœ™J˜šŸœœœuœ˜ŒJšœ,˜3JšœW˜Wšœ9˜9Jšœ$œ2˜ZJšœœ˜!—Jšœœœ!˜cJ˜J˜—šŸœœœ˜JšœPœ˜UJšœ,˜3J˜W˜9Jšœ$œ2˜ZJšœœ˜!—J˜J˜—šŸœœœ ˜2Jšœ œ$˜JJ˜J˜J˜——šœ™Jšœ™J˜šŸœœœ˜Jšœ#œ˜>Jšœ,œ˜@š œœœœ˜HJšœœœ˜—š˜Jšœœ˜>—šœB˜BJšœ#œ&˜L—Jšœœ˜:J˜——šœ™Jšœ™J˜Jšœœœ˜)J˜šŸœœœœ˜5Jšœœ˜"J˜J˜—Jšœœ˜5Jšœœœœ˜Kšœœ˜'Jšœœœœ˜@J˜—šŸœœœœ˜)Jšœ>˜>šœ˜"Jšœœœ˜)—šœ œœœ˜-Jšœœœ˜+—J˜)J˜——šœ ™ Jšœ™J˜Jšœœœ˜+J˜šŸœœœœ˜7Jšœœ˜$J˜J˜—š Ÿœœœœœœ˜AJšœ ˜&J˜J˜—Jšœœ˜5Jšœœœœ˜Ršœœ˜'Jšœœœœ˜@J˜—šŸœœœœ˜)JšœE˜Ešœ˜$Jšœœœ˜)—šœ œœœ˜-Jšœœœ˜+—J˜)J˜J˜—Jš œœœ œœ˜]Jšœœ˜Jšœœ˜4J˜šŸœœœœ˜+Jšœ1˜1šœ˜$Jšœœœ˜)—šœ'œ˜.Jšœœœ˜+—J˜3J˜(J˜J˜—šŸœœœ˜CJšœœœ˜'Jšœœ@˜[J˜J˜—šŸœœ1œ˜XJ˜9Jšœ@˜@š˜JšœM˜M˜#J˜9—Jšœ1œœ˜MJšœ˜—J˜J˜—šŸ œœ˜J˜:J˜Jšœ0˜7JšœL™LJšœ&™&Jšœl˜lJšœœœ˜%šŸœœ˜7Jšœ˜Jšœ)˜0šœ˜Jšœ!œ˜&Jšœ œ˜*Jšœ˜—J˜—JšœC˜CJšœœœœ˜Fšœœœ˜BJ˜2J˜AJšœ:˜:JšœT˜TJšœ˜—J˜9J˜Kšœœ˜ ˜#J˜)J˜Jšœž ˜J˜—Jšœ.˜.J˜ J˜2J˜—Jšœ#˜)J˜J˜—˜J˜J˜.J˜—Jšœ˜J˜—Jšœ˜ J˜J˜*JšœD™DJ˜J˜*Jšœ[™[Jšœ]™]Jšœ0™0J˜J˜+JšœS™SJšœL™LJšœN™NJšœ:™:JšœL™LJšœW™WJ˜Jšœ2˜5Jšœ%™%™%K™—™+K™—K™K™—…—PŽmΨ