<> <> <> <> <> <> <> 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 <> <> <<(it has caused several restart failures) and the LogBasic abstraction does not>> <> <> <> Changed by MBrown on January 30, 1984 10:20:09 am PST <> <> <> <> <> <<>> <<>>