-- FullBlownTestFilePageMgrImpl.mesa
-- Last edited by:
-- Kolling on November 9, 1983 5:00 pm

DIRECTORY

AlpineEnvironment
USING[FileID, PageCount, PageNumber, PageRun, VolumeID,
wordsPerPage],
AlpineInternal
USING[FileHandle],
CedarVersion
USING[machineType],
File
USING[Type],
FileIO
USING[Open],
FileTypes
USING[tUntypedFile],
FileMap
USING[GetNext, Handle, Initialize, Register],
FileMapPrivate
USING[FileHandle],
FilePageMgr
USING[CreateWithID, Delete, ExistingFile, FileInUse,
ForceOutEverything, ForceOutFile, ForceOutVMPageSet,
GetAttributes, GetSize, InitializeFilePageMgr, NoSuchFile,
PageRunArgIllegal, PageRunExtendsPastEof, ReadAheadPages,
ReadAheadLogPages, ReadLeaderPages, ReadPages, ReadLogPages,
ReleaseState, ReleaseVMPageSet, RestoreCacheToCleanState,
SetSize, ShareVMPageSet, UsePages, UseLogPages,VMPageSet],
IO
USING[card, Close, GetCard, Handle, PutF,
PutRope, rope],
Process
USING[priorityBackground, priorityNormal, SetPriority, Yield],
RandomCard
USING[Choose, Init],
Rope
USING[ROPE],
SafeStorage
USING[ReclaimCollectibleObjects],
System
USING[GetClockPulses, MicrosecondsToPulses, Pulses],
SystemInternal
USING[UniversalID],
Volume
USING[SystemID];


FullBlownTestFilePageMgrImpl: PROGRAM
IMPORTS AI: AlpineInternal, AE: AlpineEnvironment, CedarVersion, FileIO, FileMap, FPM:
FilePageMgr, IO, Process, RandomCard, SafeStorage, System, Volume =


BEGIN


GetParams: PROCEDURE =
BEGIN
paramStreamHandle: IO.Handle ← FileIO.Open["FullBlownTest.Params",
read, oldOnly];
NNormalProcesses ← paramStreamHandle.GetCard[];
NErrorProcesses ← paramStreamHandle.GetCard[];
NSharingProcesses ← paramStreamHandle.GetCard[];
IF (NNormalProcesses + NErrorProcesses + NSharingProcesses) = 0 THEN ERROR;
IF (NLoops ← paramStreamHandle.GetCard[]) = 0 THEN ERROR;
IF (MaxFilesPerProcess ← paramStreamHandle.GetCard[]) = 0 THEN ERROR;
IF (MaxFileSize ← paramStreamHandle.GetCard[]) = 0 THEN ERROR;
IF (MaxNPagesEachTime ← paramStreamHandle.GetCard[]) = 0 THEN ERROR;
IF (MaxOpsPerFileCycle ← paramStreamHandle.GetCard[]) = 0 THEN ERROR;
IF (NNormalChunksInCache ← paramStreamHandle.GetCard[]) = 0 THEN ERROR;
IF (NLogChunksInCache ← paramStreamHandle.GetCard[]) = 0 THEN ERROR;
IF (NLeaderChunksInCache ← paramStreamHandle.GetCard[]) = 0 THEN ERROR;
paramStreamHandle.Close[];
resultsStreamHandle.PutF["NNormalChunksInCache: %g NLogChunksInCache: %g NLeaderChunksInCache: %g*n*n", IO.card[NNormalChunksInCache],
IO.card[NLogChunksInCache], IO.card[NLeaderChunksInCache]];
resultsStreamHandle.PutF["NNormalProcesses: %g NLoops: %g MaxFilesPerProcess: %g MaxFileSize: %g MaxNPagesEachTime: %g", IO.card[NNormalProcesses],
IO.card[NLoops], IO.card[MaxFilesPerProcess],
IO.card[MaxFileSize], IO.card[MaxNPagesEachTime]];
resultsStreamHandle.PutF[" MaxOpsPerFileCycle: %g*n",
IO.card[MaxOpsPerFileCycle]];
resultsStreamHandle.PutF["NErrorProcesses: %g NSharingProcesses: %g*n",
IO.card[NErrorProcesses], IO.card[NSharingProcesses]];
END;


SetUpFileData: PROCEDURE[] =
BEGIN
myFilesIDsStreamHandle: IO.Handle ←
FileIO.Open["noFilesFiles.IDs", write, none];
fileHandle: FileMap.Handle;
IF myFilesIDsStreamHandle.GetCard[] < NFiles THEN ERROR;
FOR index: CARDINAL IN [0..NFiles)
DO size: CARDINAL;
files[index].fileID ← GetFileID[myFilesIDsStreamHandle];
fileHandle ← FileMap.Register[VolumeID,
files[index].fileID];
size ← FPM.GetSize[fileHandle ! FPM.NoSuchFile => CONTINUE];
FPM.Delete[fileHandle ! FPM.NoSuchFile => CONTINUE];
ENDLOOP;
myFilesIDsStreamHandle.Close[];
END;


GetFileID: PROCEDURE[stream: IO.Handle] RETURNS[fileID: AE.FileID] =
BEGIN
uid: SystemInternal.UniversalID;
a: CARDINAL ← stream.GetCard[];
b: CARDINAL ← stream.GetCard[];
c: CARDINAL ← stream.GetCard[];
uid ← [processor: [physical[a: a, b: b, c: c]], sequence: stream.GetCard[]];
RETURN[LOOPHOLE[uid, AE.FileID]];
END;



