-- file VirtSS.Mesa
-- edited by Schroeder, January 18, 1981 3:08 PM.
-- edited by Brotz, 10-Nov-80 15:32:49.

DIRECTORY
crD: FROM "CoreDefs",
exD: FROM "ExceptionDefs",
gsD: FROM "GlobalStorageDefs",
InlineDefs,
ovD: FROM "OverviewDefs",
ProcessDefs,
SystemDefs,
tfD: FROM "TempFileDefs",
vmD: FROM "VirtualMgrDefs";

VirtSS: PROGRAM
IMPORTS crD, exD, gsD, ProcessDefs, SystemDefs, tfD, vmD
EXPORTS vmD
SHARES vmD = PUBLIC

BEGIN

OPEN vmD;

-- Data Structures and Types.

tOCPageTable: TOCPageTable;

tOCPtr: POINTER TO TOC = @tOC;

tOC: TOC ← VirtualObject
[file: NIL,
memoryHeader: NIL,
open: FALSE,
vOVar: TOC
[pageTableHeader: @tOCPageTable,
filePageFF: 0, -- Next avail page in TOC file.
indexFF: 1, -- Next avail message index.
firstChange: 0, -- No changes yet.
mailFile: NIL]];

vMORoot: VirtualMessagePtr ← NIL;

tOCBufferPoolSize: CARDINAL = 3;
dMBufferPoolSize: CARDINAL = 3;
cMBufferPoolSize: CARDINAL = 3;


-- This is VirtSS, Start/Stop routines. See also VirtTOC.


VirtualizeTOC: PROCEDURE [user: crD.DMSUser, tOCFileName: crD.UFilename,
mailFileHandle: crD.UFileHandle, option: VirtualizeTOCOption]
RETURNS [firstUnSeen: TOCIndex, err: ovD.ErrorCode] =
-- Initializes the TOC virtual structure from the indicated file. Before calling this
-- procedure, the client is expected to have called CleanupTOC if necessary.
BEGIN
mtPtr: MemoryTableEntryPtr←NIL;
pages, lpn: PageNumber;
byte: CARDINAL;
index: TOCIndex;
tOCFPPtr: TOCFixedPartPtr;
zerothFP: TOCFixedPart ← [FALSE, FALSE, FALSE, FALSE, ’~, 0, 0, bugTrapValue, 0, 0];

BEGIN -- for EXITS

DoSomeYields: PROCEDURE =
{THROUGH [1 .. 3] DO ProcessDefs.Yield[]; ENDLOOP};

IF tOCPtr.open THEN exD.SysBug[];
[err, tOCPtr.file] ← crD.OpenFile[user, tOCFileName, update];
IF err # ovD.ok THEN RETURN; --toc file is not open yet

DoSomeYields[];

tOCPtr.open ← TRUE;
--now a call to CleanupTOC[dontResetChanges or delete] will work.
[err, pages, byte] ← crD.UFileLength[tOCPtr.file];
IF err # ovD.ok THEN RETURN;

lpn ← firstUnSeen ← tOCPtr.indexFF ← tOCPtr.firstChange ← 0;
tOCPtr.filePageFF ← pages;
--now a call to CleanupTOC[resetChanges] will work.
tOCPtr.mailFile ← mailFileHandle;
IF tOCPtr.memoryHeader = NIL THEN SetBufferPoolSize[tOCPtr, tOCBufferPoolSize];

IF option = new OR pages = 0 OR pages > tOCPageTableSize OR byte # 0
THEN GOTO truncateTOC;

FOR lpn IN [0 .. pages) DO
DoSomeYields[];
[mtPtr, ] ← GetMtPtr[tOCPtr, lpn, active]; -- can cause SysBug
IF mtPtr.address.numberOfEntries = 0
OR mtPtr.address.garbageDetector # tOCType
THEN {mtPtr.state ← unused; GOTO truncateTOC};
tOCPtr.pageTableHeader↑[lpn] ← [tOCPtr.indexFF]; -- 1st entry on page.
tOCPtr.indexFF ← tOCPtr.indexFF + mtPtr.address.numberOfEntries;
FOR index IN [tOCPtr.pageTableHeader[lpn] .. tOCPtr.indexFF)
WHILE tOCPtr.firstChange = 0 OR firstUnSeen = 0 DO
tOCFPPtr ← GetTOCAddressOnPage[index, mtPtr];
IF tOCFPPtr.changed AND tOCPtr.firstChange = 0
THEN tOCPtr.firstChange ← index;
IF ~tOCFPPtr.seen AND firstUnSeen = 0
THEN firstUnSeen ← index;
ENDLOOP;
ENDLOOP;

