YggLogBasicWriteImpl.mesa
Copyright © 1985, 1987 by Xerox Corporation. All rights reserved.
Implements log writing. Seperate from YggLogBasicImpl to have its own monitor.
Last edited by
MBrown on January 30, 1984 11:44:13 am PST
Carl Hauser, March 10, 1987 5:41:17 pm PST
Bob Hagmann May 12, 1988 3:10:19 pm PDT
DIRECTORY
Basics,
YggEnvironment,
YggLog,
YggLogBasic,
YggLogInline,
YggLogBasicInternal,
YggLogRep,
VM;
YggLogBasicWriteImpl:
CEDAR MONITOR
IMPORTS
Basics, YggLog, YggLogBasicInternal, YggLogInline, VM
BEGIN
PageNumber: TYPE = YggEnvironment.PageNumber;
RecordID: TYPE = YggLog.RecordID;
CallerProgrammingError:
ERROR =
CODE;
Nonspecific error that clients are not expected to catch.
Monitored state:
curPageRecordID: YggLog.RecordID;
curPageWordsUsed: CARD;
curPagePtr:
LONG
POINTER ←
NIL;
Points to first word of current page buffer.
pagesLeft:
INT;
Number of pages in buffer represented by curPagePtr.
curVersion: YggLogRep.PageVersion;
Version bit for page now being written.
nPagesWritten: INT ← 0;
nPartFullPagesWritten:
INT ← 0;
for curiosity only
Starting and stopping logging
OpenForPut:
PUBLIC
ENTRY
PROC [
nextPage: PageNumber, version: YggLogRep.PageVersion, nextRecord: RecordID] = {
nextRecord must be page-aligned.
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;
ensure that first store into page clears valid bit.
};
};
CloseForPut:
PUBLIC
ENTRY
PROC [] = {
YggLogBasicInternal.CloseBasicForPut[];
curPagePtr ← NIL;
};
Public writing
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;
! YggLog.WriteFailed (with monitor locked; this is a server-crashing error).
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
Write one log block.
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
Copy a run of words to the log block.
wordsThisCopy ← MIN[from.length, wordsThisBlock];
TRUSTED {Basics.Copy[
to: curPagePtr+curPageWordsUsed, from: from.base, nwords: wordsThisCopy]; };
curPageWordsUsed ← curPageWordsUsed+wordsThisCopy;
IF wordsThisCopy = wordsThisBlock THEN EXIT;
Assert wordsThisBlock > wordsThisCopy = from.length, from.rest # NIL.
wordsThisBlock ← wordsThisBlock-wordsThisCopy;
TRUSTED {from ← from.rest^;};
ENDLOOP;
YggLog block is now full. Is log record all written?
IF totalLen # 0
THEN {
-- YggLog record not all written.
Assert curPageWordsUsed = wordsPerPage.
Advance page and loop.
AdvancePage[];
from.base ← from.base+wordsThisCopy;
from.length ← from.length-wordsThisCopy;
isContinuation ← TRUE;
}
ELSE {
-- YggLog record all written.
Assert from.rest = NIL.
Advance page if log force or if not enough room on page for a header plus one word.
wpp: CARD = YggLog.wordsPerPage;
IF force
OR (curPageWordsUsed >= wpp-
SIZE[YggLogRep.Header])
THEN
AdvancePage[];
RETURN [thisRecord, CurrentRecordID[]];
}
ENDLOOP;
};
Force:
PUBLIC
PROC [followingRecord: RecordID] = {
YggLogBasic.Force.
AdvanceTo:
ENTRY
PROC [followingRecord: RecordID] = {
If the word before followingRecord is on the current page, skip the rest of
the current page.
TRUSTED {IF ConstArith.Compare[followingRecord, curPageRecordID] # greater
THEN RETURN; };
followingRecord must not be later than record about to be written.
TRUSTED {IF ConstArith.Compare[followingRecord, CurrentRecordID[]] = greater
THEN
RETURN WITH ERROR CallerProgrammingError; };
AdvancePage[];
};
AdvanceTo[followingRecord];
Note: tail monitor is not held during force.
YggLogBasicInternal.ForceTo[followingRecord];
};
Internal procedures and utilities
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 page is only partly full then terminate it with 0 word.
IF curPageWordsUsed # YggLog.wordsPerPage
THEN TRUSTED {
nPartFullPagesWritten ← nPartFullPagesWritten + 1;
LOOPHOLE[curPagePtr+curPageWordsUsed*SIZE[INT32], LONG POINTER TO CARD32]^ ← 0 };
nPagesWritten ← nPagesWritten + 1;
Mark current page "valid".
TRUSTED {Header[curPagePtr].valid ← TRUE;};
pagesLeft ← pagesLeft - 1;
IF pagesLeft = 0
THEN
[curVersion, pagesLeft, curPagePtr] ← YggLogBasicInternal.AdvanceChunk[]
may raise WriteFailed because log is full
ELSE
curPagePtr ← curPagePtr + YggLog.wordsPerPage;
Mark new page "invalid"
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
Introduce a separate monitor for log tail.
Changed by MBrown on June 22, 1982 11:46 am
Check for log full only when moving to new chunk. Force log without holding the tail
monitor.
Changed by MBrown on August 9, 1982 5:54 pm
Implement Force.
Changed by MBrown on August 12, 1982 4:15 pm
OpenForPut calls OpenCoreForPut, to avoid any calls from LogCoreImpl
to this module.
Changed by MBrown on September 10, 1982 10:09 pm
Bug: PutEntry loops with totalLen = wordsThisCopy = wordsThisBlock = 0.
Changed by MBrown on September 21, 1982 2:40 pm
Simplified by keeping curPageRecordID as one datum, instead of in two pieces.
Changed by MBrown on October 3, 1982 8:30 pm
Added CloseForPut, simplified ERRORs (all are now raised as a nonspecific
CallerProgrammingError, since clients won't be catching them).
Changed by MBrown on October 11, 1982 4:21 pm
Added RecordIDOfNextPut.
Changed by MBrown on November 10, 1982 3:10 pm
Added nPartFullPagesWritten.
Hauser, March 8, 1985 10:47:27 am PST
Nodified, added copyright.
Carl Hauser, October 4, 1985 1:33:03 pm PDT
Change "Log" to "AlpineLog"