<> <> <> <> <> DIRECTORY Camelot, PBasics, YggEnvironment, YggLog, YggLogBasic, YggLogInline, YggLogBasicInternal, YggLogRep, VM; YggLogBasicWriteImpl: CEDAR MONITOR IMPORTS PBasics, YggLog, YggLogBasic, YggLogBasicInternal, YggLogInline EXPORTS YggLogBasic = BEGIN PageNumber: TYPE = YggEnvironment.PageNumber; RecordID: TYPE = YggLog.RecordID; CallerProgrammingError: ERROR = CODE; <> <> curPageRecordID: YggLog.RecordID; curPageWordsUsed: CARD; -- in machine dependent words curPagePtr: LONG POINTER _ NIL; <> pagesLeft: INT; <> curVersion: YggLogRep.PageVersion; <> nPagesWritten: INT _ 0; nPartFullPagesWritten: INT _ 0; <> <> OpenForPut: PUBLIC ENTRY PROC [ nextPage: PageNumber, version: YggLogRep.PageVersion, nextRecord: RecordID] = { <> IF YggLogInline.WordInPageFromRecordID[nextRecord, VM.wordsPerPage] # 0 THEN ERROR CallerProgrammingError; curPageRecordID _ nextRecord; curPageWordsUsed _ 0; curVersion _ version; TRUSTED { [pagesLeft, curPagePtr] _ YggLogBasicInternal.OpenBasicForPut[nextPage, version, nextRecord]; LOOPHOLE[curPagePtr, LONG POINTER TO CARD32]^ _ 0; <> }; }; CloseForPut: PUBLIC ENTRY PROC [] = { YggLogBasicInternal.CloseBasicForPut[]; curPagePtr _ NIL; }; <> Put: PUBLIC PROC [ from: YggLog.Block, optr: Camelot.optrT, force: BOOL, writeID: BOOL] RETURNS [thisRecord, followingRecord: RecordID, thisWordNumber: YggEnvironment.WordNumber] = TRUSTED { << Compute the total length of the block to be written, and touch all pages in it.>> totalLen: INT _ 0; -- in 32 bit words FOR b: YggLog.BlockPtr _ @from, b.rest UNTIL b = NIL DO len: CARD = b.length; IF len > 0 THEN { IF b.base # NIL THEN { offset: CARD _ 0; UNTIL offset >= len*WordsINWORDS DO touch: CARDINAL _ LOOPHOLE[b.base + offset*SIZE[INT32], LONG POINTER TO CARDINAL]^; IF offset = len - 1 THEN EXIT; offset _ offset + VM.wordsPerPage/WORDS[CARD32]; IF offset >= len THEN offset _ len - 1; ENDLOOP; }; totalLen _ totalLen + len; }; ENDLOOP; << Total record length must be in correct range.>> IF totalLen NOT IN [YggLogBasic.minBlockLen .. YggLogBasic.maxBlockLen] THEN ERROR CallerProgrammingError; << Append to log, then force to disk if necessary.>> << Note: tail monitor is not held during force.>> [thisRecord, followingRecord, thisWordNumber] _ PutEntry[totalLen, from, force, writeID]; IF force THEN YggLogBasicInternal.ForceTo[followingRecord: followingRecord]; }; WordsINWORDS: CARD = WORDS[CARD32]; PutEntry: ENTRY PROC [ totalLen: CARDINAL, from: YggLog.Block, force: BOOL, writeID: BOOL] RETURNS [thisRecord, followingRecord: RecordID, thisWordNumber: YggEnvironment.WordNumber] = { isContinuation: BOOL _ FALSE; <> << On entry, there is always room in the current page for a header plus 1 word (i.e. curPageWordsUsed < wordsPerPage-SIZE[YggLogRep.Header]), so this record starts on the current page.>> thisRecord _ CurrentRecordID[]; thisWordNumber _ YggLogBasic.WordNumberFromRecordID[thisRecord]; IF writeID THEN TRUSTED { LOOPHOLE[from.base, LONG POINTER TO YggLogRep.CheckpointCompleteRecord].thisRecordID _ thisRecord; }; DO <> wordsThisBlock: CARD _ MIN[(YggLog.wordsPerPage-WORDS[YggLogRep.Header])-curPageWordsUsed, totalLen*WordsINWORDS]; -- in machine dependent words wordsThisCopy: CARD; totalLen _ totalLen-wordsThisBlock/WordsINWORDS; -- 32 bit words remaining to be logged when this block is finished << Write a header for the log block.>> << The first store to a page should be the one that sets valid _ FALSE.>> TRUSTED { pageNumber: INT; pageNumber _ ConstArith.ToInt[ConstArith.Div[curPageRecordID, ConstArith.FromCard[YggLog.wordsPerPage]]]; Header[curPagePtr+(curPageWordsUsed*UNITS[WORD])]^ _ [ valid: FALSE, version: curVersion, hasContinuation: (totalLen # 0), isContinuation: isContinuation, nWords: wordsThisBlock+SIZE[YggLogRep.Header], unused1: pageNumber]; }; curPageWordsUsed _ curPageWordsUsed+WORDS[YggLogRep.Header]; DO <> wordsThisCopy _ MIN[from.length*WordsINWORDS, wordsThisBlock]; TRUSTED {PBasics.Copy[ to: curPagePtr+(curPageWordsUsed*UNITS[WORD]), from: from.base, nwords: wordsThisCopy/WordsINWORDS]; }; curPageWordsUsed _ curPageWordsUsed+wordsThisCopy; IF wordsThisCopy = wordsThisBlock THEN EXIT; < wordsThisCopy = from.length, from.rest # NIL.>> wordsThisBlock _ wordsThisBlock-wordsThisCopy; TRUSTED {from _ from.rest^;}; ENDLOOP; <> IF totalLen # 0 THEN { -- YggLog record not all written. <> <> AdvancePage[]; from.base _ from.base+wordsThisCopy; from.length _ from.length-wordsThisCopy/WordsINWORDS; isContinuation _ TRUE; } ELSE { -- YggLog record all written. <> <> wpp: CARD = YggLog.wordsPerPage; IF force OR (curPageWordsUsed >= wpp-WORDS[YggLogRep.Header]) THEN AdvancePage[]; RETURN [thisRecord, CurrentRecordID[], thisWordNumber]; } ENDLOOP; }; Force: PUBLIC PROC [followingRecord: RecordID] = { <> AdvanceTo: ENTRY PROC [followingRecord: RecordID] = { <> <> TRUSTED {IF ConstArith.Compare[followingRecord, curPageRecordID] # greater THEN RETURN; }; <> TRUSTED {IF ConstArith.Compare[followingRecord, CurrentRecordID[]] = greater THEN RETURN WITH ERROR CallerProgrammingError; }; AdvancePage[]; }; AdvanceTo[followingRecord]; <> YggLogBasicInternal.ForceTo[followingRecord]; }; <> Header: PROC [p: LONG POINTER] RETURNS [LONG POINTER TO YggLogRep.Header] = INLINE { RETURN [LOOPHOLE[p]] }; RecordIDOfNextPut: PUBLIC ENTRY PROC [] RETURNS [RecordID] = { RETURN [CurrentRecordID[]] }; CurrentRecordID: INTERNAL PROC [] RETURNS [RecordID] = INLINE { RETURN [YggLogInline.AddC[curPageRecordID, curPageWordsUsed]] }; AdvancePage: INTERNAL PROC [] = { <> IF curPageWordsUsed # YggLog.wordsPerPage THEN TRUSTED { nPartFullPagesWritten _ nPartFullPagesWritten + 1; LOOPHOLE[curPagePtr+(curPageWordsUsed*UNITS[WORD]), LONG POINTER TO CARD32]^ _ 0 }; nPagesWritten _ nPagesWritten + 1; <> TRUSTED {Header[curPagePtr].valid _ TRUE;}; pagesLeft _ pagesLeft - 1; IF pagesLeft = 0 THEN [curVersion, pagesLeft, curPagePtr] _ YggLogBasicInternal.AdvanceChunk[] <> ELSE curPagePtr _ curPagePtr + YggLog.wordsPerPage * UNITS[WORD]; <> TRUSTED {Header[curPagePtr].valid _ FALSE;}; curPageRecordID _ YggLogInline.AddC[curPageRecordID, YggLog.wordsPerPage]; curPageWordsUsed _ 0; }; END.