EXITS
truncateTOC =>
BEGIN
IF pages # lpn
THEN BEGIN
err ← crD.UFileTruncate[lpn, 0, tOCPtr.file];
IF err # ovD.ok THEN RETURN;
END;
tOCPtr.filePageFF ← lpn;
IF lpn = 0 THEN err ← ExtendTOC[@zerothFP, "dummy"L];
END;
END; -- for EXITS
END; -- of VirtualizeTOC.


CleanupTOC: PROCEDURE [option: CleanupTOCOption] =
BEGIN
p: MemoryTableEntryPtr;
pageHdrPtr: POINTER TO TOCPageHdr;
lastPage: PageNumber;

IF tOCPtr.open THEN
BEGIN
tOCPtr.open ← FALSE;
IF option = delete THEN GOTO deleteTOC;
IF option = resetChanges AND tOCPtr.firstChange # 0 THEN
BEGIN
[, p] ← FindTOCAddress[tOCPtr.firstChange];
lastPage ← p.logicalPageNumber;
pageHdrPtr ← LOOPHOLE[p.address];
pageHdrPtr.numberOfEntries
← tOCPtr.firstChange - tOCPtr.pageTableHeader[lastPage];
p.state ← dirty;
IF pageHdrPtr.numberOfEntries = 0 THEN lastPage ← lastPage - 1;
END
ELSE lastPage ← tOCPtr.filePageFF - 1;
FOR p ← tOCPtr.memoryHeader, p.next UNTIL p = NIL DO
IF p.logicalPageNumber <= lastPage AND p.state = dirty THEN
IF crD.WritePages[p.address, 512, p.filePageNumber, tOCPtr.file] # ovD.ok
THEN GOTO deleteTOC;
p.state ← unused;
ENDLOOP;
IF option = resetChanges AND lastPage + 1 < tOCPtr.filePageFF
THEN IF crD.UFileTruncate[lastPage + 1, 0, tOCPtr.file] # ovD.ok
THEN GOTO deleteTOC;
IF crD.CloseFile[tOCPtr.file] # ovD.ok THEN GOTO deleteTOC;

EXITS
deleteTOC =>
BEGIN
FOR p ← tOCPtr.memoryHeader, p.next UNTIL p = NIL DO
p.state ← unused;
ENDLOOP;
[] ← crD.DeleteFile[tOCPtr.file]; --ignore disk error on purpose
END;
END; -- of IF tOCPtr.open.
END; -- of CleanupTOC --


SetTOCValidity: PROCEDURE [valid: BOOLEAN] =
BEGIN
mtP: MemoryTableEntryPtr = GetMtPtr[tOCPtr, 0, active].mtPtr;
mtP↑.address↑.garbageDetector ← tOCType + (IF valid THEN 0 ELSE 1);
mtP↑.state ← dirty;
WritePageToFile[tOCPtr, mtP];
mtP↑.state ← clean;
END; --ofSetTOCValidity--


GetTOCPtr: PROCEDURE RETURNS [POINTER TO TOC] = {RETURN[tOCPtr]};
-- Used to communicate between the departments of this division.
-- Needed when broken into more than one file.


AllocateDisplayMessageObject: PROCEDURE
RETURNS [dMPtr: DisplayMessagePtr] =
BEGIN
dMPtr ← SystemDefs.AllocateHeapNode[SIZE[DisplayMessageObject]];
dMPtr↑ ← [file: , memoryHeader: NIL, open: FALSE,
vOVar: VMO[vMORoot, , , DM[ , ]]];
vMORoot ← dMPtr; -- Splice new one into the list.
SetBufferPoolSize[dMPtr, dMBufferPoolSize];
END; -- of AllocateDisplayMessageObject --


