123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137 |
- (* ETH Oberon, Copyright 2002 ETH Zuerich Institut fuer Computersysteme, ETH Zentrum, CH-8092 Zuerich.
- Refer to the "General ETH Oberon System Source License" contract available at: http://www.oberon.ethz.ch/ *)
- MODULE Heaps; (** AUTHOR "G.F."; PURPOSE "Heap management and garbage collector"; *)
- IMPORT S := SYSTEM, Trace, Unix, Machine;
- CONST
- Stats* = TRUE; (* maintain statistical counters *)
-
- AddrSize = SIZEOF( ADDRESS );
- SizeSize = SIZEOF( SIZE );
- FlagsOfs = AddrSize * 3; (* flags offset in TypeDesc *)
- ModOfs* = AddrSize * 4; (* moduleAdr offset in TypeDesc *)
- (* TypeNameOfs = AddrSize * 5; (* type name offset in TypeDesc *)
- ModNameOfs = AddrSize * 2; (* module name offset in ModuleDesc *)
- *)
- NilVal* = 0;
-
- MethodEndMarker* = -40000000H; (* marks the end of the method addresses, used in Info.ModuleDetails *)
- ArrayAlignment = 8;
- HeapBlockOffset* = - 2*AddrSize;
- TypeDescOffset* = -AddrSize;
-
- MaxMarkDepth = 8000;
- ThruputBarrier = Machine.MemBlockSize;
- (* ----------------- object finalization ------------------------------*)
- TYPE
- Finalizer* = PROCEDURE {DELEGATE}( obj: ANY );
- FinalizerNode* = POINTER TO RECORD
- objWeak*{UNTRACED}: ANY; (* weak reference to checked object *)
- markAdr: ADDRESS; (* address of type tag of object *)
- nextFin: FinalizerNode; (* in finalization list *)
- objStrong*: ANY; (* strong reference to object to be finalized *)
- finalizer*{UNTRACED}: Finalizer; (* finalizer, if any *)
- finalizerStrong: Finalizer (* strong ref. to the obj that is referenced by the finalyzer, if any *)
- END;
- VAR
- checkRoot: FinalizerNode; (* list of checked objects (contains weak references to the checked objects) *)
- finalizeRoot: FinalizerNode; (* objects scheduled for finalization (contains references to scheduled objects) *)
- (* ------------------------- Heap ------------------------------- *)
- CONST
- BlockSize = 32;
- MaxFreeLists = 14; (* number of free lists *)
- FreeListBarrier = 7;
-
- MaxCandidates = 1024;
-
- ProtOfs = (AddrSize DIV 4)*(2*BlockSize) + 16; (*! p mod 32 = 16 ! *)
- SysOfs = 24; (*! p mod 16 = 8 ! *)
- ProtTypeBit* = 31; (** flags in TypeDesc, low bits reserved for extLevel *)
- TYPE
- FreeBlock = POINTER TO RECORD
- tag: ADDRESS; (* = ADDRESSOF( size ) *)
- size: SIZE;
- next{UNTRACED}: FreeBlock;
- END;
-
- FreeList = RECORD
- minSize: SIZE;
- first{UNTRACED}: FreeBlock;
- last{UNTRACED}: FreeBlock
- END;
-
- ProcessQueue* = RECORD
- head*, tail*: ANY
- END;
-
- ProtRecBlock* = POINTER TO ProtRecBlockDesc;
- ProtRecBlockDesc* = RECORD
- recSize-: SIZE; (* needed by procedure SizeOf(blk) *)
-
- awaitingLock* : ProcessQueue; (* unused in UnixAos *)
- awaitingCond* : ProcessQueue;
- lockedBy*: ANY;
- lock*: ANY; (* used by Win32, unused for I386 and UnixAos *)
-
- mtx*: Unix.Mutex_t; (* processes blocked awaiting lock (UnixAos only) *)
- enter*: Unix.Condition_t; (* processes blocked awaiting lock (UnixAos only) *)
- END;
- RootObject* = OBJECT
- PROCEDURE FindRoots*; (** abstract *)
- BEGIN HALT( 301 ) END FindRoots;
- END RootObject;
- StaticTypeBlock* = POINTER TO StaticTypeDesc;
- StaticTypeDesc = RECORD
- recSize: SIZE;
- pointerOffsets* {UNTRACED}: PointerOffsets;
- END;
- PointerOffsets = POINTER TO ARRAY OF SIZE;
-
- CandBuffer = ARRAY MaxCandidates OF ADDRESS;
-
- VAR
- freeLists: ARRAY MaxFreeLists + 1 OF FreeList;
- candidates: CandBuffer;
- nofcand: LONGINT;
-
- deferred: ARRAY 1000 OF ADDRESS;
- noDeferred: LONGINT;
-
- heapSize, heapAvailable: SIZE;
- shrinkDisabled: BOOLEAN;
- thruput: SIZE;
- GC* : PROCEDURE;
- InvokeGC* : PROCEDURE;
- collecting-: BOOLEAN;
- markDepth: LONGINT;
-
- saveSP* : PROCEDURE; (* save SP for usage in Objects.Process.FindRoots() *)
-
- (** Statistics. Will only be maintained if Stats = TRUE *)
- (** Memory allocation statistics *)
- Nnew- : LONGINT; (** Number of times NewBlock has been called since system startup *)
- NnewBytes- : HUGEINT; (** Number of bytes allocated by NewBlock since system startup *)
- (** Garbage collection statistics *)
- Ngc- : LONGINT; (** Number of GC cycles since system startup *)
- (** Statistics considering the last GC cyle *)
- Nmark-, Nmarked-, NfinalizeAlive-, NfinalizeDead-: LONGINT;
- NgcCyclesMark-, NgcCyclesLastRun-, NgcCyclesMax-, NgcCyclesAllRuns- : HUGEINT;
-
- PROCEDURE EmptyProc;
- END EmptyProc;
- (* ----------------- object finalization ---------------------------*)
- PROCEDURE AddFinalizer*( obj: ANY; n: FinalizerNode );
- VAR adr: ADDRESS;
- BEGIN
- n.objWeak := obj; n.objStrong := NIL; n.finalizerStrong := NIL;
- adr := S.VAL( ADDRESS, obj );
- IF ODD( adr DIV 8 ) THEN (* indirect tag *)
- S.GET( adr - AddrSize, adr );
- ELSIF ODD( adr DIV 16 ) THEN (* protected object *)
- adr := adr - ProtOfs
- END;
- n.markAdr := adr - AddrSize;
- Machine.Acquire( Machine.Heaps );
- n.nextFin := checkRoot; checkRoot := n;
- Machine.Release( Machine.Heaps )
- END AddFinalizer;
- (* Check reachability of finalized objects. *)
- PROCEDURE CheckFinalizedObjects;
- VAR n, p, t: FinalizerNode; tag: ADDRESS;
- PROCEDURE MarkDelegate( p: Finalizer );
- VAR pointer: ANY;
- BEGIN
- S.GET( ADDRESSOF( p ) + AddrSize, pointer );
- IF pointer # NIL THEN Mark( pointer ) END
- END MarkDelegate;
- BEGIN
- n := checkRoot;
- WHILE n # NIL DO (* move unmarked checked objects to finalize list *)
- S.GET( n.markAdr, tag );
- IF ~ODD( tag ) THEN (* not marked *)
- IF n = checkRoot THEN checkRoot := n.nextFin ELSE p.nextFin := n.nextFin END;
- n.objStrong := n.objWeak; (* anchor the object for finalization *)
- n.finalizerStrong := n.finalizer; (* anchor the finalizer for finalization *)
- 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;
- (* now trace the weak references to keep finalized objects alive during this collection *)
- n := finalizeRoot;
- WHILE n # NIL DO
- MarkDelegate( n.finalizerStrong );
- Mark( n.objStrong ); n := n.nextFin
- END;
- n := checkRoot;
- WHILE n # NIL DO (* list of objects that had been marked before entering CheckFinalizedObjects *)
- (* we still have to mark the weak finalizers, as they might have not been marked before *)
- MarkDelegate( n.finalizer ); n := n.nextFin
- END;
- END CheckFinalizedObjects;
- (** Return the next scheduled finalizer or NIL if none available. Called by finalizer object in AosKernel. *)
- PROCEDURE GetFinalizer*( ): FinalizerNode;
- VAR n: FinalizerNode;
- BEGIN
- n := NIL;
- IF finalizeRoot # NIL THEN
- Machine.Acquire( Machine.Heaps );
- n := finalizeRoot; (* take one finalizer *)
- 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;
- (** Check finalizers registered in the specified module, which is about to be freed or shut down.
- Remove all finalizer procedures in this module from the finalizer lists so they won't be called any more. *)
- PROCEDURE CleanupModuleFinalizers*( codeAdr: ADDRESS; codeLen: SIZE; CONST name: ARRAY OF CHAR );
- VAR n, p, t: FinalizerNode; codeEnd: ADDRESS; N1, N2: LONGINT;
- BEGIN
- codeEnd := codeAdr + codeLen; N1 := 0; N2 := 0;
- Machine.Acquire( Machine.Heaps );
- n := checkRoot;
- WHILE n # NIL DO (* iterate over checked list *)
- t := n; n := n.nextFin;
- IF (codeAdr <= S.VAL( ADDRESS, t.finalizer )) & (S.VAL( 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;
- (* also remove finalizers from list, so they won't be called *)
- n := finalizeRoot;
- WHILE n # NIL DO (* iterate over finalized list *)
- t := n; n := n.nextFin;
- IF (codeAdr <= S.VAL( ADDRESS, t.finalizer ) ) & (S.VAL( 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;
-
-
-
- (* Add a root object to the set of traversable objects. If in allocated heap then mark and traverse, if in Module Heap (Bootfile) then only traverse. *)
- PROCEDURE AddRootObject*( rootObject: RootObject );
- BEGIN
- Mark( rootObject )
-
- (* IF rootObject = NIL THEN (* nothing *)
- ELSIF CheckPointer(SYSTEM.VAL(ADDRESS,rootObject)) THEN
- (* object in heap, must be fully marked and traversed *)
- Mark(rootObject)
- ELSE
- (* object in bootfile, traverse as root object only *)
- rootObject.nextRoot := rootList; rootList := rootObject; (* link root list *)
- END *)
- END AddRootObject;
- (* ------------------------- garbage collector ----------------------- *)
- PROCEDURE UnmarkedObject( ptr: ANY ): BOOLEAN; (* FALSE: alredy marked or sysblock *)
- VAR addr, taddr: ADDRESS; tag: ADDRESS; sysblock: BOOLEAN;
- BEGIN
- IF ptr = NIL THEN RETURN FALSE END;
- addr := S.VAL( ADDRESS, ptr );
- IF ~ValidPointer( addr ) THEN RETURN FALSE END;
-
- sysblock := FALSE; taddr := addr - AddrSize;
- IF ODD( addr DIV 8 ) THEN (* sysblock *)
- taddr := taddr - SysOfs; sysblock := TRUE
- ELSIF ODD( addr DIV 16 ) THEN (* protected object *)
- taddr := taddr - ProtOfs;
- END;
- S.GET( taddr, tag );
- IF ODD( tag ) THEN
- (* already marked *) RETURN FALSE
- ELSE
- S.PUT( taddr, tag + 1 ); (* mark this block *) INC( Nmarked );
- IF sysblock THEN RETURN FALSE END;
- IF ptr IS RootObject THEN ptr(RootObject).FindRoots END;
- RETURN TRUE
- END;
- END UnmarkedObject;
- PROCEDURE MarkRecordFields( rec: ADDRESS; sTB: StaticTypeBlock );
- VAR ptr: ANY; i, n: SIZE;
- BEGIN
- n := LEN( sTB.pointerOffsets ); i := 0;
- WHILE i < n DO
- S.GET( rec + sTB.pointerOffsets[i], ptr );
- IF ptr # NIL THEN Mark( ptr ) END;
- INC( i )
- END
- END MarkRecordFields;
-
- PROCEDURE Mark*( ptr: ADDRESS );
- VAR
- cur, lastElem, tag: ADDRESS;
- sTB{UNTRACED}: StaticTypeBlock;
- BEGIN
- IF Stats THEN INC(Nmark) END;
- INC( markDepth );
-
- IF UnmarkedObject( S.VAL( ANY, ptr ) ) THEN
- S.GET( ptr - AddrSize, tag );
- sTB := S.VAL( StaticTypeBlock, tag DIV 4 * 4 );
- IF ODD( tag DIV 2 ) THEN (* array *)
- IF markDepth <= MaxMarkDepth - 10 THEN
- S.GET( ptr, lastElem );
- S.GET( ptr + 2*AddrSize, cur );
- REPEAT
- MarkRecordFields( cur, sTB );
- INC( cur, sTB.recSize );
- UNTIL cur > lastElem
- ELSE
- deferred[noDeferred] := ptr; INC( noDeferred );
- END;
- ELSE
- IF markDepth <= MaxMarkDepth THEN
- MarkRecordFields( ptr, sTB )
- ELSE
- deferred[noDeferred] := ptr; INC( noDeferred );
- END;
- END;
- END;
- DEC( markDepth );
- IF (markDepth <= 0) & (noDeferred > 0) THEN MarkDeferred END
- END Mark;
- PROCEDURE MarkDeferred;
- VAR
- ptr, cur, lastElem, tag: ADDRESS;
- sTB{UNTRACED}: StaticTypeBlock;
- BEGIN
- markDepth := 1;
- WHILE noDeferred > 0 DO
- DEC( noDeferred );
- ptr := deferred[noDeferred];
- S.GET( ptr - AddrSize, tag );
- sTB := S.VAL( StaticTypeBlock, tag DIV 4 * 4 );
- IF ODD( tag DIV 2 )THEN
- S.GET( ptr, lastElem );
- S.GET( ptr + 2*AddrSize, cur );
- REPEAT
- MarkRecordFields( cur, sTB );
- INC( cur, sTB.recSize );
- UNTIL cur > lastElem
- ELSE
- MarkRecordFields( ptr, sTB )
- END;
- END;
- END MarkDeferred;
- PROCEDURE AppendFree( VAR freeList: FreeList; block: FreeBlock );
- BEGIN
- 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 InsertSorted( VAR freeList: FreeList; block: FreeBlock );
- VAR x: FreeBlock;
- BEGIN
- (* keep them ordered to avoid unnecessary splits *)
- (* this optimization has positive impact on heap utilization
- 130 MB vs. 240 MB heap for compiling and linking a new system
- but it slows down heap allocation speed. *)
- x := freeList.first;
- IF (x = NIL) OR (x.size > block.size) THEN
- block.next := x;
- freeList.first := block
- ELSE
- WHILE ( x.next # NIL) & (x.next.size < block.size) DO x := x.next END;
- block.next := x.next;
- x.next := block
- END
- END InsertSorted;
-
- PROCEDURE Recycle( blkAdr: ADDRESS; blkSize: SIZE );
- VAR i: LONGINT; block: FreeBlock;
- BEGIN
- (* ASSERT( blkSize MOD BlockSize = 0 ); *)
- block := S.VAL( FreeBlock, blkAdr );
- block.tag := blkAdr + AddrSize;
- block.size := blkSize - AddrSize;
- block.next := NIL;
-
- i := 0;
- WHILE (i < MaxFreeLists) & (freeLists[i+1].minSize < blkSize) DO INC( i ) END;
-
- IF i < FreeListBarrier THEN
- AppendFree( freeLists[i], block )
- ELSE
- InsertSorted( freeLists[i], block )
- END;
- INC( heapAvailable, blkSize );
- END Recycle;
-
-
- PROCEDURE ClearFreeLists;
- VAR i, minSize: LONGINT;
- BEGIN
- minSize := BlockSize;
- 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;
- heapAvailable := 0
- END ClearFreeLists;
-
-
- PROCEDURE ShowFreeLists( CONST msg: ARRAY OF CHAR );
- VAR i, n: LONGINT; m: SIZE; b: FreeBlock; bad: BOOLEAN;
- BEGIN
- Trace.Ln;
- Trace.String( "==== FreeLists " ); Trace.String( msg ); Trace.Ln;
- FOR i := 0 TO MaxFreeLists DO
- Trace.Int( i, 2 ); Trace.Int( freeLists[i].minSize, 6 ); Trace.String( ": " );
- b := freeLists[i].first; n := 0; m := 0; bad := FALSE;
- IF i < FreeListBarrier THEN
- WHILE b # NIL DO INC( n ); b := b.next END;
- ELSE
- WHILE b # NIL DO
- INC( n );
- IF n < 20 THEN
- Trace.Int( b.size, 1 ); Trace.Char( ',' );
- IF n MOD 8 = 0 THEN Trace.Ln; Trace.String( " " ) END;
- END;
- IF b.size < m THEN bad := TRUE ELSE m := b.size END;
- b := b.next
- END;
- IF n > 0 THEN Trace.String( " largest=" ); Trace.Int( m, 0 ) END;
- IF bad THEN Trace.String( ", bad ordered" ) END
- END;
- Trace.String( " [" ); Trace.Int( n, 0 ); Trace.StringLn( "]" )
- END;
- Trace.Ln
- END ShowFreeLists;
-
- PROCEDURE Sweep;
- VAR
- block, freeBlock, endBlockAdr, tag: ADDRESS;
- blockSize, freeSize: SIZE;
- memBlock, nextMemBlock: Machine.MemoryBlock;
- BEGIN
- ClearFreeLists;
- heapAvailable := 0;
- memBlock := Machine.memBlockHead;
- WHILE memBlock # NIL DO
- block := memBlock.beginBlockAdr; endBlockAdr := memBlock.endBlockAdr;
- freeSize := 0;
- WHILE block < endBlockAdr DO
- blockSize := SizeOf( block );
- S.GET( block, tag );
- IF ~ODD( tag) THEN
- (* collect *)
- IF freeSize = 0 THEN freeBlock := block END;
- INC( freeSize, blockSize );
- ELSE
- S.PUT( block, tag - 1 ); (* remove mark bit *)
- IF freeSize > 0 THEN
- Recycle( freeBlock, freeSize );
- freeSize := 0
- END
- END;
- INC( block, blockSize );
- END;
- nextMemBlock := memBlock.next;
-
- IF (freeSize = endBlockAdr - memBlock.beginBlockAdr) THEN
- (* whole block is free, unlink it*)
- IF shrinkDisabled THEN
- Recycle( freeBlock, freeSize ); (* last collected block: *)
- ELSE
- Machine.FreeMemBlock( memBlock );
- heapSize := DetermineHeapSize()
- END
- ELSIF freeSize > 0 THEN
- Recycle( freeBlock, freeSize ); (* last collected block: *)
- END;
-
- memBlock := nextMemBlock;
- END;
- (* ShowFreeLists( "after Sweep" ) *)
- END Sweep;
- PROCEDURE SizeOf( block: ADDRESS ): SIZE;
- VAR tag, lastElem: ADDRESS; recSize, blockSize: SIZE;
- BEGIN
- S.GET( block, tag );
- S.GET( tag DIV 4 * 4, recSize );
- IF ODD( tag DIV 2 ) THEN (* array *)
- S.GET( block + AddrSize, lastElem );
- blockSize := lastElem + recSize - block
- ELSE
- blockSize := recSize + AddrSize
- END;
- INC( blockSize, (-blockSize) MOD BlockSize );
- RETURN blockSize
- END SizeOf;
-
- PROCEDURE CheckCandidates;
- VAR
- i, j, h, nc: LONGINT; mb: Machine.MemoryBlock;
- buffer: CandBuffer;
- cand, p, tag, tag2, block: ADDRESS;
- blkSize: SIZE;
-
- PROCEDURE NextCandidate(): ADDRESS;
- VAR cand: ADDRESS;
- BEGIN
- IF i < nc THEN cand := buffer[i]; INC( i ) ELSE cand := 0 END;
- RETURN cand
- END NextCandidate;
-
- BEGIN
- IF nofcand = 0 THEN RETURN END;
-
- buffer := candidates; nc := nofcand; nofcand := 0;
- (* sort buffer in increasing order using shellsort *)
- h := 1;
- REPEAT h := h*3 + 1 UNTIL h > nc;
- REPEAT
- h := h DIV 3; i := h;
- WHILE i < nc DO
- p := buffer[i]; j := i;
- WHILE (j >= h) & (buffer[j - h] > p) DO
- buffer[j] := buffer[j - h]; j := j - h
- END;
- buffer[j] := p; INC( i )
- END
- UNTIL h = 1;
- i := 0; cand := NextCandidate();
- mb := Machine.memBlockHead;
- REPEAT
- IF (cand < mb.endBlockAdr) & (buffer[nc-1] > mb.beginBlockAdr) THEN
- block := mb.beginBlockAdr;
- REPEAT
- blkSize := SizeOf( block );
- IF cand <= block + AddrSize THEN
- IF cand = block + AddrSize THEN
- S.GET( block, tag );
- IF tag = cand THEN (* free block *)
- ELSE (* record or array *) Mark( cand )
- END
- END;
- cand := NextCandidate( );
- ELSIF cand = block + AddrSize + SysOfs THEN (* sysblock ? *)
- IF blkSize > AddrSize + SysOfs THEN
- S.GET( block, tag ); S.GET( cand - AddrSize, tag2 );
- IF (tag2 = cand - SysOfs) & (tag2 = tag) THEN (* sysblock *) Mark( cand ) END;
- cand := NextCandidate( )
- ELSE
- block := block + blkSize
- END
- ELSIF cand = block + AddrSize + ProtOfs THEN (* protected record ? *)
- IF blkSize > AddrSize + ProtOfs THEN
- S.GET( block, tag );
- IF tag = block + AddrSize THEN (* protected record *) Mark( cand ) END;
- cand := NextCandidate( )
- ELSE
- block := block + blkSize
- END;
- ELSE
- block := block + blkSize
- END;
- UNTIL (cand = 0) OR (block >= mb.endBlockAdr) OR (cand >= mb.endBlockAdr);
- END;
- mb := mb.next;
- UNTIL (mb = NIL) OR (cand = 0);
- END CheckCandidates;
-
- PROCEDURE AddCandidate*( p: ADDRESS );
- VAR tag0Addr, tag0, tag: ADDRESS; i: LONGINT;
- BEGIN
- IF p MOD 32 = 0 THEN
- tag0Addr := p - AddrSize (* RecBlk, ArrBlk *)
- ELSIF p MOD 32 = 16 THEN
- tag0Addr := p - ProtOfs - AddrSize (* ProtRecBlk *)
- ELSIF p MOD 16 = 8 THEN
- tag0Addr := p - SysOfs - AddrSize (* SysBlk *)
- ELSE
- (* p is not a pointer *) RETURN
- END;
-
- i := 0;
- WHILE i < nofcand DO
- IF p = candidates[i] THEN (* double *) RETURN END;
- INC( i )
- END;
-
- IF ValidAddress( tag0Addr ) THEN
- S.GET( tag0Addr, tag0 );
- IF ODD( tag0 ) THEN (* already marked *) RETURN END;
- S.GET ( p - AddrSize, tag );
- IF ValidAddress( tag DIV 4 * 4 ) THEN
- candidates[nofcand] := p; INC( nofcand );
- IF nofcand = MaxCandidates THEN CheckCandidates END
- END;
- END
- END AddCandidate;
- PROCEDURE CollectGarbage*( root: RootObject );
- VAR time1, time2 : HUGEINT;
- BEGIN
- IF Stats THEN
- Nmark := 0; Nmarked := 0;
- INC( Ngc );
- time1 := Machine.GetTimer( );
- END;
- collecting := TRUE; markDepth := 0; noDeferred := 0; nofcand := 0;
-
- Mark( root );
- REPEAT CheckCandidates UNTIL nofcand = 0;
- CheckFinalizedObjects;
- Sweep;
- collecting := FALSE; thruput := 0;
- IF Stats THEN
- time2 := Machine.GetTimer( );
- NgcCyclesLastRun := time2 - time1;
- IF NgcCyclesLastRun > NgcCyclesMax THEN NgcCyclesMax := NgcCyclesLastRun END;
- INC( NgcCyclesAllRuns, NgcCyclesLastRun );
- NgcCyclesMark := NgcCyclesLastRun
- END;
- END CollectGarbage;
-
- (* -------------------------- memory allocation ----------------------- *)
- PROCEDURE FindFreeBlock( size: SIZE ): FreeBlock;
- VAR prev, block: FreeBlock; i: LONGINT;
- BEGIN
- i := 0;
- WHILE (i < MaxFreeLists) & (freeLists[i+1].minSize <= size) DO INC( i ) END;
-
- REPEAT
- block := freeLists[i].first;
- IF block # NIL THEN
- IF block.size + AddrSize >= size THEN
- IF block = freeLists[i].last THEN freeLists[i].first := NIL; freeLists[i].last := NIL
- ELSE freeLists[i].first := block.next; block.next := NIL
- END;
- ELSE (* i = MaxFreeLists *)
- REPEAT prev := block; block := block.next
- UNTIL (block = NIL) OR (block.size + AddrSize >= size);
- IF block # NIL THEN prev.next := block.next END
- END
- END;
- INC( i )
- UNTIL (block # NIL) OR (i > MaxFreeLists);
- RETURN block
- END FindFreeBlock;
-
-
- PROCEDURE Collect;
- BEGIN
- thruput := 0;
- Machine.Release( Machine.Heaps );
- GC;
- Machine.Acquire( Machine.Heaps );
- END Collect;
- PROCEDURE GetBlock( size: SIZE ): ADDRESS; (* size MOD B = 0 *)
- VAR
- block: FreeBlock; blkSize: SIZE; blkAdr, adr2: ADDRESS;
- BEGIN
- IF (thruput > ThruputBarrier) OR (heapAvailable < size) THEN Collect END;
-
- REPEAT
- block := FindFreeBlock( size );
- IF block = NIL THEN
- IF thruput > 0 THEN Collect
- ELSE
- (* ShowFreeLists( "befor ExpandHeap" ); *)
- Machine.ExpandHeap( 0, size, S.VAL( ADDRESS, block ), adr2 );
- IF block # NIL THEN
- heapSize := DetermineHeapSize();
- ELSE
- Trace.Ln;
- Trace.String( "Heapspace exhausted" ); Trace.Ln;
- Machine.Release( Machine.Heaps );
- HALT( 99 )
- END
- END
- END
- UNTIL block # NIL;
-
- blkSize := block.size + AddrSize;
- blkAdr := S.VAL( ADDRESS, block );
- DEC( heapAvailable, blkSize );
- IF blkSize > size THEN Recycle( blkAdr + size, blkSize - size ) END;
-
- INC( thruput, size );
- IF Stats THEN INC(Nnew); INC(NnewBytes, size) END;
- Machine.Fill32( blkAdr, size, 0 );
- IF saveSP # NIL THEN saveSP END;
- RETURN blkAdr
- END GetBlock;
- (** Private compiler interface. Do not use. *)
- PROCEDURE NewRec*( VAR p: ANY; tag: ADDRESS; isRealtime: BOOLEAN ); (* implementation of NEW( ptr ) *)
- VAR size, recSize: SIZE; ptr: ADDRESS; typeInfoAdr: ADDRESS; flags: SET;
- BEGIN
- S.GET( tag - AddrSize, typeInfoAdr );
- S.GET( typeInfoAdr + FlagsOfs, flags );
- IF ProtTypeBit IN flags THEN (* protected record *)
- NewProtRec( p, tag, isRealtime ); RETURN
- END;
- S.GET( tag, recSize );
- size := recSize + AddrSize; INC( size, (-size) MOD BlockSize );
-
- Machine.Acquire( Machine.Heaps );
- ptr := GetBlock( size ) + AddrSize;
- S.PUT( ptr - AddrSize, tag );
- p := S.VAL( ANY, ptr );
- Machine.Release( Machine.Heaps )
- END NewRec;
-
-
- (** Private compiler interface. Do not use. *)
- PROCEDURE NewProtRec*( VAR p: ANY; tag: ADDRESS; isRealtime: BOOLEAN );
- VAR recSize, size: SIZE; ptr0, ptr: ADDRESS;
- BEGIN
- S.GET( tag, recSize );
- (* add space for tag and header and round up to BlockSize *)
- size := recSize + ProtOfs + AddrSize; INC( size, (-size) MOD BlockSize );
-
- Machine.Acquire( Machine.Heaps );
- ptr0 := GetBlock( size ) + AddrSize;
- S.PUT( ptr0 - AddrSize, ptr0 ); (* set the tag needed by Sweep *)
- S.PUT( ptr0, size - AddrSize ); (* size, needed by procedure SizeOf(blk) *)
-
- ptr := ptr0 + ProtOfs; (* mod 32 = 16 ! *)
- S.PUT( ptr + HeapBlockOffset, ptr0 );
- S.PUT( ptr + TypeDescOffset, tag ); (* set the tag *)
-
- p := S.VAL( ANY, ptr );
- Machine.Release( Machine.Heaps );
- END NewProtRec;
-
- PROCEDURE SetPC*( p: ANY );
- BEGIN
- (* not implemented *)
- HALT( 100 );
- END SetPC;
- (** Private compiler interface. Do not use. *)
- PROCEDURE NewSys*( VAR p: ANY; size: SIZE; isRealtime: BOOLEAN ); (* implementation of S.NEW(ptr, size) *)
- VAR ptr: ADDRESS;
- BEGIN
- size := size + AddrSize + SysOfs; INC( size, (-size) MOD BlockSize );
- Machine.Acquire( Machine.Heaps );
- ptr := GetBlock( size ) + AddrSize;
- S.PUT( ptr - AddrSize, ptr ); (* tag needed by Sweep *)
- S.PUT( ptr, size - AddrSize ); (* size, needed by procedure SizeOf(blk) *)
- S.PUT( ptr + AddrSize, S.VAL( ADDRESS, -AddrSize ) );
-
- S.PUT( ptr + SysOfs - AddrSize, ptr ); (* tag *)
- p := S.VAL( ANY, ptr + SysOfs ); (* mod 16 = 8 ! *)
- Machine.Release( Machine.Heaps )
- END NewSys;
- (** Private compiler interface. Do not use. *)
- PROCEDURE NewArr*( VAR p: ANY; eltag: ADDRESS; nofelem, nofdim: SIZE; isRealtime: BOOLEAN );
- VAR
- sTB: StaticTypeBlock;
- arrSize, blkSize, dataOffset: SIZE; ptr, firstElem: ADDRESS;
- BEGIN
- sTB := S.VAL( StaticTypeBlock, eltag );
- arrSize := nofelem*sTB.recSize;
- IF arrSize = 0 THEN
- NewSys( p, nofdim*4 + 3*AddrSize, isRealtime );
- ELSE
- dataOffset := 3*AddrSize + nofdim*AddrSize;
- INC( dataOffset, (-dataOffset) MOD ArrayAlignment );
- IF LEN( sTB.pointerOffsets^ ) = 0 THEN
- (* no pointers in element type *)
- NewSys( p, dataOffset + arrSize, isRealtime );
- ELSE
- blkSize := dataOffset + arrSize + AddrSize; INC( blkSize, (-blkSize) MOD BlockSize );
- Machine.Acquire( Machine.Heaps );
- ptr := GetBlock( blkSize ) + AddrSize;
- S.PUT( ptr - AddrSize, eltag + 2 (*ArrayBit*) );
- firstElem := ptr + dataOffset;
- S.PUT( ptr, firstElem + arrSize - sTB.recSize ); (* last elem *)
- (* ptr + 4 is reserved for mark phase *)
- S.PUT( ptr + 2*AddrSize, firstElem );
- p := S.VAL( ANY, ptr );
- Machine.Release( Machine.Heaps )
- END
- END;
- END NewArr;
-
- TYPE
- ArrayDataBlockDesc*= RECORD
- numElems: SIZE;
- current: ADDRESS; (* unused *)
- first: ADDRESS;
- END;
- UnsafeArray= POINTER {UNSAFE} TO UnsafeArrayDesc;
- UnsafeArrayDesc = RECORD (ArrayDataBlockDesc)
- len: ARRAY 8 OF SIZE;
- END;
-
- (* replacement for overcomplicated code emission -- at the cost of a slightly increased runtime cost *)
- PROCEDURE NewArray*(CONST a: ARRAY OF SIZE; tag: ADDRESS; staticElements, elementSize: SIZE; VAR dest: ANY);
- VAR p: ANY; dim: SIZE;
-
- PROCEDURE GetSize(): SIZE;
- VAR i: SIZE; size: SIZE;
- BEGIN
- size := 1;
- FOR i := 0 TO dim-1 DO
- size := size * a[i];
- END;
- RETURN size*staticElements;
- END GetSize;
-
- PROCEDURE SetSizes(dest: UnsafeArray);
- VAR i: SIZE;
- BEGIN
- FOR i := 0 TO dim-1 DO
- dest.len[i] := a[dim-1-i];
- END;
- END SetSizes;
- BEGIN
- (* static elements is requred for this case : POINTER TO ARRAY OF ARRAY X OF RecordWithPointer *)
- dim := LEN( a,0 );
- IF tag = NIL THEN
- NewSys(p, GetSize() * elementSize + dim * SIZEOF(ADDRESS) + 3 *SIZEOF(ADDRESS) + (dim DIV 2) * 2 * SIZEOF(ADDRESS), FALSE);
- ELSE
- NewArr(p, tag, GetSize(), dim, FALSE);
- END;
- SetSizes(p);
- dest := p;
- END NewArray;
-
- PROCEDURE FillStaticType* ( VAR staticTypeAddr: ADDRESS;
- startAddr, typeInfoAdr: ADDRESS;
- size, recSize: SIZE;
- numPtrs, numSlots: LONGINT );
- VAR
- p, offset: ADDRESS; sTB {UNTRACED}: StaticTypeBlock;
- BEGIN
- Machine.Acquire( Machine.Heaps );
-
- Machine.Fill32( startAddr, size, 0 ); (* clear whole static type, size MOD AddrSize = 0 implicitly, see WriteType in PCOF.Mod *)
- S.PUT( startAddr, S.VAL( ADDRESS, -AddrSize ) ); (* sentinel *)
- (* methods and tags filled in later *)
- offset := AddrSize*(numSlots + 1 + 1); (* #methods, max. no. of tags, method end marker (sentinel), pointer to type information*)
- p := startAddr + offset;
- S.PUT( p - AddrSize, typeInfoAdr ); (* pointer to typeInfo *)
- sTB := S.VAL( StaticTypeBlock, p );
- sTB.recSize := recSize;
- staticTypeAddr := p;
-
- (* create the pointer for the dynamic array of pointer offsets, the dynamic array of pointer offsets
- is stored in the static type descriptor, it has no header part *)
- INC( p, SIZEOF(StaticTypeDesc) );
- IF p MOD (2 * AddrSize) # 0 THEN INC( p, AddrSize ) END;
- S.PUT( p + 3 * AddrSize, numPtrs ); (* internal structure of dynamic array without pointers: the first 3 fields are unused *)
- sTB.pointerOffsets := S.VAL( PointerOffsets, p ); (* the fourth field contains the dimension of the array *)
- (* ptrOfs filled in later *)
- Machine.Release( Machine.Heaps )
- END FillStaticType;
- (*------------------------------ misc ----------------------------------------*)
- (** WriteType - Write a type name (for tracing only). *)
- PROCEDURE WriteType*( t: ADDRESS ); (* t is static type descriptor *)
- VAR m, a: ADDRESS; i: LONGINT; ch: CHAR;
- BEGIN
- S.GET( t - AddrSize, t );
- S.GET( t + 4*AddrSize, m );
- IF m # 0 THEN
- a := m + AddrSize;
- i := 0; S.GET( a, ch );
- WHILE (ch >= '0') & (ch <= 'z') & (i < 32) DO
- Trace.Char( ch );
- INC( i ); S.GET( a + i, ch )
- END
- ELSE Trace.String( "NIL" )
- END;
- Trace.Char( '.' );
- a := t + 5*AddrSize; i := 0; S.GET( a, ch );
- WHILE (ch >= '0') & (ch <= 'z') & (i < 32) DO
- Trace.Char( ch );
- INC( i ); S.GET( a + i, ch )
- END;
- IF i = 0 THEN Trace.String( "-" ) END;
- END WriteType;
- PROCEDURE ValidAddress*( p: ADDRESS ): BOOLEAN;
- VAR sb: Machine.MemoryBlock;
- BEGIN
- IF (p # 0 ) & (p MOD 4 = 0) THEN
- sb := Machine.memBlockHead;
- WHILE sb # NIL DO
- IF (sb.beginBlockAdr <= p) & (p <= sb.endBlockAdr) THEN RETURN TRUE END;
- sb := sb.next;
- END
- END;
- RETURN FALSE
- END ValidAddress;
- PROCEDURE ValidPointer( p: ADDRESS ): BOOLEAN; (* check if p is a valid pointer into the Heap *)
- VAR tag: ADDRESS; ok: BOOLEAN;
- BEGIN
- ok := FALSE; tag := 0;
- IF (p MOD 8 = 0) & ValidAddress( p ) THEN
- IF p MOD 16 = 8 THEN ok := TRUE (* subobject or sysblock *)
- ELSE
- S.GET( p - AddrSize, tag );
- ok := ValidAddress( tag DIV 4 * 4 )
- END
- END;
- IF ~ok THEN
- Trace.String( "illegal pointer value: " ); Trace.Hex( p, -8 );
- IF tag # 0 THEN
- Trace.String( " (bad tag: " ); Trace.Hex( tag, -8 ); Trace.Char( ')' )
- END;
- Trace.Ln
- END;
- RETURN ok
- END ValidPointer;
-
- (* Returns the size in bytes of the remaining free heap *)
- PROCEDURE Available( ): SIZE;
- VAR i: LONGINT; avail: SIZE; block: FreeBlock;
- BEGIN
- avail := 0; i := 0;
- WHILE i <= MaxFreeLists DO
- block := freeLists[i].first;
- WHILE block # NIL DO
- INC( avail, block.size + AddrSize ); block := block.next
- END;
- INC( i )
- END;
- RETURN avail
- END Available;
-
- (** Returns the total heap size of the Oberon system. *)
- PROCEDURE HeapSize*( ): SIZE;
- BEGIN
- RETURN heapSize;
- END HeapSize;
-
- (** none portable, only for debugging *)
- PROCEDURE InspectFreeLists*;
- BEGIN
- Machine.Acquire( Machine.Heaps );
- ShowFreeLists( "" );
- Machine.Release( Machine.Heaps );
- END InspectFreeLists;
-
- PROCEDURE GetHeapInfo*( VAR total, free, largest: SIZE );
- VAR i: LONGINT; block: FreeBlock;
- BEGIN
- free := 0; largest := 0; i := 0;
- Machine.Acquire( Machine.Heaps );
- total := heapSize;
- WHILE i <= MaxFreeLists DO
- block := freeLists[i].first;
- WHILE block # NIL DO
- INC( free, block.size + AddrSize );
- IF block.size > largest THEN largest := block.size END;
- block := block.next;
- END;
- INC( i )
- END;
- Machine.Release( Machine.Heaps );
- END GetHeapInfo;
- PROCEDURE Used*( ): SIZE;
- VAR used: SIZE;
- BEGIN
- Machine.Acquire( Machine.Heaps );
- used := heapSize - heapAvailable;
- Machine.Release( Machine.Heaps );
- RETURN used
- END Used;
- PROCEDURE DetermineHeapSize( ): SIZE;
- VAR heap: SIZE; sb: Machine.MemoryBlock;
- BEGIN
- sb := Machine.memBlockHead; heap := 0;
- WHILE sb # NIL DO heap := heap + sb.size; sb := sb.next END;
- RETURN heap;
- END DetermineHeapSize;
- (*------------------ Initialization --------------------------------------------------*)
-
- (*
- (* for debugging the static linker output *)
- PROCEDURE BlockInfo( block: ADDRESS );
- VAR
- lastElem: ADDRESS; recSize, blockSize: SIZE;
- tag0, ttag, tag: ADDRESS;
- BEGIN
- S.GET( block, tag );
- tag0 := tag DIV 4 * 4 );
- S.GET( tag0, recSize );
- Trace.Hex( block, -8 ); Trace.Char( ' ' ); Trace.Hex( tag0, -8 ); Trace.Char( ' ' );
- IF ODD( tag DIV 2 ) THEN
- S.GET( block + AddrSize, lastElem );
- blockSize := lastElem + recSize - block;
- INC( blockSize, (-blockSize) MOD BlockSize );
- Trace.String( "array of " ); WriteType( tag0 )
- ELSE
- blockSize := recSize + AddrSize;
- INC( blockSize, (-blockSize) MOD BlockSize );
- IF tag0 # block + AddrSize THEN
- WriteType( tag0 )
- ELSE
- S.GET( block + SysOfs, ttag );
- IF ttag = tag0 THEN Trace.String( "sysblock" )
- ELSE
- S.GET( block + ProtOfs - AddrSize, ttag );
- IF ttag = tag0 THEN
- Trace.String( "prot. " );
- S.GET( block + ProtOfs, ttag ); WriteType( ttag )
- ELSE
- Trace.String( "?" )
- END
- END
- END
- END;
-
- Trace.Char( ' ' ); Trace.Int( blockSize, 1 );
- Trace.Ln
- END BlockInfo;
- *)
-
- PROCEDURE InitHeap;
- VAR adr2: ADDRESS;
- block, tag: ADDRESS; frBlock:FreeBlock;
- n: LONGINT;
- str: ARRAY 32 OF CHAR;
- BEGIN
- GC := EmptyProc; (* no GC until EmptyProc gets replaced (in module Objects) *)
- nofcand := 0;
-
- block := Machine.memBlockHead.beginBlockAdr;
- S.GET( block, tag ); n := 0;
- WHILE tag # 0 DO
- (* IF n < 200 THEN BlockInfo( block ); INC( n ) END; *)
- INC( block, SizeOf( block ) );
- S.GET( block, tag );
- END;
- S.PUT( block, block + AddrSize ); (* tag *)
- S.PUT( block + AddrSize, Machine.memBlockHead.endBlockAdr - block - AddrSize ); (* size *)
- S.PUT( block + AddrSize + SizeSize, S.VAL( ADDRESS, 0 ) ); (* next *)
-
- ClearFreeLists;
- freeLists[MaxFreeLists].first := S.VAL( FreeBlock, block );
-
- Machine.ExpandHeap( 0, 3*Machine.MemBlockSize - 2*BlockSize, S.VAL( ADDRESS, frBlock ), adr2 );
- IF frBlock # NIL THEN freeLists[MaxFreeLists].first.next := frBlock END;
- heapSize := DetermineHeapSize();
- heapAvailable := Available();
-
- Machine.GetConfig( "DisableShrinkHeap", str );
- shrinkDisabled := str[0] = '1';
- IF shrinkDisabled THEN
- Trace.StringLn( "#### Heap shrinking disabled" );
- END;
- IF Stats THEN
- Ngc := 0;
- Nmark := 0; Nmarked := 0; NfinalizeAlive := 0; NfinalizeDead := 0;
- NgcCyclesMark := 0; NgcCyclesLastRun := 0; NgcCyclesMax := 0; NgcCyclesAllRuns := 0;
- END
- END InitHeap
- BEGIN
- InitHeap;
- END Heaps.
|