CleanCache: PROCEDURE =
BEGIN
FPM.RestoreCacheToCleanState[];
END;


AllSomeOrSomeRandom: TYPE = {allFixed, allRandom, someFixed, someRandom};

FileInterval: TYPE = RECORD[SELECT allOrSomeRandom: AllSomeOrSomeRandom FROM
allFixed => [nPagesEachTime: CARDINAL],
allRandom => NULL,
someFixed => [firstPage, lastPage, nPagesEachTime: CARDINAL],
someRandom => NULL,
ENDCASE];

IOMode: TYPE = {read, readWithReadAhead, write};
IOReadMode: TYPE = IOMode[read..readWithReadAhead];

MapAndSetSizeIllegal: PROCEDURE[fileHandle: FileMap.Handle, fileIndex: CARDINAL] =
BEGIN
pageRun: AE.PageRun;
vMPageSet: FPM.VMPageSet;
pages: LONG POINTER;

BEGIN
pageRun ← [firstPage: files[fileIndex].size - 1, count: 0];
vMPageSet ← (IF files[fileIndex].type = normal
THEN FPM.ReadPages ELSE FPM.ReadLogPages)[fileHandle, pageRun
! FPM.PageRunArgIllegal => GOTO okay];
ERROR;
EXITS okay => NULL;
END;

BEGIN
IF files[fileIndex].size = 0 THEN RETURN;
pageRun ← [firstPage: files[fileIndex].size - 1,
count: RandomCard.Choose[min: 2, max: 5]];
vMPageSet ← (IF files[fileIndex].type = normal
THEN FPM.ReadPages ELSE FPM.ReadLogPages)[fileHandle, pageRun
! FPM.PageRunExtendsPastEof => GOTO okay];
ERROR;
EXITS okay => NULL;
END;

BEGIN
firstPage: CARDINAL ← files[fileIndex].size/2;
count: CARDINAL ← RandomCard.Choose[min: 1, max: 14];
newSize: CARDINAL ← IF firstPage = 0 THEN 0 ELSE firstPage - 1;
IF firstPage + count > files[fileIndex].size THEN count ← 1;
pageRun ← [firstPage: firstPage, count: count];
vMPageSet ← (IF files[fileIndex].type = normal
THEN FPM.ReadPages ELSE FPM.ReadLogPages)[fileHandle, pageRun];
pages ← vMPageSet.pages;
FOR index: CARDINAL IN [0..vMPageSet.pageRun.count)
DO
ReadDataPage[fileIndex, pages, vMPageSet.pageRun.firstPage + index];
pages ← pages + AE.wordsPerPage;
ENDLOOP;
BEGIN
FPM.SetSize[fileHandle, newSize ! FPM.FileInUse => GOTO okay];
ERROR;
EXITS okay => NULL;
END;
BEGIN
FPM.Delete[fileHandle ! FPM.FileInUse => GOTO okay];
ERROR;
EXITS okay => NULL;
END;
END;

FPM.ReleaseVMPageSet[vMPageSet, clean, FALSE];

END;

SequentialIO: PROCEDURE[fileHandle: FileMap.Handle, fileIndex: CARDINAL, ioMode:
IOMode, releaseState: FPM.ReleaseState, keep: BOOLEAN, includeLeader: BOOLEAN,
fileInterval: FileInterval] =
BEGIN
RandomFirstPage: PROCEDURE RETURNS[CARDINAL] = INLINE
BEGIN RETURN[RandomCard.Choose[min: 0, max: files[fileIndex].size - 1]];
END;
RandomLastPage: PROCEDURE RETURNS[CARDINAL] = INLINE
BEGIN RETURN[RandomCard.Choose[min: firstPage, max:
files[fileIndex].size - 1]];
END;
RandomNPagesEachTime: PROCEDURE RETURNS[CARDINAL] = INLINE
BEGIN RETURN[MIN[RandomCard.Choose[min: 1, max: MaxNPagesEachTime],
totalPages]];
END;
totalPages: CARDINAL;
times: CARDINAL;
startPage, nPages: CARDINAL;
firstPage, lastPage, nPagesEachTime: CARDINAL;
IF includeLeader
THEN BEGIN vMPageSet: FPM.VMPageSet ← FPM.ReadLeaderPages[fileHandle];
(IF ioMode = write THEN WriteLeaderPage ELSE
ReadLeaderPage)[fileIndex, vMPageSet.pages];
FPM.ReleaseVMPageSet[vMPageSet, releaseState, keep];
END;
IF files[fileIndex].size = 0 THEN RETURN;
WITH fileInt: fileInterval SELECT FROM
allFixed => BEGIN firstPage ← 0; lastPage ← files[fileIndex].size - 1; END;
allRandom => BEGIN firstPage ← 0; lastPage ← files[fileIndex].size - 1; END;
someFixed => BEGIN firstPage ← fileInt.firstPage; lastPage ←
fileInt.lastPage; END;
someRandom => BEGIN firstPage ← RandomFirstPage[]; lastPage ←
RandomLastPage[];
END;
ENDCASE => ERROR;
totalPages ← lastPage - firstPage + 1;
nPagesEachTime ← (WITH fileInt: fileInterval SELECT FROM
allFixed => fileInt.nPagesEachTime,
allRandom => RandomNPagesEachTime[],
someFixed => fileInt.nPagesEachTime,
someRandom => RandomNPagesEachTime[],
ENDCASE => ERROR);
times ← (totalPages + (nPagesEachTime - 1))/nPagesEachTime;
startPage ← firstPage;
nPages ← nPagesEachTime;
FOR index: CARDINAL IN [0..times)
DO IF index = times - 1 THEN nPages ← lastPage - startPage + 1;
HandleNPages[fileHandle, fileIndex, ioMode, releaseState, keep, startPage,
nPages];
DelayForNPages[nPages];
startPage ← startPage + nPages;
ENDLOOP;
END;