FreeVirtualMessageObject: PROCEDURE [vMPtr: VirtualMessagePtr] =
-- DM or CM.
BEGIN
p, back: VirtualMessagePtr;
mtPtr, mtPtr1: MemoryTableEntryPtr;
-- err: ovD.ErrorCode;
-- pages: crD.PageNumber;
IF (p ← vMORoot) = vMPtr
THEN vMORoot ← vMPtr.next -- Was at head.
ELSE BEGIN
UNTIL p = NIL DO
back←p;
p←p↑.next;
IF p = vMPtr THEN EXIT;
REPEAT
FINISHED => exD.SysBug[];
ENDLOOP;
back.next ← vMPtr.next; --Splice it out.
END;
WITH msg: vMPtr↑ SELECT FROM
DM => NULL;
CM => BEGIN
SystemDefs.FreeHeapNode [msg.charMap];
IF msg.file # NIL THEN tfD.FreeTempFile[msg.file];
END;
ENDCASE => exD.SysBug[];
FOR mtPtr ← vMPtr.memoryHeader, mtPtr ← mtPtr1
UNTIL mtPtr = NIL DO
-- Free pages and mte’s.
IF mtPtr.address # NIL
THEN gsD.ReturnMemoryPages[1, mtPtr.address];
mtPtr1 ← mtPtr.next;
SystemDefs.FreeHeapNode[mtPtr];
ENDLOOP;
SystemDefs.FreeHeapNode[vMPtr]
END; -- of FreeVirtualMessageObject --


CleanupCMs: PROCEDURE=
-- Truncates all backing files.
BEGIN
vMPtr: VirtualMessagePtr;
FOR vMPtr ← vMORoot, vMPtr.next UNTIL vMPtr = NIL DO
WITH msg: vMPtr↑ SELECT FROM
DM => NULL;
CM =>
BEGIN
IF msg.file # NIL THEN tfD.FreeTempFile[msg.file];
msg.open ← FALSE;
END;
ENDCASE => exD.SysBug[];
ENDLOOP;
END; -- of CleanupCMs.


AllocateComposeMessageObject: PROCEDURE RETURNS [cMPtr: ComposeMessagePtr] =
BEGIN
cMPtr ← SystemDefs.AllocateHeapNode[SIZE[ComposeMessageObject]];
cMPtr↑ ← [file: NIL,memoryHeader: NIL, open: FALSE,
vOVar: VMO[vMORoot, , ,
CM[FALSE, , , SystemDefs.AllocateHeapNode[SIZE[CMOCharMapTable]], 0]]];
vMORoot ← cMPtr; -- Splice new one into the list.
SetBufferPoolSize[cMPtr, cMBufferPoolSize];
END; -- of AllocateComposeMessageObject --


SetBufferPoolSize: PROCEDURE[vop: VirtualObjectPtr, size: CARDINAL] =
--Sets the number of Mte’s in the buffer pool of the designated object to
--the specified value. May add or delete Mte’s, and works if there are
--no Mte’s at all.
BEGIN
delta: INTEGER;
c: INTEGER ← 0;
mtPtr: MemoryTableEntryPtr;
last: MemoryTableEntryPtr←NIL;
IF size < 2 THEN exD.SysBug[]; -- Must be at least 2.
FOR mtPtr ← vop.memoryHeader, mtPtr.next UNTIL mtPtr = NIL DO
last ← mtPtr;
c ← c + 1;
ENDLOOP; -- c is current size.
delta ← size - c;

-- Note: only one (or none) of the below THROUGHs will happen..
SELECT delta FROM
> 0 =>
THROUGH [0 .. delta) DO -- To add pages.
mtPtr ← SystemDefs.AllocateHeapNode[SIZE[MemoryTableEntry]];
mtPtr.state ← unused;
mtPtr.address ← NIL;
IF last = NIL THEN vop.memoryHeader ← mtPtr ELSE last.next ← mtPtr;
mtPtr.next ← NIL;
last ← mtPtr;
ENDLOOP;
< 0 => BEGIN
THROUGH [delta .. 0) DO -- To delete pages.
[] ← MakeBufferPoolHeadUnused[vop];
mtPtr ← vop.memoryHeader; --We will delete mtPtr.
vop.memoryHeader ← mtPtr.next; -- First splice it out.
IF mtPtr.address # NIL THEN gsD.ReturnMemoryPages[1, mtPtr.address];
SystemDefs.FreeHeapNode[mtPtr];
ENDLOOP;
WITH vo: vop↑ SELECT FROM
VMO => VoidCharCache[@vo.get];
ENDCASE;
END;
ENDCASE;
END; -- of SetBufferPoolSize --


SetTOCBufferPoolSize: PROCEDURE [size: CARDINAL] =
-- Adjust the size of the TOC buffer pool.
BEGIN
SetBufferPoolSize[GetTOCPtr[], size];
END; -- of SetTOCBufferPoolSize --


END. -- of VirtSS.