DIRECTORY
Allocator: TYPE USING [bsiEscape, ExtendedHeader, HeaderP, NHeaderP, pagesPerQuantum, QuantumIndex, wordsPerQuantum, LastAddress, NormalHeader],
AllocatorOps: TYPE USING [EnterAndCallBack, quantumMap, BlockSize],
LoadState: TYPE USING [Acquire, Release, local],
Process,
SafeStorage: TYPE USING [Type],
SweepCollectableStorage USING [InfoProc],
VM: TYPE USING [Interval, SwapIn],
ZCT: TYPE USING [EnterAndCallBack, EnterRCOvAndCallBack];
SweepCollectableStorageImpl:
PROGRAM
IMPORTS AllocatorOps, LoadState, Process, VM, ZCT
EXPORTS SweepCollectableStorage
= BEGIN OPEN Allocator, AllocatorOps, SafeStorage;
Variables
currentQuantumStart: Allocator.QuantumIndex;
TYPEs and constants
PROCS
LPToQuantumIndex:
PROC[lp:
LONG
POINTER]
RETURNS[QuantumIndex] = {
RETURN[LOOPHOLE[lp, LONG CARDINAL]/wordsPerQuantum];
};
QuantumIndexToLP:
PROC[qi: QuantumIndex]
RETURNS[
LONG
POINTER] = {
n: LONG CARDINAL ← qi;
RETURN[LOOPHOLE[n*wordsPerQuantum, LONG POINTER]];
};
IsExtendedBlock:
PROC[hp: HeaderP]
RETURNS[
BOOL] = {
RETURN[LOOPHOLE[hp, NHeaderP].blockSizeIndex = bsiEscape];
};
EnumerateCollectableStorage:
PUBLIC
PROC [callersInfoProc: SweepCollectableStorage.InfoProc] = {
done: BOOL ← FALSE;
regionCount: INT ← 0 ;
ActuallyDoIt:
PROC[]
RETURNS [userFinished:
BOOL] = {
this proc visits all collectible objects (free AND allocated) in a region of contiguous VM
Find the beginning of the next run of quanta from the quantum map
hp: HeaderP;
blockSize: INT ← 0;
Start parsing at the beginning of this run
FOR hp ← QuantumIndexToLP[currentQuantumStart], hp + blockSize
WHILE
LOOPHOLE[hp,
LONG
CARDINAL] < LastAddress
AND quantumMap[LPToQuantumIndex[hp]]
DO
continue: BOOL;
nhp: NHeaderP
=
LOOPHOLE
[
IF IsExtendedBlock[hp]
THEN hp + SIZE[ExtendedHeader] - SIZE[NormalHeader]
ELSE hp,
NHeaderP
];
blockSize ← BlockSize[hp];
continue ← callersInfoProc[type: nhp.type, size: blockSize, object: hp, objectHP: nhp];
IF ~continue THEN RETURN[TRUE];
ENDLOOP;
IF LOOPHOLE[hp, LONG CARDINAL] >= LastAddress THEN RETURN[FALSE];
RETURN[FALSE];
};
doSweepOfRegion:
PROC
RETURNS [endOfMemory:
BOOL] = {
ENABLE UNWIND => NULL;
quantumEnd: Allocator.QuantumIndex;
userDone: BOOL;
haveAllocatorLocked:
PROC = {
-- here with the loader and allocator locked
haveRCOvLocked:
PROC = {
here with the loader, allocator and RCOvBank locked
haveZCTLocked:
PROC = {
here with the loader, allocator, RCOvBank and ZCT locked
userDone ← ActuallyDoIt[];
};
START haveRCOvLocked HERE
Acquire the lock on the ref counting machinery, do the work, then release the lock.
ZCT.EnterAndCallBack[haveZCTLocked];
};
START haveAllocatorLocked HERE
Acquire the lock on the RCOverflowBank, do the work, then release the lock.
ZCT.EnterRCOvAndCallBack[haveRCOvLocked];
}; -- end haveAllocatorLocked
START doSweepOfRegion HERE
First, find and swap in interval needed
WHILE ~AllocatorOps.quantumMap[currentQuantumStart]
DO
IF currentQuantumStart = LAST[QuantumIndex] THEN RETURN[TRUE];
currentQuantumStart ← currentQuantumStart+1;
ENDLOOP;
quantumEnd ← currentQuantumStart;
WHILE AllocatorOps.quantumMap[quantumEnd]
AND quantumEnd <
LAST[QuantumIndex]
DO
quantumEnd ← quantumEnd+1;
ENDLOOP;
VM.SwapIn[interval: [page: currentQuantumStart * Allocator.pagesPerQuantum, count: (quantumEnd - currentQuantumStart ) * Allocator.pagesPerQuantum ]];
Next, acquire the lock on the loader (REFs in GF's are counted)
LoadState.local.Acquire[exclusive];
Next, acquire the lock on the allocator, do the work, then release the lock.
AllocatorOps.EnterAndCallBack[haveAllocatorLocked !
UNWIND => LoadState.local.Release[]];
Release the loader's lock
LoadState.local.Release[];
currentQuantumStart ← quantumEnd+1;
RETURN[ userDone OR quantumEnd >= LAST[QuantumIndex] ];
}; -- end doSweepOfRegion
currentQuantumStart ← FIRST[QuantumIndex];
WHILE ~done
DO
Process.Pause[1];
done ← doSweepOfRegion[];
regionCount ← regionCount + 1 ;
ENDLOOP;
};
START SweepCollectableStorageImpl HERE