HandleNPages: PROCEDURE[fileHandle: FileMap.Handle, fileIndex: CARDINAL,
ioMode: IOMode, releaseState: FPM.ReleaseState, keep: BOOLEAN, firstPage, nPages:
CARDINAL] =
BEGIN
pageRun: AE.PageRun ← [firstPage: firstPage, count: nPages];
vMPageSet: FPM.VMPageSet;
pages: LONG POINTER;
pageNumber: CARDINAL ← firstPage;
sharing: BOOLEAN ← FALSE;
DO
vMPageSet ← (SELECT TRUE FROM
ioMode IN IOReadMode => IF files[fileIndex].type = normal
THEN FPM.ReadPages ELSE FPM.ReadLogPages,
ENDCASE => IF files[fileIndex].type = normal THEN FPM.UsePages ELSE
FPM.UseLogPages)[fileHandle, pageRun];
pages ← vMPageSet.pages;
IF vMPageSet.pageRun.count > pageRun.count THEN ERROR;
IF RandomCard.Choose[min: 0, max: 10] = 1
THEN BEGIN sharing ← TRUE;
FPM.ShareVMPageSet[vMPageSet];
END;
FOR index: CARDINAL IN [0..vMPageSet.pageRun.count)
DO
(IF ioMode IN IOReadMode
THEN ReadDataPage ELSE WriteDataPage)[fileIndex, pages,
vMPageSet.pageRun.firstPage + index];
pageNumber ← pageNumber + 1;
pages ← pages + AE.wordsPerPage;
ENDLOOP;
IF RandomCard.Choose[min: 0, max: 10] = 1
THEN FPM.ForceOutVMPageSet[vMPageSet];
FPM.ReleaseVMPageSet[vMPageSet, releaseState, keep];
IF sharing THEN BEGIN FPM.ReleaseVMPageSet[vMPageSet, releaseState, keep];
sharing ← FALSE;
END;
pageRun.firstPage ← pageRun.firstPage + vMPageSet.pageRun.count;
pageRun.count ← pageRun.count - vMPageSet.pageRun.count;
IF pageRun.count = 0 THEN EXIT;
ENDLOOP;
IF ((ioMode = readWithReadAhead) AND (pageRun.firstPage <
files[fileIndex].size))
THEN BEGIN
pageRun.count ← MIN[nPages + nPages, files[fileIndex].size -
(firstPage + nPages)];
(IF files[fileIndex].type = normal THEN FPM.ReadAheadPages ELSE
FPM.ReadAheadLogPages)[fileHandle, pageRun];
END;
END;


Halfway: CARDINAL ← AE.wordsPerPage/2;

ReadLeaderPage: PROCEDURE[fileIndex: CARDINAL, pages: LONG POINTER] =
BEGIN
wordCount: CARDINAL ← RandomCard.Choose[min: 0, max: Halfway - 1];
IF LOOPHOLE[(pages + wordCount)^, CARDINAL] # leaderTouch[fileIndex]
THEN ERROR;
wordCount ← RandomCard.Choose[min: Halfway, max: AE.wordsPerPage - 1];
IF LOOPHOLE[(pages + wordCount)^, CARDINAL] # wordCount + fileIndex
THEN ERROR;
END;

OneThird: CARDINAL ← AE.wordsPerPage/3;
TwoThird: CARDINAL ← 2*OneThird; -- close enough.


WriteLeaderPage: PROCEDURE[fileIndex: CARDINAL, pages: LONG POINTER] =
BEGIN
leaderTouch[fileIndex] ← leaderTouch[fileIndex] + 1;
FOR wordCount: CARDINAL IN [0..Halfway)
DO (pages + wordCount)^ ← leaderTouch[fileIndex]; ENDLOOP;
FOR wordCount: CARDINAL IN [Halfway..AE.wordsPerPage)
DO (pages + wordCount)^ ← wordCount + fileIndex; ENDLOOP;
END;



ReadDataPage: PROCEDURE[fileIndex: CARDINAL, pages: LONG POINTER, pageNumber:
CARDINAL] =
BEGIN
IF LOOPHOLE[(pages + RandomCard.Choose[min: 0, max: OneThird - 1])^,
CARDINAL] # dataTouch[fileIndex][pageNumber] THEN ERROR;
IF LOOPHOLE[(pages + RandomCard.Choose[min: OneThird, max: TwoThird - 1])^,
CARDINAL] # fileIndex THEN ERROR;
IF LOOPHOLE[(pages + RandomCard.Choose[min: TwoThird, max: AE.wordsPerPage
- 1])^, CARDINAL] # pageNumber THEN ERROR;
END;


WriteDataPage: PROCEDURE[fileIndex: CARDINAL, pages: LONG POINTER, pageNumber:
CARDINAL] =
BEGIN
dataTouch[fileIndex][pageNumber] ← dataTouch[fileIndex][pageNumber] + 1;
FOR wordCount: CARDINAL IN [0..OneThird)
DO (pages + wordCount)^ ← dataTouch[fileIndex][pageNumber]; ENDLOOP;
FOR wordCount: CARDINAL IN [OneThird..TwoThird)
DO (pages + wordCount)^ ← fileIndex; ENDLOOP;
FOR wordCount: CARDINAL IN [TwoThird..AE.wordsPerPage)
DO (pages + wordCount)^ ← pageNumber; ENDLOOP;
END;


