MODULE Heaps;
IMPORT SYSTEM, Trace, Machine;
CONST
DebugValue = LONGINT(0DEADDEADH);
Stats* = TRUE;
AddressSize = SYSTEM.SIZEOF(SYSTEM.ADDRESS);
MaxTries = 16;
Unmarked = -1;
TemporaryMarkValue = -2;
BlockSize* = 32;
ArrayAlignment = 8;
BlockHeaderSize* = 2 * AddressSize;
HeapBlockOffset* = - 2 * AddressSize;
TypeDescOffset* = - AddressSize;
MaxCandidates = 1024;
MarkStackSize = 1024;
ProtTypeBit* = 31;
FlagsOfs = AddressSize * 3;
ModOfs* = AddressSize * 4;
TypeNameOfs = AddressSize * 5;
ModNameOfs = AddressSize * 2;
MinPtrOfs = -40000000H;
MethodEndMarker* = MinPtrOfs;
NilVal* = 0;
NumPriorities* = 6;
HeuristicStackInspectionGC* = 0;
MetaDataForStackGC* = 1;
TYPE
RootObject* = OBJECT
VAR nextRoot: RootObject;
PROCEDURE FindRoots*;
BEGIN HALT(301) END FindRoots;
END RootObject;
ProcessLink* = OBJECT (RootObject)
VAR next*, prev*: ProcessLink
END ProcessLink;
ProcessQueue* = RECORD
head*, tail*: ProcessLink
END;
Finalizer* = PROCEDURE {DELEGATE} (obj: ANY);
FinalizerNode* = POINTER TO RECORD
objWeak* {UNTRACED}: ANY;
nextFin: FinalizerNode;
objStrong*: ANY;
finalizer* {UNTRACED} : Finalizer;
finalizerStrong: Finalizer;
END;
HeapBlock* = POINTER TO HeapBlockDesc;
HeapBlockDesc* = RECORD
mark: LONGINT;
dataAdr-: SYSTEM.ADDRESS;
size-: SYSTEM.SIZE;
nextRealtime: HeapBlock;
END;
FreeBlock* = POINTER TO FreeBlockDesc;
FreeBlockDesc* = RECORD (HeapBlockDesc)
next: FreeBlock;
END;
SystemBlock* = POINTER TO SystemBlockDesc;
SystemBlockDesc = RECORD (HeapBlockDesc)
END;
RecordBlock* = POINTER TO RecordBlockDesc;
RecordBlockDesc = RECORD (HeapBlockDesc)
END;
ProtRecBlock* = POINTER TO ProtRecBlockDesc;
ProtRecBlockDesc* = RECORD (RecordBlockDesc)
count*: LONGINT;
locked*: BOOLEAN;
awaitingLock*, awaitingCond*: ProcessQueue;
lockedBy*: ANY;
lock*: ANY;
waitingPriorities*: ARRAY NumPriorities OF LONGINT;
END;
ArrayBlock* = POINTER TO ArrayBlockDesc;
ArrayBlockDesc = RECORD (HeapBlockDesc)
END;
StaticTypeBlock*= POINTER TO StaticTypeDesc;
StaticTypeDesc = RECORD
recSize: SYSTEM.SIZE;
pointerOffsets* {UNTRACED}: PointerOffsets;
END;
PointerOffsets = POINTER TO ARRAY OF SYSTEM.SIZE;
TYPE
GCStatus* = OBJECT
PROCEDURE SetgcOngoing*(value: BOOLEAN);
BEGIN
HALT(2000);
END SetgcOngoing;
PROCEDURE GetgcOngoing*(): BOOLEAN;
BEGIN
HALT(2001); RETURN FALSE
END GetgcOngoing;
PROCEDURE WaitForGCEnd*;
BEGIN
HALT(2002)
END WaitForGCEnd;
END GCStatus;
CONST
MaxFreeLists = 14;
FreeListBarrier = 7;
TYPE
FreeList= RECORD minSize: SYSTEM.SIZE; first {UNTRACED}, last{UNTRACED}: FreeBlock END;
FreeLists = ARRAY MaxFreeLists+1 OF FreeList;
VAR
freeLists: FreeLists;
GC*: PROCEDURE;
initBlock: ANY;
markStack: ARRAY MarkStackSize OF SYSTEM.ADDRESS;
lowestForgotten: SYSTEM.ADDRESS;
markStackHeight: LONGINT;
currentMarkValue: LONGINT;
sweepMarkValue: LONGINT;
sweepBlockAdr: SYSTEM.ADDRESS;
sweepMemBlock {UNTRACED}: Machine.MemoryBlock;
candidates: ARRAY MaxCandidates OF SYSTEM.ADDRESS;
numCandidates: LONGINT;
rootList {UNTRACED}: RootObject;
realtimeList {UNTRACED}: HeapBlock;
newSum: SYSTEM.SIZE;
checkRoot: FinalizerNode;
finalizeRoot: FinalizerNode;
freeBlockTag, systemBlockTag, recordBlockTag, protRecBlockTag, arrayBlockTag: SYSTEM.ADDRESS;
Nnew- : LONGINT;
NnewBytes- : HUGEINT;
Ngc- : LONGINT;
Nmark-, Nmarked-, NfinalizeAlive-, NfinalizeDead-: LONGINT;
NgcCyclesMark-, NgcCyclesLastRun-, NgcCyclesMax-, NgcCyclesAllRuns- : HUGEINT;
gcStatus*: GCStatus;
GCType*: LONGINT;
freeBlockFound-, freeBlockNotFound-: LONGINT;
EnableFreeLists, EnableReturnBlocks: BOOLEAN;
PROCEDURE CheckPointer(p: SYSTEM.ADDRESS): BOOLEAN;
VAR ret: BOOLEAN; heapBlockAdr, tdAdr: SYSTEM.ADDRESS;
BEGIN
ret := FALSE;
IF Machine.ValidHeapAddress(p)THEN
SYSTEM.GET(p + HeapBlockOffset, heapBlockAdr);
IF Machine.ValidHeapAddress(heapBlockAdr + TypeDescOffset) THEN
SYSTEM.GET(heapBlockAdr + TypeDescOffset, tdAdr);
IF (tdAdr = systemBlockTag) OR (tdAdr = recordBlockTag) OR (tdAdr = protRecBlockTag) OR (tdAdr = arrayBlockTag) THEN
ret := TRUE
END
END
END;
RETURN ret
END CheckPointer;
PROCEDURE Inspect(blockAdr: SYSTEM.ADDRESS);
VAR adr: SYSTEM.ADDRESS; heapBlock {UNTRACED}: HeapBlock; rootObj: RootObject; staticTypeBlock {UNTRACED}: StaticTypeBlock;
block {UNTRACED}: ANY;
BEGIN
IF (blockAdr = NilVal) OR ~CheckPointer(blockAdr) THEN RETURN END;
SYSTEM.GET(blockAdr + HeapBlockOffset, heapBlock);
IF (heapBlock = NIL) OR (heapBlock.mark >= currentMarkValue) THEN RETURN END;
block := SYSTEM.VAL(ANY, blockAdr);
heapBlock.mark := currentMarkValue;
IF Stats THEN INC(Nmarked) END;
IF (heapBlock IS RecordBlock) OR (heapBlock IS ProtRecBlock) OR (heapBlock IS ArrayBlock) THEN
IF block IS RootObject THEN
rootObj := SYSTEM.VAL(RootObject, block);
rootObj.nextRoot := rootList; rootList := rootObj;
END;
SYSTEM.GET(blockAdr + TypeDescOffset, staticTypeBlock);
IF (LEN(staticTypeBlock.pointerOffsets) > 0) OR (heapBlock IS ProtRecBlock) THEN
IF markStackHeight # MarkStackSize THEN
markStack[markStackHeight] := blockAdr; INC(markStackHeight);
ELSE
adr := SYSTEM.VAL(SYSTEM.ADDRESS, heapBlock) - BlockHeaderSize;
IF Machine.LessThan(adr, lowestForgotten) THEN lowestForgotten := adr END
END
END
END
END Inspect;
PROCEDURE Mark*(p: ANY);
VAR orgBlock, block: SYSTEM.ADDRESS; staticTypeBlock {UNTRACED}: StaticTypeBlock;
orgHeapBlock {UNTRACED}: HeapBlock;
currentArrayElemAdr, lastArrayElemAdr: SYSTEM.ADDRESS; i: LONGINT;
BEGIN
IF Stats THEN INC(Nmark) END;
markStackHeight := 0;
lowestForgotten := Machine.memBlockTail.endBlockAdr;
block := SYSTEM.VAL(SYSTEM.ADDRESS, p);
Inspect(block);
LOOP
WHILE markStackHeight # 0 DO
DEC(markStackHeight);
orgBlock := markStack[markStackHeight];
SYSTEM.GET(orgBlock + HeapBlockOffset, orgHeapBlock);
IF orgHeapBlock # NIL THEN
SYSTEM.GET(orgBlock + TypeDescOffset, staticTypeBlock);
IF ~(orgHeapBlock IS ArrayBlock) THEN
FOR i := 0 TO LEN(staticTypeBlock.pointerOffsets) - 1 DO
SYSTEM.GET(orgBlock + staticTypeBlock.pointerOffsets[i], block);
Inspect(block)
END
ELSE
SYSTEM.GET(orgBlock + 2 * AddressSize, currentArrayElemAdr);
SYSTEM.GET(orgBlock, lastArrayElemAdr);
IF Machine.GreaterThan(currentArrayElemAdr, lastArrayElemAdr) THEN HALT(100) END;
WHILE Machine.LessOrEqual(currentArrayElemAdr, lastArrayElemAdr) DO
FOR i := 0 TO LEN(staticTypeBlock.pointerOffsets) - 1 DO
SYSTEM.GET(currentArrayElemAdr + staticTypeBlock.pointerOffsets[i], block);
Inspect(block)
END;
INC(currentArrayElemAdr, staticTypeBlock.recSize);
END
END;
IF orgHeapBlock IS ProtRecBlock THEN
Inspect(SYSTEM.VAL(SYSTEM.ADDRESS, orgHeapBlock(ProtRecBlock).awaitingLock.head));
Inspect(SYSTEM.VAL(SYSTEM.ADDRESS, orgHeapBlock(ProtRecBlock).awaitingCond.head));
Inspect(SYSTEM.VAL(SYSTEM.ADDRESS, orgHeapBlock(ProtRecBlock).lockedBy));
Inspect(SYSTEM.VAL(SYSTEM.ADDRESS, orgHeapBlock(ProtRecBlock).lock))
END
ELSE
END
END;
IF lowestForgotten = Machine.memBlockTail.endBlockAdr THEN EXIT END;
FindForgottenBlock;
END;
END Mark;
PROCEDURE FindForgottenBlock;
VAR memBlock {UNTRACED}, startMemBlock {UNTRACED}: Machine.MemoryBlock; p {UNTRACED}, heapBlock {UNTRACED}: HeapBlock;
staticTypeBlock {UNTRACED}: StaticTypeBlock;
blockAdr, currentArrayElemAdr, lastArrayElemAdr, refBlock: SYSTEM.ADDRESS;
isMarkStackEntry, isOverflow: BOOLEAN; i: LONGINT;
BEGIN
isOverflow := FALSE;
memBlock := Machine.memBlockHead;
WHILE (memBlock # NIL) & ~(Machine.LessOrEqual(memBlock.beginBlockAdr, lowestForgotten) & Machine.LessThan(lowestForgotten, memBlock.endBlockAdr)) DO
memBlock := memBlock.next
END;
startMemBlock := memBlock;
WHILE (memBlock # NIL) & ~isOverflow DO
IF memBlock = startMemBlock THEN
blockAdr := lowestForgotten;
ELSE
blockAdr := memBlock.beginBlockAdr
END;
WHILE Machine.LessThan(blockAdr, memBlock.endBlockAdr) & ~isOverflow DO
p := SYSTEM.VAL(HeapBlock, blockAdr + BlockHeaderSize);
IF (p.mark = currentMarkValue) & ((p IS RecordBlock) OR (p IS ProtRecBlock) OR (p IS ArrayBlock)) THEN
isMarkStackEntry := FALSE;
SYSTEM.GET(p.dataAdr + TypeDescOffset, staticTypeBlock);
IF ~(p IS ArrayBlock) THEN
i := 0;
WHILE ~isMarkStackEntry & (i < LEN(staticTypeBlock.pointerOffsets)) DO
SYSTEM.GET(p.dataAdr + staticTypeBlock.pointerOffsets[i], refBlock);
IF refBlock # NilVal THEN
SYSTEM.GET(refBlock + HeapBlockOffset, heapBlock);
IF heapBlock.mark < currentMarkValue THEN
isMarkStackEntry := TRUE
END
END;
INC(i)
END
ELSE
SYSTEM.GET(p.dataAdr + 2 * AddressSize, currentArrayElemAdr);
SYSTEM.GET(p.dataAdr, lastArrayElemAdr);
WHILE ~isMarkStackEntry & Machine.LessOrEqual(currentArrayElemAdr, lastArrayElemAdr) DO
i := 0;
WHILE ~isMarkStackEntry & (i < LEN(staticTypeBlock.pointerOffsets)) DO
SYSTEM.GET(currentArrayElemAdr + staticTypeBlock.pointerOffsets[i], refBlock);
IF refBlock # NilVal THEN
SYSTEM.GET(refBlock + HeapBlockOffset, heapBlock);
IF heapBlock.mark < currentMarkValue THEN
isMarkStackEntry := TRUE
END
END;
INC(i)
END;
INC(currentArrayElemAdr, staticTypeBlock.recSize)
END
END;
IF ~isMarkStackEntry & (p IS ProtRecBlock) THEN
IF p(ProtRecBlock).awaitingLock.head # NIL THEN
SYSTEM.GET(SYSTEM.VAL(SYSTEM.ADDRESS, p(ProtRecBlock).awaitingLock.head) + HeapBlockOffset, heapBlock);
IF heapBlock.mark < currentMarkValue THEN isMarkStackEntry := TRUE END
END;
IF p(ProtRecBlock).awaitingCond.head # NIL THEN
SYSTEM.GET(SYSTEM.VAL(SYSTEM.ADDRESS, p(ProtRecBlock).awaitingCond.head) + HeapBlockOffset, heapBlock);
IF heapBlock.mark < currentMarkValue THEN isMarkStackEntry := TRUE END
END;
IF p(ProtRecBlock).lockedBy # NIL THEN
SYSTEM.GET(SYSTEM.VAL(SYSTEM.ADDRESS, p(ProtRecBlock).lockedBy) + HeapBlockOffset, heapBlock);
IF heapBlock.mark < currentMarkValue THEN isMarkStackEntry := TRUE END
END;
END;
IF isMarkStackEntry THEN
IF markStackHeight # MarkStackSize THEN
markStack[markStackHeight] := p.dataAdr;
INC(markStackHeight)
ELSE
isOverflow := TRUE;
lowestForgotten := blockAdr;
END
END
END;
blockAdr := blockAdr + p.size;
END;
memBlock := memBlock.next;
END;
IF ~isOverflow THEN
lowestForgotten := Machine.memBlockTail.endBlockAdr
END
END FindForgottenBlock;
PROCEDURE MarkRealtimeObjects;
VAR heapBlock {UNTRACED}: HeapBlock;
BEGIN
heapBlock := realtimeList;
WHILE heapBlock # NIL DO
Mark(SYSTEM.VAL(ANY, heapBlock.dataAdr));
heapBlock := heapBlock.nextRealtime;
END;
END MarkRealtimeObjects;
PROCEDURE WriteType*(t: SYSTEM.ADDRESS);
VAR m: SYSTEM.ADDRESS; i: LONGINT; ch: CHAR; name: ARRAY 32 OF CHAR;
BEGIN
SYSTEM.GET (t + TypeDescOffset, t);
SYSTEM.GET (t + ModOfs, m);
IF m # NilVal THEN
i := 0; SYSTEM.GET (m + ModNameOfs + i, ch);
WHILE (ch >= "0") & (ch <= "z") & (i # 32) DO
Trace.Char(ch);
INC(i); SYSTEM.GET (m + ModNameOfs + i, ch)
END
ELSE
Trace.String("NIL")
END;
Trace.Char(".");
SYSTEM.MOVE(t + TypeNameOfs, SYSTEM.ADR(name[0]), 32);
IF name[0] = 0X THEN Trace.String("-")
ELSE Trace.String(name)
END
END WriteType;
PROCEDURE ClearFreeLists;
VAR i: LONGINT;
BEGIN
FOR i := 0 TO MaxFreeLists DO
freeLists[i].first := NIL;
freeLists[i].last := NIL
END;
END ClearFreeLists;
PROCEDURE AppendFree(VAR freeList: FreeList; block: FreeBlock);
BEGIN
ASSERT(block.size >= freeList.minSize);
IF freeList.first = NIL THEN
freeList.first := block; freeList.last := block
ELSE
freeList.last.next := block;
freeList.last := block;
END;
block.next := NIL
END AppendFree;
PROCEDURE GetFree(VAR freeList: FreeList): FreeBlock;
VAR block: FreeBlock;
BEGIN
IF freeList.first = NIL THEN block := NIL;
ELSIF freeList.first = freeList.last THEN block := freeList.first; freeList.first := NIL; freeList.last := NIL
ELSE block := freeList.first; freeList.first := block.next; block.next := NIL
END;
RETURN block
END GetFree;
PROCEDURE InsertSorted(VAR freeList: FreeList; block: FreeBlock);
VAR x: FreeBlock;
BEGIN
x := freeList.first;
WHILE x # NIL DO
ASSERT(x # block);
x := x.next;
END;
x := freeList.first;
IF (x = NIL) OR (block.size <= x.size) THEN
block.next := x;
freeList.first := block;
ELSE
WHILE (x.next # NIL) & (block.size > x.next.size) DO x := x.next END;
block.next := x.next;
x.next := block;
END;
END InsertSorted;
PROCEDURE AppendFreeBlock(block: FreeBlock);
VAR i: LONGINT;
BEGIN
i := MaxFreeLists;
WHILE (i > 0) & (freeLists[i].minSize > block.size) DO DEC( i ) END;
IF i < FreeListBarrier THEN
AppendFree(freeLists[i], block);
ELSE
InsertSorted(freeLists[i], block);
END;
END AppendFreeBlock;
PROCEDURE FindFreeBlock( size: SYSTEM.SIZE ): FreeBlock;
VAR prev, block: FreeBlock; i: LONGINT;
BEGIN
i := MaxFreeLists;
WHILE (i > 0) & (freeLists[i].minSize > size) DO DEC( i ) END;
REPEAT
IF i < FreeListBarrier THEN
block := GetFree(freeLists[i]);
ELSE
block := freeLists[i].first;
WHILE (block # NIL) & (block.size < size) DO
prev := block;
block := block.next;
END;
IF block # NIL THEN
IF prev = NIL THEN freeLists[i].first := block.next
ELSE prev.next := block.next
END;
block.next := NIL;
END;
prev := freeLists[i].first;
WHILE prev # NIL DO
ASSERT(prev # block);
prev := prev.next;
END;
END;
INC( i )
UNTIL (block # NIL) OR (i > MaxFreeLists);
RETURN block
END FindFreeBlock;
PROCEDURE GetFreeBlockAndSplit(size: SYSTEM.SIZE): FreeBlock;
VAR p,remainder: FreeBlock;
BEGIN
p := FindFreeBlock(size);
IF (p # NIL) & Machine.GreaterThan(p.size, size) THEN
ASSERT(Machine.GreaterOrEqual(p.size - size, BlockHeaderSize + SYSTEM.SIZEOF(FreeBlockDesc)));
remainder := SYSTEM.VAL(FreeBlock, SYSTEM.VAL(SYSTEM.ADDRESS, p) + size);
InitFreeBlock(remainder, Unmarked, NilVal, p.size - size);
AppendFreeBlock(remainder);
p.size := size;
END;
IF p # NIL THEN INC(freeBlockFound) ELSE INC(freeBlockNotFound) END;
RETURN p
END GetFreeBlockAndSplit;
PROCEDURE GetFreeBlock(size: SYSTEM.SIZE; VAR p: FreeBlock);
BEGIN
IF EnableFreeLists THEN
IF sweepMarkValue < currentMarkValue THEN
ClearFreeLists;
LazySweep(MAX(LONGINT), p)
END;
p := GetFreeBlockAndSplit(size)
ELSE
LazySweep(size, p)
END;
END GetFreeBlock;
PROCEDURE LazySweep(size: SYSTEM.SIZE; VAR p: FreeBlock);
VAR lastFreeBlockAdr: SYSTEM.ADDRESS; found : BOOLEAN;
block: HeapBlock; freeBlock, lastFreeBlock: FreeBlock; blockMark: LONGINT; blockSize: SYSTEM.SIZE;
BEGIN
found := FALSE;
lastFreeBlockAdr := NilVal;
lastFreeBlock := NIL;
IF (sweepMemBlock = NIL) OR (sweepMarkValue < currentMarkValue) THEN
sweepMemBlock := Machine.memBlockHead;
sweepBlockAdr := Machine.memBlockHead.beginBlockAdr;
sweepMarkValue := currentMarkValue;
END;
WHILE ~found & (sweepMemBlock # NIL) DO
WHILE ~found & Machine.LessThan(sweepBlockAdr, sweepMemBlock.endBlockAdr) DO
block := SYSTEM.VAL(HeapBlock, sweepBlockAdr + BlockHeaderSize);
blockMark := block.mark;
blockSize := block.size;
IF (block.mark # TemporaryMarkValue) & (block.mark < sweepMarkValue) THEN
IF (block IS SystemBlock) OR (block IS RecordBlock) OR (block IS ProtRecBlock) OR (block IS ArrayBlock) THEN
freeBlock := SYSTEM.VAL(FreeBlock, block);
InitFreeBlock(freeBlock, Unmarked, NilVal, block.size);
Machine.Fill32(sweepBlockAdr + BlockHeaderSize + SYSTEM.SIZEOF(FreeBlockDesc), freeBlock.size - BlockHeaderSize - SYSTEM.SIZEOF(FreeBlockDesc), DebugValue);
ELSE
ASSERT(block IS FreeBlock);
freeBlock := block(FreeBlock);
END;
IF lastFreeBlockAdr = NilVal THEN
lastFreeBlockAdr := sweepBlockAdr;
lastFreeBlock := freeBlock;
ELSIF lastFreeBlockAdr + lastFreeBlock.size = sweepBlockAdr THEN
lastFreeBlock.size := lastFreeBlock.size + block.size;
Machine.Fill32(sweepBlockAdr, BlockHeaderSize + SYSTEM.SIZEOF(FreeBlockDesc), DebugValue);
END
END;
IF (blockMark = TemporaryMarkValue) OR (blockMark >= sweepMarkValue) OR (sweepBlockAdr + blockSize = sweepMemBlock.endBlockAdr) THEN
IF (blockMark = TemporaryMarkValue) THEN
block.mark := currentMarkValue
END;
IF lastFreeBlockAdr # NilVal THEN
IF Machine.GreaterOrEqual(lastFreeBlock.size, size) THEN
p := lastFreeBlock;
IF Machine.GreaterThan(p.size, size) THEN
ASSERT(Machine.GreaterOrEqual(p.size - size, BlockHeaderSize + SYSTEM.SIZEOF(FreeBlockDesc)));
freeBlock := SYSTEM.VAL(FreeBlock, SYSTEM.VAL(SYSTEM.ADDRESS, p) + size);
InitFreeBlock(freeBlock, Unmarked, NilVal, p.size - size);
p.size := size
END;
found := TRUE;
sweepBlockAdr := lastFreeBlockAdr + size
ELSIF EnableFreeLists THEN AppendFreeBlock(lastFreeBlock);
END;
lastFreeBlockAdr := NilVal;
lastFreeBlock := NIL;
END
END;
IF ~found THEN sweepBlockAdr := sweepBlockAdr + blockSize END
END;
IF ~found THEN
sweepMemBlock := sweepMemBlock.next;
IF sweepMemBlock # NIL THEN
sweepBlockAdr := sweepMemBlock.beginBlockAdr
ELSE
sweepBlockAdr := NilVal
END
END
END;
END LazySweep;
PROCEDURE CheckHeap;
VAR memBlock {UNTRACED}: Machine.MemoryBlock; p, refBlock, currentArrayElemAdr, lastArrayElemAdr: SYSTEM.ADDRESS;
heapBlock {UNTRACED}: HeapBlock; staticTypeBlock {UNTRACED}: StaticTypeBlock; i: LONGINT;
PROCEDURE CheckBlock(block: SYSTEM.ADDRESS): BOOLEAN;
VAR heapBlockAdr: SYSTEM.ADDRESS;
BEGIN
IF block = NilVal THEN
RETURN TRUE
ELSE
IF Machine.GreaterOrEqual(block, Machine.memBlockHead.beginBlockAdr) & Machine.LessThan(block, Machine.memBlockTail.endBlockAdr) THEN
SYSTEM.GET(block + HeapBlockOffset, heapBlockAdr);
IF Machine.GreaterOrEqual(heapBlockAdr, Machine.memBlockHead.beginBlockAdr) & Machine.LessThan(heapBlockAdr, Machine.memBlockTail.endBlockAdr) THEN
RETURN TRUE
ELSE
RETURN FALSE
END
ELSE
RETURN FALSE
END
END
END CheckBlock;
BEGIN
memBlock := Machine.memBlockHead;
WHILE memBlock # NIL DO
p := memBlock.beginBlockAdr;
WHILE Machine.LessThan(p, memBlock.endBlockAdr) DO
heapBlock := SYSTEM.VAL(HeapBlock, p + BlockHeaderSize);
IF heapBlock IS SystemBlock THEN
ELSIF heapBlock IS RecordBlock THEN
IF heapBlock.dataAdr # NilVal THEN
SYSTEM.GET(heapBlock.dataAdr + TypeDescOffset, staticTypeBlock); ASSERT(staticTypeBlock # NIL);
FOR i := 0 TO LEN(staticTypeBlock.pointerOffsets) - 1 DO
SYSTEM.GET(heapBlock.dataAdr + staticTypeBlock.pointerOffsets[i], refBlock);
IF ~CheckBlock(refBlock) THEN
Trace.String("SEVERE ERROR: RecordBlock = "); Trace.Hex(heapBlock.dataAdr, 8);
Trace.String(" invalid reference at pointer offset = "); Trace.Hex(staticTypeBlock.pointerOffsets[i], 0); Trace.Ln
END
END;
IF heapBlock IS ProtRecBlock THEN
IF CheckBlock(SYSTEM.VAL(SYSTEM.ADDRESS, heapBlock(ProtRecBlock).awaitingLock.head)) &
CheckBlock(SYSTEM.VAL(SYSTEM.ADDRESS, heapBlock(ProtRecBlock).awaitingLock.tail)) &
CheckBlock(SYSTEM.VAL(SYSTEM.ADDRESS, heapBlock(ProtRecBlock).awaitingCond.head)) &
CheckBlock(SYSTEM.VAL(SYSTEM.ADDRESS, heapBlock(ProtRecBlock).awaitingCond.tail)) &
CheckBlock(SYSTEM.VAL(SYSTEM.ADDRESS, heapBlock(ProtRecBlock).lockedBy)) THEN
ELSE
Trace.String("SEVERE ERROR in awaiting queues of block = "); Trace.Hex(heapBlock.dataAdr, 8); Trace.Ln
END
END
ELSE
Trace.StringLn("SEVERE ERROR: heapBlock.dataAdr = NilVal for RecordBlock or ProtRecBlock")
END;
ELSIF heapBlock IS ArrayBlock THEN
IF heapBlock.dataAdr # NilVal THEN
SYSTEM.GET(heapBlock.dataAdr + TypeDescOffset, staticTypeBlock); ASSERT(staticTypeBlock # NIL);
SYSTEM.GET(heapBlock.dataAdr + 2 * AddressSize, currentArrayElemAdr);
SYSTEM.GET(heapBlock.dataAdr, lastArrayElemAdr);
WHILE Machine.LessOrEqual(currentArrayElemAdr, lastArrayElemAdr) DO
FOR i := 0 TO LEN(staticTypeBlock.pointerOffsets) - 1 DO
SYSTEM.GET(currentArrayElemAdr + staticTypeBlock.pointerOffsets[i], refBlock);
IF ~CheckBlock(refBlock) THEN
Trace.String("SEVERE ERROR in ArrayBlock = "); Trace.Hex(currentArrayElemAdr, 8);
Trace.String(" invalid reference at pointer offset = "); Trace.Hex(staticTypeBlock.pointerOffsets[i], 0); Trace.Ln
END
END;
INC(currentArrayElemAdr, staticTypeBlock.recSize)
END
ELSE
Trace.StringLn("SEVERE ERROR: heapBlock.dataAdr = NilVal for ArrayBlock")
END
ELSIF heapBlock IS FreeBlock THEN
ELSE
Trace.StringLn("Invalid heap block type")
END;
p := p + heapBlock.size;
END;
memBlock := memBlock.next
END
END CheckHeap;
PROCEDURE CheckCandidates*;
CONST MinDataOffset = BlockHeaderSize + SYSTEM.SIZEOF(HeapBlockDesc) + BlockHeaderSize;
VAR i, j, h: LONGINT; p, blockStart, tdAdr: SYSTEM.ADDRESS; memBlock {UNTRACED}: Machine.MemoryBlock;
heapBlock {UNTRACED}: HeapBlock;
BEGIN
h := 1; REPEAT h := h*3 + 1 UNTIL h > numCandidates;
REPEAT
h := h DIV 3; i := h;
WHILE i < numCandidates DO
p := candidates[i]; j := i;
WHILE (j >= h) & Machine.GreaterThan(candidates[j-h], p) DO
candidates[j] := candidates[j-h]; j := j-h;
END;
candidates[j] := p; INC(i)
END
UNTIL h = 1;
i := 0;
p := candidates[i];
memBlock := Machine.memBlockHead;
WHILE memBlock # NIL DO
blockStart := memBlock.beginBlockAdr;
WHILE (i < numCandidates) & Machine.LessThan(blockStart, memBlock.endBlockAdr) DO
IF Machine.LessThan(p, blockStart + MinDataOffset) THEN
INC(i);
IF i < numCandidates THEN
p := candidates[i]
END
ELSE
heapBlock := SYSTEM.VAL(HeapBlock, blockStart + BlockHeaderSize);
SYSTEM.GET(blockStart + BlockHeaderSize + TypeDescOffset, tdAdr);
IF ~(tdAdr = freeBlockTag) & (p = heapBlock.dataAdr) THEN
Mark(SYSTEM.VAL(ANY, p))
END;
blockStart := blockStart + heapBlock.size;
END
END;
memBlock := memBlock.next
END;
numCandidates := 0
END CheckCandidates;
PROCEDURE Candidate*(p: SYSTEM.ADDRESS);
VAR memBlock {UNTRACED}: Machine.MemoryBlock; heapBlockAdr, tdAdr: SYSTEM.ADDRESS;
BEGIN
IF Machine.GreaterOrEqual(p, Machine.memBlockHead.beginBlockAdr) & Machine.LessThan(p, Machine.memBlockTail.endBlockAdr) THEN
memBlock := Machine.memBlockHead;
WHILE memBlock # NIL DO
IF Machine.GreaterOrEqual(p + HeapBlockOffset, memBlock.beginBlockAdr) & Machine.LessThan(p + HeapBlockOffset, memBlock.endBlockAdr) THEN
SYSTEM.GET(p + HeapBlockOffset, heapBlockAdr);
IF Machine.GreaterOrEqual(heapBlockAdr + TypeDescOffset, memBlock.beginBlockAdr) & Machine.LessThan(heapBlockAdr + TypeDescOffset, memBlock.endBlockAdr) THEN
SYSTEM.GET(heapBlockAdr + TypeDescOffset, tdAdr);
IF (tdAdr = systemBlockTag) OR (tdAdr = recordBlockTag) OR (tdAdr = protRecBlockTag) OR (tdAdr = arrayBlockTag) THEN
candidates[numCandidates] := p;
INC(numCandidates);
IF numCandidates = LEN(candidates) THEN CheckCandidates END
END
END
END;
memBlock := memBlock.next
END
END
END Candidate;
PROCEDURE RegisterCandidates*(adr: SYSTEM.ADDRESS; size: SYSTEM.SIZE);
VAR end, p: SYSTEM.ADDRESS;
BEGIN
end := adr + size;
WHILE adr # end DO
SYSTEM.GET(adr, p);
Candidate(p);
INC(adr, AddressSize)
END
END RegisterCandidates;
PROCEDURE CheckFinalizedObjects;
VAR n, p, t: FinalizerNode; heapBlock {UNTRACED}: HeapBlock;
PROCEDURE MarkDelegate(p: Finalizer);
VAR pointer: ANY;
BEGIN
SYSTEM.GET(SYSTEM.ADR(p)+SYSTEM.SIZEOF(SYSTEM.ADDRESS),pointer);
IF pointer # NIL THEN Mark(pointer) END;
END MarkDelegate;
BEGIN
n := checkRoot;
WHILE n # NIL DO
SYSTEM.GET(SYSTEM.VAL(SYSTEM.ADDRESS, n.objWeak) + HeapBlockOffset, heapBlock);
IF heapBlock.mark < currentMarkValue THEN
IF n = checkRoot THEN checkRoot := n.nextFin ELSE p.nextFin := n.nextFin END;
n.objStrong := n.objWeak;
n.finalizerStrong := n.finalizer;
t := n.nextFin; n.nextFin := finalizeRoot; finalizeRoot := n; n := t;
IF Stats THEN DEC(NfinalizeAlive); INC(NfinalizeDead) END
ELSE
p := n; n := n.nextFin
END
END;
n := finalizeRoot;
WHILE n # NIL DO
MarkDelegate(n.finalizerStrong);
Mark(n.objStrong); n := n.nextFin
END;
n := checkRoot;
WHILE n # NIL DO
MarkDelegate(n.finalizer); n := n.nextFin
END;
END CheckFinalizedObjects;
PROCEDURE GetFinalizer* (): FinalizerNode;
VAR n: FinalizerNode;
BEGIN
n := NIL;
IF finalizeRoot # NIL THEN
Machine.Acquire(Machine.Heaps);
n := finalizeRoot;
IF n # NIL THEN
finalizeRoot := n.nextFin; n.nextFin := NIL;
IF Stats THEN DEC(NfinalizeDead) END;
END;
Machine.Release(Machine.Heaps);
END;
RETURN n
END GetFinalizer;
PROCEDURE CleanupModuleFinalizers*(codeAdr: SYSTEM.ADDRESS; codeLen: SYSTEM.SIZE; CONST name: ARRAY OF CHAR);
VAR n, p, t: FinalizerNode; codeEnd: SYSTEM.ADDRESS; N1, N2: LONGINT;
BEGIN
codeEnd := codeAdr + codeLen; N1 := 0; N2 := 0;
Machine.Acquire(Machine.Heaps);
n := checkRoot;
WHILE n # NIL DO
t := n; n := n.nextFin;
IF Machine.LessOrEqual(codeAdr, SYSTEM.VAL (SYSTEM.ADDRESS, t.finalizer)) & Machine.LessOrEqual(SYSTEM.VAL (SYSTEM.ADDRESS, t.finalizer), codeEnd) THEN
IF t = checkRoot THEN checkRoot := t.nextFin ELSE p.nextFin := t.nextFin END;
IF Stats THEN DEC(NfinalizeAlive) END;
INC(N1)
ELSE
p := t
END
END;
n := finalizeRoot;
WHILE n # NIL DO
t := n; n := n.nextFin;
IF Machine.LessOrEqual(codeAdr, SYSTEM.VAL (SYSTEM.ADDRESS, t.finalizer)) & Machine.LessOrEqual(SYSTEM.VAL (SYSTEM.ADDRESS, t.finalizer), codeEnd) THEN
IF t = finalizeRoot THEN finalizeRoot := t.nextFin ELSE p.nextFin := t.nextFin END;
IF Stats THEN DEC(NfinalizeDead) END;
INC(N2)
ELSE
p := t
END
END;
Machine.Release(Machine.Heaps);
IF (N1 # 0) OR (N2 # 0) THEN
Machine.Acquire (Machine.TraceOutput);
Trace.String(name); Trace.Char(" ");
Trace.Int(N1, 1); Trace.String(" discarded finalizers, ");
Trace.Int(N2, 1); Trace.StringLn (" pending finalizers");
Machine.Release (Machine.TraceOutput);
END
END CleanupModuleFinalizers;
PROCEDURE AddRootObject*(rootObject: RootObject);
BEGIN
IF rootObject = NIL THEN
ELSIF CheckPointer(SYSTEM.VAL(SYSTEM.ADDRESS,rootObject)) THEN
Mark(rootObject)
ELSE
rootObject.nextRoot := rootList; rootList := rootObject;
END;
END AddRootObject;
PROCEDURE CollectGarbage*(root : RootObject);
VAR
obj: RootObject;
time1, time2: HUGEINT;
f: FreeBlock;
BEGIN
IF (sweepMemBlock = NIL) & (sweepMarkValue = currentMarkValue) THEN
IF Stats THEN
Nmark := 0; Nmarked := 0;
INC(Ngc);
time1 := Machine.GetTimer ();
END;
numCandidates := 0;
rootList := NIL;
INC(currentMarkValue);
AddRootObject(root);
IF GCType = HeuristicStackInspectionGC THEN
REPEAT
REPEAT
IF rootList # NIL THEN
REPEAT
obj := rootList;
rootList := rootList.nextRoot;
obj.FindRoots;
UNTIL rootList = NIL
END;
IF numCandidates # 0 THEN CheckCandidates END
UNTIL (numCandidates = 0) & (rootList = NIL);
MarkRealtimeObjects;
CheckFinalizedObjects;
UNTIL rootList = NIL;
ELSIF GCType = MetaDataForStackGC THEN
REPEAT
IF rootList # NIL THEN
REPEAT
obj := rootList;
rootList := rootList.nextRoot;
obj.FindRoots;
UNTIL rootList = NIL
END;
MarkRealtimeObjects;
CheckFinalizedObjects
UNTIL rootList = NIL;
ELSE
HALT(901)
END;
IF Stats THEN
time2 := Machine.GetTimer ();
NgcCyclesLastRun := time2 - time1;
IF NgcCyclesLastRun > NgcCyclesMax THEN NgcCyclesMax := NgcCyclesLastRun; END;
INC(NgcCyclesAllRuns, NgcCyclesLastRun);
NgcCyclesMark := NgcCyclesLastRun
END;
END;
IF EnableFreeLists THEN GetFreeBlock(MAX(LONGINT), f) END;
END CollectGarbage;
PROCEDURE InvokeGC*;
BEGIN
ASSERT(gcStatus # NIL);
gcStatus.SetgcOngoing(TRUE);
END InvokeGC;
PROCEDURE ReturnBlocks;
VAR memBlock {UNTRACED}, free{UNTRACED}: Machine.MemoryBlock; p: SYSTEM.ADDRESS; heapBlock {UNTRACED}: HeapBlock; f: FreeBlock;
BEGIN
GetFreeBlock(MAX(LONGINT), f);
memBlock := Machine.memBlockHead;
WHILE memBlock # NIL DO
free := NIL;
p := memBlock.beginBlockAdr;
heapBlock := SYSTEM.VAL(HeapBlock, p + BlockHeaderSize);
IF (heapBlock IS FreeBlock) & (p + heapBlock.size = memBlock.endBlockAdr) THEN
free := memBlock;
END;
memBlock := memBlock.next;
IF free # NIL THEN
Machine.FreeMemBlock(free)
END;
END;
sweepMemBlock := NIL;
ClearFreeLists;
END ReturnBlocks;
PROCEDURE LazySweepGC*;
VAR p {UNTRACED}: FreeBlock;
BEGIN
Machine.Acquire(Machine.Heaps);
GetFreeBlock(MAX(LONGINT), p);
Machine.Release(Machine.Heaps);
GC;
Machine.Acquire(Machine.Heaps);
ReturnBlocks;
Machine.Release(Machine.Heaps);
END LazySweepGC;
PROCEDURE InitFreeBlock(freeBlock: FreeBlock; mark: LONGINT; dataAdr: SYSTEM.ADDRESS; size: SYSTEM.SIZE);
VAR freeBlockAdr: SYSTEM.ADDRESS;
BEGIN
freeBlock.mark := mark;
freeBlock.dataAdr := dataAdr;
freeBlock.size := size;
freeBlock.next := NIL;
freeBlockAdr := SYSTEM.VAL(SYSTEM.ADDRESS, freeBlock);
SYSTEM.PUT(freeBlockAdr + TypeDescOffset, freeBlockTag);
SYSTEM.PUT(freeBlockAdr + HeapBlockOffset, NilVal)
END InitFreeBlock;
PROCEDURE NewBlock(size: SYSTEM.SIZE): SYSTEM.ADDRESS;
VAR try: LONGINT; p {UNTRACED}, freeBlock {UNTRACED}: FreeBlock; memBlock {UNTRACED}: Machine.MemoryBlock;
beginHeapBlockAdr, endHeapBlockAdr: SYSTEM.ADDRESS;
PROCEDURE CheckPostGC;
BEGIN
IF (sweepMarkValue < currentMarkValue) & EnableReturnBlocks THEN
ReturnBlocks
END;
END CheckPostGC;
BEGIN
CheckPostGC;
try := 1;
p := NIL;
GetFreeBlock(size, p);
WHILE (p = NIL) & (try <= MaxTries) DO
Machine.Release(Machine.Heaps);
GC;
Machine.Acquire(Machine.Heaps);
CheckPostGC;
sweepMemBlock := NIL;
GetFreeBlock(size, p);
IF p = NIL THEN
Machine.ExpandHeap(try, size, memBlock, beginHeapBlockAdr, endHeapBlockAdr);
IF Machine.GreaterThan(endHeapBlockAdr, beginHeapBlockAdr) THEN
freeBlock := SYSTEM.VAL(FreeBlock, beginHeapBlockAdr + BlockHeaderSize);
InitFreeBlock(freeBlock, Unmarked, NilVal, endHeapBlockAdr - beginHeapBlockAdr);
Machine.SetMemoryBlockEndAddress(memBlock, endHeapBlockAdr);
IF EnableFreeLists THEN AppendFreeBlock(freeBlock)
ELSE
sweepMemBlock := memBlock;
sweepBlockAdr := beginHeapBlockAdr;
END;
GetFreeBlock(size, p);
sweepMemBlock := NIL;
END;
INC(try)
END;
END;
IF p # NIL THEN
IF Stats THEN INC(Nnew); INC(NnewBytes, size) END;
ASSERT(p.size >= size);
RETURN SYSTEM.VAL(SYSTEM.ADDRESS, p)
ELSE
SYSTEM.HALT(14)
END;
END NewBlock;
PROCEDURE NewSys*(VAR p: ANY; size: SYSTEM.SIZE; isRealtime: BOOLEAN);
VAR blockSize, systemBlockSize: SYSTEM.SIZE; systemBlockAdr, dataBlockAdr: SYSTEM.ADDRESS;
systemBlock {UNTRACED}: SystemBlock; pc: SYSTEM.ADDRESS;
BEGIN
systemBlockSize := BlockHeaderSize + SYSTEM.SIZEOF(SystemBlockDesc);
INC(systemBlockSize, (-systemBlockSize) MOD ArrayAlignment);
blockSize := systemBlockSize + BlockHeaderSize + size;
INC(blockSize, (-blockSize) MOD BlockSize);
Machine.Acquire(Machine.Heaps);
systemBlockAdr:= NewBlock(blockSize);
IF systemBlockAdr # 0 THEN
SYSTEM.PUT(systemBlockAdr + TypeDescOffset, systemBlockTag);
SYSTEM.GET(Machine.CurrentBP()+SYSTEM.SIZEOF(SYSTEM.ADDRESS),pc);
SYSTEM.PUT(systemBlockAdr + HeapBlockOffset,pc);
dataBlockAdr := systemBlockAdr + systemBlockSize ;
SYSTEM.PUT(dataBlockAdr + TypeDescOffset, NilVal);
SYSTEM.PUT(dataBlockAdr + HeapBlockOffset, systemBlockAdr);
systemBlock := SYSTEM.VAL(SystemBlock, systemBlockAdr);
systemBlock.mark := currentMarkValue ;
systemBlock.dataAdr := dataBlockAdr;
systemBlock.size := blockSize;
IF isRealtime THEN
systemBlock.nextRealtime := realtimeList;
realtimeList := systemBlock
ELSE
systemBlock.nextRealtime := NIL
END;
p := SYSTEM.VAL(ANY, dataBlockAdr);
Machine.Fill32(dataBlockAdr, blockSize - systemBlockSize - BlockHeaderSize, 0);
ELSE
p := NIL
END;
Machine.Release(Machine.Heaps)
END NewSys;
PROCEDURE NewRec*(VAR p: ANY; tag: SYSTEM.ADDRESS; isRealtime: BOOLEAN);
VAR flags: SET; size, blockSize: SYSTEM.SIZE; typeInfoAdr, recordBlockAdr, dataBlockAdr : SYSTEM.ADDRESS;
recordBlock {UNTRACED}: RecordBlock; pc: SYSTEM.ADDRESS;
BEGIN
SYSTEM.GET (tag - AddressSize, typeInfoAdr);
SYSTEM.GET (typeInfoAdr + FlagsOfs, flags);
IF ProtTypeBit IN flags THEN
NewProtRec(p, tag, isRealtime);
ELSE
SYSTEM.GET(tag, size);
blockSize := BlockHeaderSize + SYSTEM.SIZEOF(RecordBlockDesc) + BlockHeaderSize + size;
INC(blockSize, (-blockSize) MOD BlockSize);
Machine.Acquire(Machine.Heaps);
recordBlockAdr := NewBlock(blockSize);
IF recordBlockAdr # 0 THEN
SYSTEM.PUT(recordBlockAdr + TypeDescOffset, recordBlockTag);
SYSTEM.GET(Machine.CurrentBP()+SYSTEM.SIZEOF(SYSTEM.ADDRESS),pc);
SYSTEM.PUT(recordBlockAdr + HeapBlockOffset,pc);
dataBlockAdr := recordBlockAdr + SYSTEM.SIZEOF(RecordBlockDesc) + BlockHeaderSize;
SYSTEM.PUT(dataBlockAdr + TypeDescOffset, tag);
SYSTEM.PUT(dataBlockAdr + HeapBlockOffset, recordBlockAdr);
recordBlock := SYSTEM.VAL(RecordBlock, recordBlockAdr);
recordBlock.mark := currentMarkValue ;
recordBlock.dataAdr := dataBlockAdr;
recordBlock.size := blockSize;
IF isRealtime THEN
recordBlock.nextRealtime := realtimeList;
realtimeList := recordBlock
ELSE
recordBlock.nextRealtime := NIL
END;
p := SYSTEM.VAL(ANY, dataBlockAdr);
Machine.Fill32(dataBlockAdr, blockSize - SYSTEM.SIZEOF(RecordBlockDesc) - 2 * BlockHeaderSize, 0);
ELSE
p := NIL
END;
Machine.Release(Machine.Heaps)
END;
END NewRec;
PROCEDURE NewProtRec*(VAR p: ANY; tag: SYSTEM.ADDRESS; isRealtime: BOOLEAN);
VAR size, blockSize: SYSTEM.SIZE; protRecBlockAdr, dataBlockAdr: SYSTEM.ADDRESS;
protRecBlock {UNTRACED}: ProtRecBlock; i: LONGINT; pc: SYSTEM.ADDRESS;
BEGIN
SYSTEM.GET(tag, size);
blockSize := BlockHeaderSize + SYSTEM.SIZEOF(ProtRecBlockDesc) + BlockHeaderSize + size;
INC(blockSize, (-blockSize) MOD BlockSize);
Machine.Acquire(Machine.Heaps);
protRecBlockAdr := NewBlock(blockSize);
IF protRecBlockAdr # 0 THEN
SYSTEM.PUT(protRecBlockAdr + TypeDescOffset, protRecBlockTag);
SYSTEM.GET(Machine.CurrentBP()+SYSTEM.SIZEOF(SYSTEM.ADDRESS),pc);
SYSTEM.PUT(protRecBlockAdr + HeapBlockOffset,pc);
dataBlockAdr := protRecBlockAdr + SYSTEM.SIZEOF(ProtRecBlockDesc) + BlockHeaderSize;
SYSTEM.PUT(dataBlockAdr + TypeDescOffset, tag);
SYSTEM.PUT(dataBlockAdr + HeapBlockOffset, protRecBlockAdr);
protRecBlock := SYSTEM.VAL(ProtRecBlock, protRecBlockAdr);
protRecBlock.mark := currentMarkValue ;
protRecBlock.dataAdr := dataBlockAdr;
protRecBlock.size := blockSize;
IF isRealtime THEN
protRecBlock.nextRealtime := realtimeList;
realtimeList := protRecBlock
ELSE
protRecBlock.nextRealtime := NIL
END;
protRecBlock.count := 0;
protRecBlock.awaitingLock.head := NIL;
protRecBlock.awaitingLock.tail := NIL;
protRecBlock.awaitingCond.head := NIL;
protRecBlock.awaitingCond.tail := NIL;
protRecBlock.lockedBy := NIL;
protRecBlock.lock := NIL;
FOR i := 0 TO NumPriorities - 1 DO
protRecBlock.waitingPriorities[i] := 0
END;
INC(protRecBlock.waitingPriorities[0]);
p := SYSTEM.VAL(ANY, dataBlockAdr);
Machine.Fill32(dataBlockAdr, blockSize - SYSTEM.SIZEOF(ProtRecBlockDesc) - 2 * BlockHeaderSize, 0);
ELSE
p := NIL
END;
Machine.Release(Machine.Heaps)
END NewProtRec;
PROCEDURE NewArr*(VAR p: ANY; elemTag: SYSTEM.ADDRESS; numElems, numDims: SYSTEM.SIZE; isRealtime: BOOLEAN);
VAR arrayBlockAdr, dataBlockAdr: SYSTEM.ADDRESS; arrayBlock {UNTRACED}: ArrayBlock;
elemSize, arrSize, blockSize, arrayBlockSize, fillSize, size, ptrOfs, arrayDataOffset: SYSTEM.SIZE;
firstElem: SYSTEM.ADDRESS; pc: SYSTEM.ADDRESS;
BEGIN
SYSTEM.GET(elemTag, elemSize);
arrSize := numElems * elemSize;
IF arrSize = 0 THEN
NewSys(p, numDims * AddressSize + 3 * AddressSize, isRealtime);
ELSE
ASSERT(BlockHeaderSize MOD ArrayAlignment = 0);
arrayDataOffset := numDims * AddressSize + 3 * AddressSize;
INC(arrayDataOffset, (-arrayDataOffset) MOD ArrayAlignment);
SYSTEM.GET(elemTag + AddressSize, ptrOfs);
IF ptrOfs = MinPtrOfs - AddressSize THEN
size := arrayDataOffset + arrSize;
NewSys(p, size, isRealtime);
ELSE
arrayBlockSize := BlockHeaderSize + SYSTEM.SIZEOF(ArrayBlockDesc);
INC(arrayBlockSize, (-arrayBlockSize) MOD ArrayAlignment);
blockSize := arrayBlockSize + BlockHeaderSize + (arrayDataOffset + arrSize);
INC(blockSize, (-blockSize) MOD BlockSize);
Machine.Acquire(Machine.Heaps);
arrayBlockAdr := NewBlock(blockSize);
IF arrayBlockAdr # 0 THEN
SYSTEM.PUT(arrayBlockAdr + TypeDescOffset, arrayBlockTag);
SYSTEM.GET(Machine.CurrentBP()+SYSTEM.SIZEOF(SYSTEM.ADDRESS),pc);
SYSTEM.PUT(arrayBlockAdr + HeapBlockOffset,pc);
dataBlockAdr := arrayBlockAdr + arrayBlockSize ;
SYSTEM.PUT(dataBlockAdr + TypeDescOffset, elemTag);
SYSTEM.PUT(dataBlockAdr + HeapBlockOffset, arrayBlockAdr);
arrayBlock := SYSTEM.VAL(ArrayBlock, arrayBlockAdr);
arrayBlock.mark := currentMarkValue ;
arrayBlock.dataAdr := dataBlockAdr;
arrayBlock.size := blockSize;
IF isRealtime THEN
arrayBlock.nextRealtime := realtimeList;
realtimeList := arrayBlock
ELSE
arrayBlock.nextRealtime := NIL
END;
fillSize := blockSize - arrayBlockSize - BlockHeaderSize;
Machine.Fill32(dataBlockAdr, fillSize, 0);
firstElem := dataBlockAdr + arrayDataOffset;
SYSTEM.PUT(dataBlockAdr, firstElem + arrSize - elemSize);
SYSTEM.PUT(dataBlockAdr + AddressSize, NIL);
SYSTEM.PUT(dataBlockAdr + 2 * AddressSize, firstElem);
p := SYSTEM.VAL(ANY, dataBlockAdr);
ELSE
p := NIL
END;
Machine.Release(Machine.Heaps)
END
END
END NewArr;
PROCEDURE FillStaticType*(VAR staticTypeAddr: SYSTEM.ADDRESS; startAddr, typeInfoAdr: SYSTEM.ADDRESS; size, recSize: SYSTEM.SIZE;
numPtrs, numSlots: LONGINT);
VAR p, offset: SYSTEM.ADDRESS; staticTypeBlock {UNTRACED}: StaticTypeBlock;
BEGIN
Machine.Acquire(Machine.Heaps);
Machine.Fill32(startAddr, size, 0);
SYSTEM.PUT(startAddr, MethodEndMarker);
offset := AddressSize * (numSlots + 1 + 1);
p := startAddr + offset;
SYSTEM.PUT(p + TypeDescOffset, typeInfoAdr);
staticTypeBlock := SYSTEM.VAL(StaticTypeBlock, p);
staticTypeBlock.recSize := recSize;
staticTypeAddr := p;
INC(p, SYSTEM.SIZEOF(StaticTypeDesc));
IF p MOD (2 * AddressSize) # 0 THEN INC(p, AddressSize) END;
SYSTEM.PUT(p + 3 * AddressSize, numPtrs);
staticTypeBlock.pointerOffsets := SYSTEM.VAL(PointerOffsets, p);
Machine.Release(Machine.Heaps)
END FillStaticType;
PROCEDURE AddFinalizer*(obj: ANY; n: FinalizerNode);
BEGIN
n.objWeak := obj; n.objStrong := NIL; n.finalizerStrong := NIL;
Machine.Acquire(Machine.Heaps);
n.nextFin := checkRoot; checkRoot := n;
IF Stats THEN INC(NfinalizeAlive) END;
Machine.Release(Machine.Heaps)
END AddFinalizer;
PROCEDURE GetHeapInfo*(VAR total, free, largest: SYSTEM.SIZE);
VAR memBlock {UNTRACED}: Machine.MemoryBlock; blockAdr: SYSTEM.ADDRESS;
block {UNTRACED}: HeapBlock;
BEGIN
Machine.Acquire(Machine.Heaps);
memBlock := Machine.memBlockHead;
total := 0; free := 0; largest := 0;
WHILE memBlock # NIL DO
total := total + memBlock.endBlockAdr - memBlock.beginBlockAdr;
blockAdr := memBlock.beginBlockAdr;
WHILE Machine.LessThan(blockAdr, memBlock.endBlockAdr) DO
block := SYSTEM.VAL(HeapBlock, blockAdr + BlockHeaderSize);
IF (block.mark # TemporaryMarkValue) & (block.mark < currentMarkValue) THEN
free := free + block.size;
IF Machine.GreaterThan(block.size, largest) THEN largest := block.size END
END;
blockAdr := blockAdr + block.size;
END;
memBlock := memBlock.next
END;
Machine.Release(Machine.Heaps)
END GetHeapInfo;
PROCEDURE NilGC;
BEGIN
HALT(301)
END NilGC;
PROCEDURE Init;
VAR beginBlockAdr, endBlockAdr, freeBlockAdr, p: SYSTEM.ADDRESS;
heapBlock {UNTRACED}: HeapBlock; freeBlock {UNTRACED}: FreeBlock; memBlock {UNTRACED}: Machine.MemoryBlock;
s: ARRAY 32 OF CHAR; minSize,i: LONGINT;
BEGIN
Machine.GetConfig("EnableFreeLists", s);
EnableFreeLists := (s[0] = "1");
Machine.GetConfig("EnableReturnBlocks", s);
EnableReturnBlocks := (s[0] = "1");
IF EnableReturnBlocks THEN Trace.String("Heaps:ReturnBlocks enabled"); Trace.Ln END;
minSize := 32;
FOR i := 0 TO MaxFreeLists DO
freeLists[i].minSize := minSize;
freeLists[i].first := NIL; freeLists[i].last := NIL;
IF i < FreeListBarrier THEN INC( minSize, BlockSize ) ELSE minSize := 2 * minSize END
END;
GC := NilGC;
newSum := 0;
checkRoot := NIL; finalizeRoot := NIL; rootList := NIL; realtimeList := NIL;
gcStatus := NIL;
Machine.SetGCParams;
Machine.GetStaticHeap(beginBlockAdr, endBlockAdr, freeBlockAdr);
freeBlockTag := SYSTEM.TYPECODE (FreeBlockDesc);
systemBlockTag := SYSTEM.TYPECODE (SystemBlockDesc);
recordBlockTag := SYSTEM.TYPECODE (RecordBlockDesc);
protRecBlockTag := SYSTEM.TYPECODE (ProtRecBlockDesc);
arrayBlockTag := SYSTEM.TYPECODE (ArrayBlockDesc);
p := beginBlockAdr;
heapBlock := SYSTEM.VAL(HeapBlock, p + BlockHeaderSize);
WHILE Machine.LessThan(p, freeBlockAdr) DO
initBlock := SYSTEM.VAL(ANY, heapBlock.dataAdr);
p := p + heapBlock.size;
heapBlock := SYSTEM.VAL(HeapBlock, p + BlockHeaderSize)
END;
ASSERT(p = freeBlockAdr);
IF Machine.GreaterThan(endBlockAdr - freeBlockAdr, 0) THEN
freeBlock := SYSTEM.VAL(FreeBlock, freeBlockAdr + BlockHeaderSize);
InitFreeBlock(freeBlock, Unmarked, NilVal, endBlockAdr - freeBlockAdr);
IF EnableFreeLists THEN AppendFreeBlock(freeBlock) END;
ASSERT(freeBlock.size MOD BlockSize = 0)
END;
currentMarkValue := 1;
Machine.ExpandHeap(1, 1, memBlock, beginBlockAdr, endBlockAdr);
IF Machine.GreaterThan(endBlockAdr, beginBlockAdr) THEN
freeBlock := SYSTEM.VAL(FreeBlock, beginBlockAdr + BlockHeaderSize);
InitFreeBlock(freeBlock, Unmarked, NilVal, endBlockAdr - beginBlockAdr);
Machine.SetMemoryBlockEndAddress(memBlock, endBlockAdr);
IF EnableFreeLists THEN AppendFreeBlock(freeBlock) END;
sweepMarkValue := currentMarkValue;
sweepMemBlock := memBlock;
sweepBlockAdr := beginBlockAdr
END;
END Init;
PROCEDURE SetHeuristic*;
BEGIN
GCType := HeuristicStackInspectionGC;
Trace.String("GC mode : heuristic"); Trace.Ln;
END SetHeuristic;
PROCEDURE SetMetaData*;
BEGIN
GCType := MetaDataForStackGC;
Trace.String("GC mode : metadata"); Trace.Ln;
END SetMetaData;
BEGIN
Trace.String("Heaps: Initializing heap...");
SetHeuristic;
Init;
Trace.Green; Trace.StringLn("... Ok"); Trace.Default;
END Heaps.
(*
TraceHeap:
0 1 NR NEW record
1 2 NA/NV NEW array
2 4 NS SYSTEM.NEW
3 8 DR deallocate record #
4 16 DA deallocate array #
5 32 DS deallocate sysblk #
6 64 NT NewType
7 128
8 256 FB show free blocks #
9 512 DP deallocate protrec #
10 1024 finalizers
11 2048 live/dead #
12 4096 trace mark stack overflows #
# influences timing
*)
(*
20.03.1998 pjm Started
17.08.1998 pjm FindRoots method
18.08.1998 pjm findPossibleRoots removed, use FindRoots method
09.10.1998 pjm NewRec with page alignment
21.01.1999 pjm Mark adapted for AosBuffers
26.01.1999 pjm Incorporated changes for new compiler
10.11.2000 pjm Finalizers
26.01.2001 pjm Removed trapReserve, reimplemented NewBlock
11.11.2004 lb Garbage collector with marking stack
19.06.2007 ug Garbage collector using meta data for stack inspection (cf. Objects)
11.07.2008 ug new heap data structures and adaption to GC
*)