-- file VirtCom.mesa
-- edited by Kierr, February 8, 1978 11:32 AM.
-- edited by Schroeder, November 18, 1980 12:50 PM.
-- edited by Brotz, November 19, 1980 3:08 PM.
-- edited by Levin, November 19, 1980 10:15 AM.

-- Provides procedures common to VirtTOC, VirtCM, and VirtSS that manage the primary
-- memory buffers for TOC’s, DM’s, and CM’s. This module is expected to be in primary
-- memory whenever any of the other three are.

DIRECTORY
crD: FROM "CoreDefs",
exD: FROM "ExceptionDefs",
gsD: FROM "GlobalStorageDefs",
Inline,
intCommon: FROM "IntCommon",
ovD: FROM "OverviewDefs",
tfD: FROM "TempFileDefs",
vmD: FROM "VirtualMgrDefs";

VirtCom: PROGRAM
IMPORTS crD, exD, gsD, Inline, intC: intCommon, tfD, vmD
EXPORTS vmD
SHARES vmD =

PUBLIC BEGIN

OPEN vmD;



GetMessageChar: PROCEDURE [vs: VirtualMessagePtr, index: ovD.CharIndex]
RETURNS [CHARACTER] =
-- Obtains the index’th character (starting at zero) from the virtual structure vs.
BEGIN OPEN g: vs.get;
page: PageNumber; byte: CARDINAL;
IF NOT (index IN [g.first .. g.free)) THEN
WITH msg:vs↑ SELECT FROM
DM =>
BEGIN
IF index >= msg.textLength THEN exD.SysBug[];
[page, byte] ← Inline.DIVMOD[msg.firstByte + index, 512];
page ← page + msg.firstPage;
[g.mtPtr,] ← GetMtPtr[@msg, page, active];
g.first ← IF index > byte THEN index - byte ELSE 0;
g.floor ← IF g.first = 0 THEN byte - index ELSE 0;
g.free ← MIN[index + 512 - byte, vs.textLength];
g.string ← LOOPHOLE[g.mtPtr.address - 2, STRING];
END;
CM =>
BEGIN
[page, byte] ← MapCharIndexToPageByte[@msg, index];
IF SetGetCacheForCMPage[old, @msg, page, index - byte] # ovD.ok
THEN exD.SysBug[]; --Can’t happen for "old".
END;
ENDCASE;
RETURN[g.string[index + g.floor - g.first]];
END; -- of GetMessageChar --


VoidCharCache: PROCEDURE [c: POINTER TO CharCache] =
BEGIN
easyToSpotValue: ovD.CharIndex = 177777B;
c.first ← c.free ← easyToSpotValue; -- first=free is what counts
END; -- of VoidCharCache --


GetMtPtr: PROCEDURE [vop: VirtualObjectPtr, pn: PageNumber, pageState: GetMtPtrState]
RETURNS [mtPtr: MemoryTableEntryPtr, npn: PageNumber] =
-- Arranges for the designated page of the designated object to occupy a primary memory
-- buffer page, and returns a pointer to the corresponding Mte. Also returned is the new
-- number for the designated page, which in the case that vop points to a CM may be less
-- than pn because a compaction of the CM buffer pool has occured. If pageState = new
-- then buffer pool is NOT checked to see if the page is already there. If pageState =
-- active then the corresponding page will be read in from the backing file if the page is
-- not already in the buffer pool.
BEGIN
oneBeforeMtPtr: MemoryTableEntryPtr ← NIL;
rpn: PageNumber; -- page removed by a compaction
compacted: BOOLEAN;
npn ← pn; -- guess that returned pn is to be the same as requested pn
IF pageState # new THEN --see if page is already in the buffer pool
FOR mtPtr ← vop.memoryHeader, mtPtr.next UNTIL mtPtr = NIL DO
IF mtPtr.state # unused AND mtPtr.logicalPageNumber = pn THEN
BEGIN --found page in the buffer pool
IF oneBeforeMtPtr # NIL THEN
BEGIN
oneBeforeMtPtr.next ← mtPtr.next;
mtPtr.next ← vop.memoryHeader;
vop.memoryHeader ← mtPtr;
END;
RETURN; --page was already in the buffer pool
END;
oneBeforeMtPtr ← mtPtr;
ENDLOOP;
[compacted, rpn] ← MakeBufferPoolHeadUnused[vop];
mtPtr ← vop.memoryHeader; -- get a pointer to the unused Mte
mtPtr.state ← clean;
mtPtr.filePageNumber ← mtPtr.logicalPageNumber ← pn; -- guess that they are the same
IF mtPtr.address = NIL THEN -- no buffer page yet for this entry
mtPtr.address ← gsD.GetMemoryPages[1];
WITH vo: vop↑ SELECT FROM -- if we have a CM page numbers may be wrong
VMO =>
WITH msg: vo SELECT FROM
CM =>
BEGIN
IF compacted AND pn > rpn THEN mtPtr.logicalPageNumber ← npn ← pn-1;
mtPtr.filePageNumber ← msg.charMap[npn].page;
END;
ENDCASE;
ENDCASE;
IF pageState = active THEN ReadPageFromFile[vop, mtPtr];
RETURN;
END; -- of GetMtPtr --