delayConst: LONG CARDINAL ← 4000;

DelayForNPages: PROCEDURE[nPages: CARDINAL] =
BEGIN
newPulseLimit: System.Pulses;
IF delayConst = 0 THEN RETURN;
newPulseLimit ← [System.GetClockPulses[] +
System.MicrosecondsToPulses[delayConst*LONG[nPages]]];
Process.SetPriority[Process.priorityBackground];
DO
Process.Yield[];
IF System.GetClockPulses[] >= newPulseLimit THEN EXIT;
ENDLOOP;
Process.SetPriority[Process.priorityNormal];
END;

GetSequenceID: PROCEDURE[fileHandle: FileMap.Handle] RETURNS[fileSequenceID: LONG
CARDINAL] =
BEGIN
uid: SystemInternal.UniversalID ← LOOPHOLE[LOOPHOLE[fileHandle,
FileMapPrivate.FileHandle].fileID];
RETURN[uid.sequence];
END;


BasicProcess: PROCEDURE [firstFileIndex, maxFileIndex: CARDINAL]=
BEGIN
debugArray: DebugArray;
debugArrayIndex: INTEGER ← 0;
debugSize: CARDINAL;
FOR indexLoops: CARDINAL IN[0..NLoops)
DO
FOR indexFiles: CARDINAL IN [firstFileIndex..maxFileIndex)
DO
oldSize: CARDINAL;
resets: CARDINAL ← 0;
attemptedBadCreate: BOOLEAN ← FALSE;
fileHandle: FileMap.Handle ← FileMap.Register[VolumeID,
files[indexFiles].fileID];
IF NOT files[indexFiles].exists
THEN BEGIN files[indexFiles].size ← RandomCard.Choose[min: 0, max:
MaxFileSize];
FPM.CreateWithID[fileHandle, files[indexFiles].size,
FileTypes.tUntypedFile];
debugArray[debugArrayIndex] ←
[GetSequenceID[fileHandle], create, files[indexFiles].size];
debugArrayIndex ← (IF debugArrayIndex = MaxDebugIndex - 1
THEN 0 ELSE debugArrayIndex + 1);
files[indexFiles].exists ← TRUE;
END;
files[indexFiles].nOps ← RandomCard.Choose[min: 0, max: MaxOpsPerFileCycle];
files[indexFiles].totalNOps ← files[indexFiles].totalNOps +
files[indexFiles].nOps;
IF RandomCard.Choose[min: 0, max: 1] = 1
THEN BEGIN attemptedBadCreate ← TRUE;
debugArray[debugArrayIndex] ←
[GetSequenceID[fileHandle], badCreate, files[indexFiles].size];
debugArrayIndex ← (IF debugArrayIndex = MaxDebugIndex - 1
THEN 0 ELSE debugArrayIndex + 1);
FPM.CreateWithID[fileHandle, files[indexFiles].size, FileTypes.tUntypedFile
! FPM.ExistingFile => GOTO okay];
ERROR;
EXITS okay => NULL;
END;

IF (debugSize ← FPM.GetSize[fileHandle]) # files[indexFiles].size
THEN ERROR;
BEGIN
chkType: File.Type;
chkVolID: AE.VolumeID;
[chkType, chkVolID] ← FPM.GetAttributes[fileHandle];
IF chkType # FileTypes.tUntypedFile THEN ERROR;
IF chkVolID # VolumeID THEN ERROR;
END;

IF files[indexFiles].nOps # 0
THEN SequentialIO[fileHandle, indexFiles, write, writeBatchedNoWait,
--keep--FALSE, --leader--TRUE, [allFixed[files[indexFiles].size]]];

FOR indexOps: CARDINAL IN [0..files[indexFiles].nOps)
DO
ioMode: IOMode ← (SELECT RandomCard.Choose[0, 2] FROM
0 => read, 1 => readWithReadAhead, 2 => write, ENDCASE => ERROR);
release: FPM.ReleaseState ← (SELECT RandomCard.Choose[0, 4] FROM
0 => writeIndividualWait, 1 => writeBatchedWait, 2 =>
writeIndividualNoWait, 3 => writeBatchedNoWait, 4 => clean,
ENDCASE => ERROR);
keep: BOOLEAN ← (RandomCard.Choose[0, 1] = 0);
leader: BOOLEAN ← (RandomCard.Choose[0, 1] = 0);
fileInterval: FileInterval ← (SELECT RandomCard.Choose[0, 2] FROM
0 => [allFixed[RandomCard.Choose[1, MaxNPagesEachTime]]],
1 => [allRandom[]],
2 => [someRandom[]],
ENDCASE => ERROR);
IF (RandomCard.Choose[0, files[indexFiles].nOps] MOD 10) = 0
THEN BEGIN resets ← resets + 1;
oldSize ← files[indexFiles].size;
files[indexFiles].size ← RandomCard.Choose[min: 0, max:
MaxFileSize];
debugArray[debugArrayIndex] ←
[GetSequenceID[fileHandle], setSize, files[indexFiles].size];
debugArrayIndex ← (IF debugArrayIndex = MaxDebugIndex - 1
THEN 0 ELSE debugArrayIndex + 1);
FPM.SetSize[fileHandle, files[indexFiles].size];
IF files[indexFiles].size > oldSize
THEN SequentialIO[fileHandle, indexFiles, write,
writeBatchedNoWait, --keep--FALSE, --leader--TRUE,
[someFixed[oldSize, files[indexFiles].size - 1,
RandomCard.Choose[1, MaxNPagesEachTime]]]];
END;
SequentialIO[fileHandle, indexFiles, ioMode, release, keep, leader,
fileInterval];
ENDLOOP;

