<> <> <> <> <> <> <> DIRECTORY Basics, YggEnvironment, YggLog, YggLogBasic, YggLogInline, YggLogBasicInternal, YggLogRep, VM; YggLogBasicWriteImpl: CEDAR MONITOR IMPORTS Basics, YggLog, YggLogBasicInternal, YggLogInline, VM EXPORTS YggLogBasic = BEGIN PageNumber: TYPE = YggEnvironment.PageNumber; RecordID: TYPE = YggLog.RecordID; CallerProgrammingError: ERROR = CODE; <> <> curPageRecordID: YggLog.RecordID; curPageWordsUsed: CARD; 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, force: BOOL, writeID: BOOL] RETURNS [thisRecord, followingRecord: RecordID] = TRUSTED { << Compute the total length of the block to be written, and touch all pages in it.>> totalLen: INT _ 0; FOR b: YggLog.BlockPtr _ @from, b.rest UNTIL b = NIL DO len: INT = b.length; IF len > 0 THEN { IF b.base # NIL THEN { offset: CARD _ 0; UNTIL offset >= len DO touch: CARDINAL _ LOOPHOLE[b.base + offset*SIZE[INT32], LONG POINTER TO CARDINAL]^; IF offset = len - 1 THEN EXIT; offset _ offset + VM.wordsPerPage; 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] _ PutEntry[totalLen, from, force, writeID]; IF force THEN YggLogBasicInternal.ForceTo[followingRecord: followingRecord]; }; PutEntry: ENTRY PROC [ totalLen: CARDINAL, from: YggLog.Block, force: BOOL, writeID: BOOL] RETURNS [thisRecord, followingRecord: RecordID] = { 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[]; IF writeID THEN TRUSTED { LOOPHOLE[from.base, LONG POINTER TO YggLogRep.CheckpointCompleteRecord].thisRecordID _ thisRecord; }; DO <> wordsThisBlock: CARD _ MIN[(YggLog.wordsPerPage-SIZE[YggLogRep.Header])-curPageWordsUsed, totalLen]; wordsThisCopy: CARD; totalLen _ totalLen-wordsThisBlock; --words remaining to be logged when this block finished << Write a header for the log block.>> << The first store to a page should be the one that sets valid _ FALSE.>> TRUSTED { Header[curPagePtr+curPageWordsUsed]^ _ [ valid: FALSE, version: curVersion, hasContinuation: (totalLen # 0), isContinuation: isContinuation, nWords: wordsThisBlock+SIZE[YggLogRep.Header]]; }; curPageWordsUsed _ curPageWordsUsed+SIZE[YggLogRep.Header] * SIZE[INT32]; DO <> wordsThisCopy _ MIN[from.length, wordsThisBlock]; TRUSTED {Basics.Copy[ to: curPagePtr+curPageWordsUsed, from: from.base, nwords: wordsThisCopy]; }; 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; isContinuation _ TRUE; } ELSE { -- YggLog record all written. <> <> wpp: CARD = YggLog.wordsPerPage; IF force OR (curPageWordsUsed >= wpp-SIZE[YggLogRep.Header]) THEN AdvancePage[]; RETURN [thisRecord, CurrentRecordID[]]; } 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*SIZE[INT32], 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; <> TRUSTED {Header[curPagePtr].valid _ FALSE;}; curPageRecordID _ YggLogInline.AddC[curPageRecordID, YggLog.wordsPerPage]; curPageWordsUsed _ 0; }; END. CHANGE LOG Created by MBrown on June 15, 1982 3:38 pm <> Changed by MBrown on June 22, 1982 11:46 am <> <> Changed by MBrown on August 9, 1982 5:54 pm <> Changed by MBrown on August 12, 1982 4:15 pm <> <> Changed by MBrown on September 10, 1982 10:09 pm <> Changed by MBrown on September 21, 1982 2:40 pm <> Changed by MBrown on October 3, 1982 8:30 pm <> <> Changed by MBrown on October 11, 1982 4:21 pm <> Changed by MBrown on November 10, 1982 3:10 pm <> <> <> <> <> <<>>