MakeBufferPoolHeadUnused: PROCEDURE [vop: VirtualObjectPtr]
RETURNS [compacted: BOOLEAN, rpn: PageNumber] =
-- Finds or makes an unused mte and relinks it to head of lru list. Compacted is returned
-- FALSE unless the vop designates a CM and finding an unused Mte has caused
-- compaction in the CM’s buffer pool. When compacted is returned TRUE, the rpn
-- returns the number of the CM page removed by the compaction.
BEGIN
oneBeforeMtPtr: MemoryTableEntryPtr ← NIL;
mtPtr: MemoryTableEntryPtr;
compacted ← FALSE;
FOR mtPtr ← vop.memoryHeader, mtPtr.next UNTIL mtPtr = NIL DO
IF mtPtr.state = unused THEN
BEGIN -- have found an unused Mte
IF oneBeforeMtPtr # NIL THEN
BEGIN -- relink to the head of the buffer pool
oneBeforeMtPtr.next ← mtPtr.next;
mtPtr.next ← vop.memoryHeader;
vop.memoryHeader ← mtPtr;
END;
RETURN;
END;
oneBeforeMtPtr ← mtPtr;
ENDLOOP;
-- there was no unused Mte so we must make one
WITH vo: vop↑ SELECT FROM -- if we have a CM compaction may be possible
VMO =>
WITH msg: vo SELECT FROM
CM => [compacted, rpn] ← TryToCompactBufferPool[@msg];
ENDCASE;
ENDCASE;
IF NOT compacted THEN
BEGIN -- take last buffer in the lru list
IF oneBeforeMtPtr.state = dirty THEN WritePageToFile[vop, oneBeforeMtPtr];
oneBeforeMtPtr.state ← unused;
END;
-- now there is an unused Mte for sure so find it and relink it
oneBeforeMtPtr ← NIL;
FOR mtPtr ← vop.memoryHeader, mtPtr.next UNTIL mtPtr.state = unused DO
oneBeforeMtPtr ← mtPtr;
ENDLOOP;
IF oneBeforeMtPtr # NIL THEN
BEGIN -- relink to the head of the buffer pool
oneBeforeMtPtr.next ← mtPtr.next;
mtPtr.next ← vop.memoryHeader;
vop.memoryHeader ← mtPtr;
END;
RETURN;
END; -- of MakeBufferPoolHeadUnused.


WritePageToFile: PROCEDURE [vop: VirtualObjectPtr, mtPtr: MemoryTableEntryPtr] =
-- Writes the file page associated with the designated Mte.
BEGIN
IF vop.file = NIL THEN vop.file ← tfD.AllocateTempFile[];
BombIfIOError[crD.WritePages[mtPtr.address, 512, mtPtr.filePageNumber, vop.file]];
RETURN;
END; -- of WritePageToFile --


ReadPageFromFile: PROCEDURE [vop: VirtualObjectPtr, mtPtr: MemoryTableEntryPtr] =
-- Reads the file page associated with the designated Mte.
BEGIN
IF vop.file = NIL THEN exD.SysBug[];
BombIfIOError[crD.ReadPages[mtPtr.address, 512, mtPtr.filePageNumber, vop.file].erc];
RETURN;
END; -- of ReadPageFromFile --


BombIfIOError: PROCEDURE [erc: ovD.ErrorCode] =
-- Call SysBug if erc isn’t "ok".
BEGIN
IF erc # ovD.ok THEN exD.SysBug
[SELECT erc FROM
ovD.diskFull => exD.diskFull,
ovD.fileTooBig => exD.fileTooLong,
ENDCASE => exD.ioError];
RETURN;
END; -- of BombIfIOError --




END. -- of VirtCom