IF RandomCard.Choose[min: 0, max: 10] = 1 THEN FPM.ForceOutFile[fileHandle];

IF RandomCard.Choose[min: 0, max: 1] = 1
THEN BEGIN
IF (debugSize ← FPM.GetSize[fileHandle]) # files[indexFiles].size
THEN ERROR;
debugArray[debugArrayIndex] ←
[GetSequenceID[fileHandle], delete, files[indexFiles].size];
debugArrayIndex ← (IF debugArrayIndex = MaxDebugIndex - 1
THEN 0 ELSE debugArrayIndex + 1);
FPM.Delete[fileHandle];
files[indexFiles].exists ← FALSE;
END;

ENDLOOP;

ENDLOOP;

FOR indexFiles: CARDINAL IN [firstFileIndex..maxFileIndex)
DO
IF files[indexFiles].exists
THEN BEGIN fileHandle: FileMap.Handle ← FileMap.Register[VolumeID,
files[indexFiles].fileID];
IF (debugSize ← FPM.GetSize[fileHandle]) # files[indexFiles].size
THEN ERROR;
debugArray[debugArrayIndex] ←
[GetSequenceID[fileHandle], delete, files[indexFiles].size];
debugArrayIndex ← (IF debugArrayIndex = MaxDebugIndex - 1
THEN 0 ELSE debugArrayIndex + 1);
FPM.Delete[fileHandle];
files[indexFiles].exists ← FALSE;
END;
ENDLOOP;
END;


ErrorProcess: PROCEDURE [firstFileIndex: CARDINAL]=
BEGIN
debugArray: DebugArray;
debugArrayIndex: INTEGER ← 0;
debugSize: CARDINAL;
FOR indexLoops: CARDINAL IN[0..100)
DO
FOR indexFiles: CARDINAL IN [firstFileIndex..firstFileIndex +
NFilesPerErrorProcess)
DO
fileHandle: FileMap.Handle ← FileMap.Register[VolumeID,
files[indexFiles].fileID];
IF RandomCard.Choose[min: 0, max: 1] = 1
THEN BEGIN tempSize: CARDINAL ← IF files[indexFiles].exists
THEN files[indexFiles].size ELSE RandomCard.Choose[min: 0, max:
MaxFileSize];
FPM.CreateWithID[fileHandle, tempSize, FileTypes.tUntypedFile
! FPM.ExistingFile => GOTO fileAlreadyExists];
IF files[indexFiles].exists THEN ERROR;
debugArray[debugArrayIndex] ←
[GetSequenceID[fileHandle], create, tempSize];
debugArrayIndex ← (IF debugArrayIndex = MaxDebugIndex - 1
THEN 0 ELSE debugArrayIndex + 1);
files[indexFiles].exists ← TRUE;
files[indexFiles].size ← tempSize;
EXITS fileAlreadyExists =>
IF (NOT files[indexFiles].exists) THEN ERROR;
END;
files[indexFiles].nOps ← RandomCard.Choose[min: 0, max: 4];
files[indexFiles].totalNOps ← files[indexFiles].totalNOps +
files[indexFiles].nOps;

BEGIN
debugSize ← FPM.GetSize[fileHandle ! FPM.NoSuchFile => GOTO noSuchFile];
IF NOT files[indexFiles].exists THEN ERROR;
IF debugSize # files[indexFiles].size THEN ERROR;
EXITS noSuchFile => IF files[indexFiles].exists THEN ERROR;
END;
BEGIN
chkType: File.Type;
chkVolID: AE.VolumeID;
[chkType, chkVolID] ← FPM.GetAttributes[fileHandle
! FPM.NoSuchFile => GOTO noSuchFile];
IF NOT files[indexFiles].exists THEN ERROR;
IF chkType # FileTypes.tUntypedFile THEN ERROR;
IF chkVolID # VolumeID THEN ERROR;
EXITS noSuchFile => IF files[indexFiles].exists THEN ERROR;
END;

BEGIN
SequentialIO[fileHandle, indexFiles, write, writeBatchedNoWait,
--keep--FALSE, --leader--TRUE, [allFixed[files[indexFiles].size]]
! FPM.NoSuchFile => GOTO noSuchFile];
IF NOT files[indexFiles].exists THEN ERROR;
EXITS noSuchFile => IF files[indexFiles].exists THEN ERROR;
END;

FOR indexOps: CARDINAL IN [0..files[indexFiles].nOps)
DO
ioMode: IOMode ← (SELECT RandomCard.Choose[0, 2] FROM
0 => read, 1 => readWithReadAhead, 2 => write, ENDCASE => ERROR);
release: FPM.ReleaseState ← (SELECT RandomCard.Choose[0, 4] FROM
0 => writeIndividualWait, 1 => writeBatchedWait, 2 =>
writeIndividualNoWait, 3 => writeBatchedNoWait, 4 => clean,
ENDCASE => ERROR);
keep: BOOLEAN ← (RandomCard.Choose[0, 1] = 0);
leader: BOOLEAN ← (RandomCard.Choose[0, 1] = 0);
fileInterval: FileInterval ← (SELECT RandomCard.Choose[0, 2] FROM
0 => [allFixed[RandomCard.Choose[1, MaxNPagesEachTime]]],
1 => [allRandom[]],
2 => [someRandom[]],
ENDCASE => ERROR);
IF (RandomCard.Choose[0, files[indexFiles].nOps] MOD 10) = 0
THEN BEGIN newSize: CARDINAL ← RandomCard.Choose[min: 0, max:
MaxFileSize];
oldSize: CARDINAL;
FPM.SetSize[fileHandle, newSize
! FPM.NoSuchFile => GOTO noSuchFile];
IF NOT files[indexFiles].exists THEN ERROR;
oldSize ← files[indexFiles].size;
files[indexFiles].size ← newSize;
IF newSize > oldSize
THEN SequentialIO[fileHandle, indexFiles, write,
writeBatchedNoWait, --keep--FALSE, --leader--TRUE,
[someFixed[oldSize, files[indexFiles].size - 1,
RandomCard.Choose[1, MaxNPagesEachTime]]]];
debugArray[debugArrayIndex] ←
[GetSequenceID[fileHandle], setSize, newSize];
debugArrayIndex ← (IF debugArrayIndex = MaxDebugIndex - 1
THEN 0 ELSE debugArrayIndex + 1);
EXITS noSuchFile => IF files[indexFiles].exists THEN ERROR;
END;
SequentialIO[fileHandle, indexFiles, ioMode, release, keep, leader,
fileInterval! FPM.NoSuchFile => GOTO noSuchFile];
REPEAT noSuchFile => IF files[indexFiles].exists THEN ERROR;
ENDLOOP;

IF RandomCard.Choose[min: 0, max: 10] = 1
THEN BEGIN FPM.ForceOutFile[fileHandle ! FPM.NoSuchFile => GOTO noSuchFile];
IF NOT files[indexFiles].exists THEN ERROR;
EXITS noSuchFile => IF files[indexFiles].exists THEN ERROR;
END;

IF ((RandomCard.Choose[min: 0, max: 6] >0) AND (files[indexFiles].exists))
THEN BEGIN -- map in illegal area.
MapAndSetSizeIllegal[fileHandle, indexFiles];
debugArray[debugArrayIndex] ← [GetSequenceID[fileHandle],
mapAndSetSize, files[indexFiles].size];
debugArrayIndex ← (IF debugArrayIndex = MaxDebugIndex - 1
THEN 0 ELSE debugArrayIndex + 1);
END;

IF RandomCard.Choose[min: 0, max: 1] = 1
THEN BEGIN
debugSize: CARDINAL;
BEGIN
debugSize ← FPM.GetSize[fileHandle
! FPM.NoSuchFile => GOTO noSuchFile];
IF NOT files[indexFiles].exists THEN ERROR;
IF debugSize # files[indexFiles].size THEN ERROR;
EXITS noSuchFile => IF files[indexFiles].exists THEN ERROR;
END;
FPM.Delete[fileHandle! FPM.NoSuchFile => GOTO noSuchFile];
IF NOT files[indexFiles].exists THEN ERROR;
debugArray[debugArrayIndex] ←
[GetSequenceID[fileHandle], delete, files[indexFiles].size];
debugArrayIndex ← (IF debugArrayIndex = MaxDebugIndex - 1
THEN 0 ELSE debugArrayIndex + 1);
files[indexFiles].exists ← FALSE;
EXITS noSuchFile => IF files[indexFiles].exists THEN ERROR;
END;

ENDLOOP;

ENDLOOP;

FOR indexFiles: CARDINAL IN [firstFileIndex..firstFileIndex +
NFilesPerErrorProcess)
DO
fileHandle: FileMap.Handle ← FileMap.Register[VolumeID,
files[indexFiles].fileID];
debugSize: CARDINAL;
BEGIN
debugSize ← FPM.GetSize[fileHandle
! FPM.NoSuchFile => GOTO noSuchFile];
IF NOT files[indexFiles].exists THEN ERROR;
IF debugSize # files[indexFiles].size THEN ERROR;
EXITS noSuchFile => IF files[indexFiles].exists THEN ERROR;
END;
BEGIN
FPM.Delete[fileHandle! FPM.NoSuchFile => GOTO noSuchFile];
IF NOT files[indexFiles].exists THEN ERROR;
files[indexFiles].exists ← FALSE;
EXITS noSuchFile => IF files[indexFiles].exists THEN ERROR;
END;
ENDLOOP;

END;


SubSharingProcess: PROCEDURE [firstFileIndex: CARDINAL] RETURNS [nOps:
ARRAY[0..NFilesPerSharingProcess) OF CARDINAL] =
BEGIN
debugSize: CARDINAL;
FOR index: CARDINAL IN [0..NFilesPerSharingProcess)
DO nOps[index] ← 0;
ENDLOOP;

FOR indexLoops: CARDINAL IN[0..NLoops)
DO
FOR indexFiles: CARDINAL IN [firstFileIndex..firstFileIndex +
NFilesPerSharingProcess)
DO
fileHandle: FileMap.Handle ← FileMap.Register[VolumeID,
files[indexFiles].fileID];
tempNOps: CARDINAL ← RandomCard.Choose[min: 0, max: MaxOpsPerFileCycle];
nOps[indexFiles - firstFileIndex] ← nOps[indexFiles - firstFileIndex] + tempNOps;

IF (debugSize ← FPM.GetSize[fileHandle]) # files[indexFiles].size
THEN ERROR;
BEGIN
chkType: File.Type;
chkVolID: AE.VolumeID;
[chkType, chkVolID] ← FPM.GetAttributes[fileHandle];
IF chkType # FileTypes.tUntypedFile THEN ERROR;
IF chkVolID # VolumeID THEN ERROR;
END;

FOR indexOps: CARDINAL IN [0..tempNOps)
DO
ioMode: IOMode ← (SELECT RandomCard.Choose[0, 1] FROM
0 => read, 1 => readWithReadAhead, ENDCASE => ERROR);
keep: BOOLEAN ← (RandomCard.Choose[0, 1] = 0);
leader: BOOLEAN ← (RandomCard.Choose[0, 1] = 0);
fileInterval: FileInterval ← (SELECT RandomCard.Choose[0, 2] FROM
0 => [allFixed[RandomCard.Choose[1, MaxNPagesEachTime]]],
1 => [allRandom[]],
2 => [someRandom[]],
ENDCASE => ERROR);
SequentialIO[fileHandle, indexFiles, ioMode, clean, keep, leader,
fileInterval];
ENDLOOP;

ENDLOOP;

ENDLOOP;

END;


SharingProcess: PROCEDURE [firstFileIndex: CARDINAL]=
BEGIN
NSubSharingProcesses: CARDINAL = 3;
mySubProcesses: ARRAY [0..NSubSharingProcesses) OF PROCESS RETURNS [nOps:
ARRAY[0..NFilesPerSharingProcess) OF CARDINAL];

FOR indexFiles: CARDINAL IN [firstFileIndex..firstFileIndex +
NFilesPerSharingProcess)
DO
fileHandle: FileMap.Handle ← FileMap.Register[VolumeID,
files[indexFiles].fileID];
files[indexFiles].size ← RandomCard.Choose[min: 0, max: MaxFileSize];
FPM.CreateWithID[fileHandle, files[indexFiles].size,
FileTypes.tUntypedFile];
files[indexFiles].exists ← TRUE;
SequentialIO[fileHandle, indexFiles, write, writeBatchedNoWait,
--keep--FALSE, --leader--TRUE, [allFixed[files[indexFiles].size]]];
ENDLOOP;


FOR indexProcesses: CARDINAL IN [0..NSubSharingProcesses)
DO
mySubProcesses[indexProcesses] ← FORK SubSharingProcess[firstFileIndex];
ENDLOOP;

FOR indexProcesses: CARDINAL IN [0..NSubSharingProcesses)
DO
tempNOps: ARRAY[0..NFilesPerSharingProcess) OF CARDINAL ←
JOIN mySubProcesses[indexProcesses];
FOR index: CARDINAL IN [0..NFilesPerSharingProcess)
DO files[firstFileIndex + index].totalNOps ← files[firstFileIndex +
index].totalNOps + tempNOps[index];
ENDLOOP;
ENDLOOP;

END;


VolumeID: AE.VolumeID = LOOPHOLE[Volume.SystemID[]];

Processes: TYPE = RECORD[SEQUENCE nProcesses: [0..LAST[CARDINAL]] OF
ProcessModel];
ProcessModel: TYPE = RECORD[process: PROCESS, nFiles: CARDINAL];

Files: TYPE = RECORD[SEQUENCE nFiles: [0..LAST[CARDINAL]] OF FileModel];
FileModel: TYPE = RECORD[fileID: AE.FileID, type: FileType, exists: BOOLEAN,
size: CARDINAL, totalNOps: CARDINAL, nOps: CARDINAL];
FileType: TYPE = {normal, log};

LeaderTouch: TYPE = RECORD[SEQUENCE nFiles: [0..LAST[CARDINAL]] OF CARDINAL];

DataTouch: TYPE = RECORD[SEQUENCE nFiles: [0..LAST[CARDINAL]] OF REF DataArray];
DataArray: TYPE = RECORD[SEQUENCE nPages: [0..LAST[CARDINAL]] OF CARDINAL];


processes: REF Processes;
files: REF Files;
leaderTouch: REF LeaderTouch;
dataTouch: REF DataTouch;

MaxDebugIndex: CARDINAL = 100;
DebugArray: TYPE = ARRAY [0..MaxDebugIndex) OF RECORD[fileSequenceID: LONG
CARDINAL, op: DebugOp ← inited, newSize: CARDINAL];
DebugOp: TYPE = {badCreate, create, setSize, delete, mapAndSetSize, inited};


PrintFileStats: PROCEDURE =
BEGIN
PutFileID: PROCEDURE[fileID: AE.FileID] =
BEGIN
uid: SystemInternal.UniversalID ← LOOPHOLE[fileID];
resultsStreamHandle.PutF[" %5g %5g %5g %10g ",
IO.card[uid.processor.a], IO.card[uid.processor.b],
IO.card[uid.processor.c], IO.card[uid.sequence]];
END;
FOR index: CARDINAL IN [0..NFiles)
DO
resultsStreamHandle.PutRope["file: "];
PutFileID[files[index].fileID];
resultsStreamHandle.PutF[" type: %g final size: %g totalNOps: %g*n",
IO.rope[(IF files[index].type = normal THEN "normal" ELSE "log")],
IO.card[files[index].size], IO.card[files[index].totalNOps]];
ENDLOOP;
FOR index: CARDINAL IN [0..NNormalProcesses)
DO
resultsStreamHandle.PutF["process: %g nFiles: %g*n",
IO.card[index], IO.card[processes[index].nFiles]];
ENDLOOP;

resultsStreamHandle.PutF["%g*n", IO.rope["For sharing processes: "]];

FOR index: CARDINAL IN [NNormalFiles..NNormalFiles +
NSharingProcesses*NFilesPerSharingProcess)
DO
resultsStreamHandle.PutRope["file: "];
PutFileID[files[index].fileID];
resultsStreamHandle.PutF[" type: %g final size: %g totalNOps: %g*n",
IO.rope[(IF files[index].type = normal THEN "normal" ELSE "log")],
IO.card[files[index].size], IO.card[files[index].totalNOps]];
ENDLOOP;
END;



Main: PROCEDURE =
BEGIN -- set up to call from CoPilot for debugging.
foo: CARDINAL ← 0;
BEGIN
ENABLE ANY => foo ← foo + 1; -- protect us from the tempcedarexec.
firstFileIndex, maxFileIndex: CARDINAL;

resultsStreamHandle ←
FileIO.Open["FullBlownTest.Results", overwrite, none];
resultsStreamHandle.PutF["*nMachine type: %g*n*n", IO.rope[SELECT
CedarVersion.machineType FROM dolphin => "Dolphin", dorado => "Dorado",
dandelion => "Dandelion", dicentra => "Dicentra", ENDCASE => "unknown"]];

[] ← RandomCard.Init[0];
GetParams[];

FPM.InitializeFilePageMgr[nNormalChunksInCache: NNormalChunksInCache,
nLogChunksInCache: NLogChunksInCache, nLeaderChunksInCache:
NLeaderChunksInCache];
resultsStreamHandle.PutRope["
Delay used is 4 milliseconds per page, after each transfer, unless superceeded.

"];

NProcesses ← NNormalProcesses + NErrorProcesses + NSharingProcesses;

processes ← NEW[Processes[NProcesses]];
NNormalFiles ← 0;
FOR indexProcesses: CARDINAL IN [0..NNormalProcesses)
DO
processes[indexProcesses].nFiles ← RandomCard.Choose[1, MaxFilesPerProcess];
NNormalFiles ← NNormalFiles + processes[indexProcesses].nFiles;
ENDLOOP;

NFiles ← NNormalFiles + (NFilesPerErrorProcess*NErrorProcesses) +
(NFilesPerSharingProcess*NSharingProcesses);

files ← NEW[Files[NFiles]];
leaderTouch ← NEW[LeaderTouch[NFiles]];
dataTouch ← NEW[DataTouch[NFiles]];

FOR index: CARDINAL IN [0..NFiles)
DO
files[index] ← [fileID: , type: IF
RandomCard.Choose[0,1] = 0 THEN normal ELSE log, exists: FALSE,
size: 0, totalNOps: 0, nOps: 0];
leaderTouch[index] ← 0;
dataTouch[index] ← NEW[DataArray[MaxFileSize]];
FOR index1: CARDINAL IN [0..MaxFileSize)
DO dataTouch[index][index1] ← 0;
ENDLOOP;
ENDLOOP;

IF NFiles > 100 THEN ERROR;


SetUpFileData[];

--SafeStorage.ReclaimCollectibleObjects[TRUE, TRUE];
--SafeStorage.ReclaimCollectibleObjects[TRUE, TRUE];
--IF FileMap.GetNext[NIL] # NIL THEN ERROR;
CleanCache[];

firstFileIndex ← 0;
FOR indexProcesses: CARDINAL IN [0..NNormalProcesses)
DO
maxFileIndex ← firstFileIndex + processes[indexProcesses].nFiles;
processes[indexProcesses].process ← FORK BasicProcess[firstFileIndex,
maxFileIndex];
firstFileIndex ← maxFileIndex;
ENDLOOP;

FOR indexProcesses: CARDINAL IN [NNormalProcesses..NNormalProcesses +
NSharingProcesses)
DO
processes[indexProcesses].process ← FORK SharingProcess[firstFileIndex];
firstFileIndex ← firstFileIndex + NFilesPerSharingProcess;
ENDLOOP;

FOR indexProcesses: CARDINAL IN [NNormalProcesses +
NSharingProcesses..NNormalProcesses + NSharingProcesses + NErrorProcesses)
DO
processes[indexProcesses].process ← FORK ErrorProcess[firstFileIndex];
firstFileIndex ← firstFileIndex + NFilesPerErrorProcess;
ENDLOOP;


FOR indexProcesses: CARDINAL IN [0..NProcesses)
DO
JOIN processes[indexProcesses].process;
IF (indexProcesses MOD 4) = 0 THEN FPM.ForceOutEverything[];
ENDLOOP;

FPM.ForceOutEverything[];

CleanCache[];

--SafeStorage.ReclaimCollectibleObjects[TRUE, TRUE];
--SafeStorage.ReclaimCollectibleObjects[TRUE, TRUE];
--IF FileMap.GetNext[NIL] # NIL THEN ERROR;

PrintFileStats[];
resultsStreamHandle.Close[];

END;
END;




-- main line code:

resultsStreamHandle: IO.Handle;

NNormalProcesses, NLoops: CARDINAL;
NErrorProcesses, NSharingProcesses: CARDINAL;
MaxFilesPerProcess, MaxFileSize, MaxNPagesEachTime, MaxOpsPerFileCycle: CARDINAL;

NFilesPerSharingProcess: CARDINAL = 3;
NFilesPerErrorProcess: CARDINAL ← 2;

NFiles: CARDINAL;
NProcesses: CARDINAL;

NNormalChunksInCache: CARDINAL;
NLogChunksInCache: CARDINAL;
NLeaderChunksInCache: CARDINAL;

NNormalFiles: CARDINAL;


FileMap.Initialize[101, 1000];


END.

Edit Log

Initial: Kolling: May 20, 1982 4:15 pm: general test for FilePageManager.