home *** CD-ROM | disk | FTP | other *** search
/ Amiga MA Magazine 1998 #6 / amigamamagazinepolishissue1998.iso / coders / collector / collector.mod < prev    next >
Text File  |  1995-03-31  |  25KB  |  881 lines

  1. (* (* $VER: Collector 1.0 (10-Feb-94) Copyright © by Lars Düning *) *)
  2.  
  3. MODULE Collector;
  4.  
  5. (*---------------------------------------------------------------------------
  6. ** Allocation and garbage-collection of elemental objects.
  7. **
  8. ** Copyright © 1993-1994 by Lars Düning  -  All rights reserved.
  9. ** Permission granted for non-commercial use.
  10. **---------------------------------------------------------------------------
  11. ** The GC is a variant of Dijkstra's incremental update collector as
  12. ** described in:
  13. **   Paul R. Wilson: Uniprocessor Garbage Collection Techniques
  14. **   1992 International Workshop on Memory Management
  15. **   Springer-Verlag, Lecture Notes
  16. **---------------------------------------------------------------------------
  17. ** Oberon-2: Amiga-Oberon v3.10, F. Siebert / A+L AG
  18. **---------------------------------------------------------------------------
  19. ** [lars] Lars Düning; Am Wendenwehr 25; D-38114-Braunschweig;
  20. **                     Germany; Tel. 49-531-345692
  21. **---------------------------------------------------------------------------
  22. ** 01-Nov-93 [lars]
  23. ** 16-Nov-93 [lars] Option 'NoStatistics' added.
  24. ** 03-Feb-94 [lars] Split the two big rings into several small ones.
  25. **                  Reduced the number of ring reconfigurations during GC.
  26. ** 07-Feb-94 [lars] gcRuns is needed even without 'Statistics'
  27. ** 09-Feb-94 [lars] Option 'Statistics' dropped; enforced collection.
  28. ** 10-Feb-94 [lars] 'Rootness' is now part of the element type.
  29. **                  'gcReqT' added.
  30. ** 10-Feb-94 [lars] actual
  31. **---------------------------------------------------------------------------
  32. *)
  33.  
  34. (* $StackChk- $OvflChk- *)
  35.  
  36. IMPORT
  37.   (* $IF Debug *) Debug, (* $END *)
  38.   SYSTEM;
  39.  
  40. (*-------------------------------------------------------------------------*)
  41.  
  42. TYPE
  43.   ElementP  *= POINTER TO Element;
  44.   FreeDataP *= POINTER TO FreeData;
  45.  
  46. TYPE
  47.  
  48.   (* Base structure of an collectable element
  49.   *)
  50.   Element *= RECORD
  51.     next     : ElementP;  (* next element; NIL for unchained elements *)
  52.     prev     : ElementP;  (* previous element; or GC pass# of freelisting *)
  53.     ecType  -: SHORTINT;  (* type of this element *)
  54.     ecFlags -: SHORTSET;  (* color and other flags *)
  55.   END;
  56.  
  57. CONST  (* Element.ecFlags *)
  58.   Black *= 0;  (* Color 'black', must be 1-White *)
  59.   White *= 1;  (* Color 'white', must be 1-Black *)
  60.   Grey  *= 2;  (* Color 'grey' *)
  61.   Dark  *= 5;  (* set for root elements which are already processed *)
  62.   Root  *= 6;  (* set for members of the root set *)
  63.   Base  *= 7;  (* Baseelement of a ring *)
  64.  
  65. TYPE
  66.  
  67.   (* A Freelist entry for one ecType
  68.   *)
  69.   FreeData *= STRUCT
  70.     list    -: ElementP;                 (* List of free elements *)
  71.     new     -: PROCEDURE () : ElementP;  (* Allocator procedure *)
  72.     root    -: BOOLEAN;                  (* Flag if these elements are 'Root' *)
  73.     max     -: LONGINT;                  (* Max number of elements to hold *)
  74.     nrList  -: LONGINT;                  (* Current number of held elements *)
  75.     nrReq   -: LONGINT;  (* Total count of requests for a new Element *)
  76.     nrRecyc -: LONGINT;  (* Count of Element requests served from the free list *)
  77.     nrFreed -: LONGINT;  (* Total count of Element freed *)
  78.   END;
  79.  
  80.   FreeListP *= POINTER TO ARRAY OF FreeData;
  81.  
  82. VAR
  83.   black -: INTEGER; (* Actual colors *)
  84.   white -: INTEGER;
  85.  
  86.   nrTypes -: SHORTINT;  (* Number of registered ecTypes *)
  87.  
  88.   freelist -: FreeListP;  (* All Freelists *)
  89.  
  90.     (* Rings of the normal element set
  91.     ** During GC:
  92.     **   black  : processed and referenced
  93.     **   grey   : referenced, but not processed yet
  94.     **   white  : unprocessed and/or unreferenced
  95.     **   garbage: empty
  96.     **
  97.     ** After GC:
  98.     **   black  : alive elements
  99.     **   grey   : empty
  100.     **   white  : dead elements
  101.     **   garbage: empty
  102.     **
  103.     ** During Sweeping:
  104.     **   black  : empty
  105.     **   grey   : empty
  106.     **   white  : alive elements from previous run, and new elements
  107.     **   garbage: elements left to deallocate
  108.     *)
  109.   nBlack   : ElementP;  (* ring of the black elements *)
  110.   nGrey    : ElementP;  (* ring of the grey elements *)
  111.   nWhite   : ElementP;  (* ring of the white element *)
  112.   nGarbage : ElementP;  (* list of the garbage elements *)
  113.   nrNormal : LONGINT;   (* Number of normal elements *)
  114.  
  115.     (* Pointers of the root element set
  116.     ** During GC:
  117.     **   black : processed and referenced
  118.     **   grey  : unprocessed yet, but referenced
  119.     **   dark  : processed, but unreferenced
  120.     **   white : unprocessed, mostly unreferenced
  121.     **
  122.     ** After GC:
  123.     **   black : alive elements
  124.     **   grey  : empty
  125.     **   dark  : possibly dead elements (the white ones)
  126.     **   white : dead elements
  127.     **
  128.     ** During Sweeping:
  129.     **  black  : empty
  130.     **  grey   : empty
  131.     **  dark   : possible dead elements left to sweep
  132.     **  white  : alive elements from previous run, and new elements.
  133.     *)
  134.   rBlack : ElementP;  (* ring of black root elements *)
  135.   rGrey  : ElementP;  (* ring of grey root elements *)
  136.   rDark  : ElementP;  (* ring of dark root elements *)
  137.   rWhite : ElementP;  (* ring of white root elements *)
  138.   nrRoot : LONGINT;   (* Number of root elements *)
  139.  
  140.     (* Used by the GC *)
  141.   sweeping -: BOOLEAN;  (* GC state: false: marking, true: sweeping *)
  142.   gcRuns   -: LONGINT;  (* Count of GC runs *)
  143.   gcCalls  -: LONGINT;  (* Calls to GC() during this run *)
  144.  
  145.     (* And for my curiosity... *)
  146.   gcReqT   -: LONGINT;  (* Total number of elements requested *)
  147.   gcFreeT  -: LONGINT;  (* Total number of elements freed by GC so far *)
  148.   gcExaT   -: LONGINT;  (* Total number of elements examinated by GC so far *)
  149.   gcCallsT -: LONGINT;  (* Total count of calls to GC() *)
  150.  
  151.     (* ..and to build those numbers... *)
  152.   gcFree -: LONGINT;  (* Actual number of elements freed this GC-run *)
  153.   gcExa  -: LONGINT;  (* Actual number of elements examinated this GC-run *)
  154.  
  155. (*=========================================================================
  156. **
  157. **                             E L E M E N T
  158. **
  159. **=========================================================================*)
  160.  
  161. (*-------------------------------------------------------------------------*)
  162. PROCEDURE (this : ElementP) mark *;
  163.  
  164. (* Mark all elements which are referenced by this element as grey.
  165. **
  166. ** Argument:
  167. **   this: the referencing element.
  168. **
  169. ** The element itself is marked by the collector.
  170. *)
  171.  
  172. BEGIN
  173.   (* Nothing to do as default. *)
  174. END mark;
  175.  
  176. (*-------------------------------------------------------------------------*)
  177. CONST (* Returnvalues of this.free() *)
  178.   keep    *= 0;
  179.   enlist  *= 1;
  180.   dispose *= 2;
  181.  
  182. PROCEDURE (this : ElementP) free * () : INTEGER;
  183.  
  184. (* Let an unreferenced element clean up itself.
  185. **
  186. ** Argument:
  187. **   this: the now unreferenced element.
  188. **
  189. ** Result:
  190. **   keep   : the element is still alive, keep it.
  191. **   enlist : put the element into its freelist.
  192. **   dispose: deallocate the element.
  193. **
  194. ** The result determines the action done by the collector.
  195. *)
  196.  
  197. BEGIN
  198.   RETURN enlist;
  199. END free;
  200.  
  201. (*=========================================================================*)
  202.  
  203. (*-------------------------------------------------------------------------*)
  204. PROCEDURE ResizeFreelist (count : SHORTINT);
  205.  
  206. (* (Re)Allocate 'count' entries in 'freelist'.
  207. **
  208. ** Argument:
  209. **   count : the new size of the freelist.
  210. **
  211. ** Existing entries are copied; it is not possible to shrink the freelist.
  212. *)
  213.  
  214. VAR
  215.   oldCount : SHORTINT;
  216.   i        : SHORTINT;
  217.   newlist  : FreeListP;
  218. BEGIN
  219.   IF (freelist # NIL) & (count <= LEN(freelist^)) THEN RETURN; END;
  220.   IF freelist # NIL THEN
  221.     oldCount := nrTypes;
  222.   ELSE
  223.     oldCount := 0;
  224.   END;
  225.   NEW(newlist, count);
  226.   FOR i := 0 TO oldCount-1 DO
  227.     newlist[i] := freelist[i];
  228.   END;
  229.   FOR i := oldCount TO count-1 DO
  230.     newlist[i].list := NIL;
  231.     newlist[i].new  := NIL;
  232.     newlist[i].root := FALSE;
  233.     newlist[i].max  := 0;
  234.     newlist[i].nrList := 0;
  235.     newlist[i].nrReq   := 0;
  236.     newlist[i].nrRecyc := 0;
  237.     newlist[i].nrFreed := 0;
  238.   END;
  239.   (* $IFNOT GarbageCollector *)
  240.     IF freelist # NIL THEN
  241.       DISPOSE(freelist);
  242.     END;
  243.   (* $END *)
  244.   freelist := newlist;
  245. END ResizeFreelist;
  246.  
  247. (*-------------------------------------------------------------------------*)
  248. PROCEDURE RegisterType * ( new    : PROCEDURE() : ElementP
  249.                          ; isRoot : BOOLEAN
  250.                          ; max    : LONGINT
  251.                          ) : SHORTINT;
  252.  
  253. (* Register a new collectable type.
  254. **
  255. ** Arguments:
  256. **   new    : the procedure to allocate a record of that type.
  257. **   isRoot : TRUE if these elements are to be members of the root set.
  258. **   max    : the max number of records to hold in the freelist.
  259. **
  260. ** Result:
  261. **   The ecType number for the registered type.
  262. *)
  263.  
  264. BEGIN
  265.   ResizeFreelist(nrTypes+1);
  266.   freelist[nrTypes].new := new;
  267.   freelist[nrTypes].max := max;
  268.   freelist[nrTypes].root := isRoot;
  269.   INC(nrTypes);
  270.   RETURN nrTypes-1;
  271. END RegisterType;
  272.  
  273. (*-------------------------------------------------------------------------*)
  274. PROCEDURE New * (type : SHORTINT) : ElementP;
  275.  
  276. (* Return an element of the given type.
  277. **
  278. ** Arguments:
  279. **   type   : the type number for the requested element.
  280. **
  281. ** Result:
  282. **   A pointer to the new element.
  283. **
  284. ** The element is allocated either from the freelist, or using the 'new'
  285. ** procedure. It is entangled into the collector lists, either in
  286. ** the normalset, or if .root is TRUE, in the rootset.
  287. *)
  288.  
  289. VAR
  290.   freep : FreeDataP;
  291.   rc : ElementP;
  292. BEGIN
  293.   freep := SYSTEM.VAL(FreeDataP, SYSTEM.ADR(freelist[type]));
  294.   INC(freep.nrReq);
  295.   INC(gcReqT);
  296.   IF freep.list # NIL THEN
  297.     rc := SYSTEM.VAL(ElementP, freep.list);
  298.     freep.list := freep.list.next;
  299.     rc.next := NIL;
  300.     DEC(freep.nrList);
  301.     INC(freep.nrRecyc);
  302.   ELSE
  303.     rc := freep.new();
  304.     IF rc # NIL THEN
  305.       rc.ecType := type;
  306.     END;
  307.   END;
  308.   IF rc # NIL THEN
  309.     (* === Entangle(rc, isRoot); === *)
  310.       rc.ecFlags := SHORTSET{white};
  311.       IF freep.root THEN
  312.         INCL(rc.ecFlags, Root);
  313.         rc.prev := rWhite.prev;
  314.         rc.next := rWhite;
  315.         INC(nrRoot);
  316.       ELSE
  317.         rc.prev := nWhite.prev;
  318.         rc.next := nWhite;
  319.         INC(nrNormal);
  320.       END;
  321.       rc.prev.next := rc;
  322.       rc.next.prev := rc;
  323.     (* ====== *)
  324.   END;
  325.   RETURN rc;
  326. END New;
  327.  
  328. (*-------------------------------------------------------------------------*)
  329. PROCEDURE Allocate * (type : SHORTINT) : ElementP;
  330.  
  331. (* Return an element of the given type.
  332. **
  333. ** Arguments:
  334. **   type   : the type number for the requested element.
  335. **
  336. ** Result:
  337. **   A pointer to the new element.
  338. **
  339. ** The element is allocated either from the freelist, or using the 'new'
  340. ** procedure. It is NOT entangled into the collector lists.
  341. ** Use it with care!
  342. *)
  343.  
  344. VAR
  345.   freep : FreeDataP;
  346.   rc : ElementP;
  347. BEGIN
  348.   freep := SYSTEM.VAL(FreeDataP, SYSTEM.ADR(freelist[type]));
  349.   INC(freep.nrReq);
  350.   IF freep.list # NIL THEN
  351.     rc := SYSTEM.VAL(ElementP, freep.list);
  352.     freep.list := freep.list.next;
  353.     rc.next := NIL;
  354.     DEC(freep.nrList);
  355.     INC(freep.nrRecyc);
  356.   ELSE
  357.     rc := freep.new();
  358.     IF rc # NIL THEN
  359.       rc.ecType := type;
  360.       rc.next := NIL;
  361.     END;
  362.   END;
  363.   RETURN rc;
  364. END Allocate;
  365.  
  366. (*-------------------------------------------------------------------------*)
  367. PROCEDURE Free * (elem : ElementP; dealloc : BOOLEAN);
  368.  
  369. (* Remove an element from the active ring.
  370. **
  371. ** Arguments:
  372. **   elem    : the element to remove.
  373. **   dealloc : flag if the element should be deallocated.
  374. **
  375. ** The element is removed from its ring (if any) and added to the freelist
  376. ** of its type, unless 'dealloc' is false, then it is deallocated.
  377. *)
  378.  
  379. VAR
  380.   freep : FreeDataP;
  381. BEGIN
  382.   IF elem.next # NIL THEN
  383.     IF Base IN elem.ecFlags THEN RETURN; END;
  384.     IF Root IN elem.ecFlags THEN DEC(nrRoot);
  385.                             ELSE DEC(nrNormal);
  386.     END;
  387.   (* === Detangle(elem) === *)
  388.     elem.next.prev := elem.prev;
  389.     elem.prev.next := elem.next;
  390.     elem.next := NIL;
  391.   (* ====== *)
  392.   END;
  393.   freep := SYSTEM.VAL(FreeDataP, SYSTEM.ADR(freelist[elem.ecType]));
  394.   INC(freep.nrFreed);
  395.   IF dealloc OR (freep.nrList >= freep.max) THEN
  396.     (* $IFNOT GarbageCollector *)
  397.       DISPOSE(elem);
  398.     (* $ELSE *)
  399.       elem := NIL;
  400.     (* $END *)
  401.   ELSE
  402.     elem.next := freep.list;
  403.     freep.list := elem;
  404.     elem.prev := SYSTEM.VAL(ElementP, gcRuns);
  405.     INC(freep.nrList);
  406.   END;
  407. END Free;
  408.  
  409. (*-------------------------------------------------------------------------*)
  410. PROCEDURE ShortenLists * (age : LONGINT);
  411.  
  412. (* Remove old elements from the freelists.
  413. **
  414. ** Argument:
  415. **   age : the age in GC runs an element needs to stay in the lists.
  416. **
  417. ** Elements older than 'age' GC runs are deallocated; with an 'age' of 0
  418. ** causing the deallocation of all elements.
  419. *)
  420.  
  421. VAR
  422.   freep      : FreeDataP;
  423.   i          : SHORTINT;
  424.   count      : LONGINT;
  425.   this, prev : ElementP;
  426. BEGIN
  427.   age := gcRuns-age;  (* get min gc-run# *)
  428.   FOR i := 0 TO nrTypes-1 DO
  429.     IF freelist[i].list # NIL THEN
  430.       freep := SYSTEM.VAL(FreeDataP, SYSTEM.ADR(freelist[i]));
  431.       prev := NIL;
  432.       this := freep.list;
  433.       WHILE SYSTEM.VAL(LONGINT, this.prev) > age DO
  434.         prev := this;
  435.         this := this.next;
  436.       END;
  437.       IF this # NIL THEN
  438.         count := freep.nrList;
  439.         IF prev = NIL THEN freep.list := NIL;
  440.                       ELSE prev.next := NIL;
  441.         END;
  442.         REPEAT
  443.           prev := this; this := this.next;
  444.           (* $IFNOT GarbageCollector *)
  445.             DISPOSE(prev);
  446.           (* $ELSE *)
  447.             prev := NIL;
  448.           (* $END *)
  449.           DEC(count);
  450.         UNTIL this = NIL;
  451.         freep.nrList := count;
  452.       END;
  453.     END;
  454.   END;
  455. END ShortenLists;
  456.  
  457. (*-------------------------------------------------------------------------*)
  458. PROCEDURE Mark * (this : ElementP);
  459.  
  460. (* Mark an element as grey.
  461. **
  462. ** Argument:
  463. **   this : the element to shade grey.
  464. **
  465. ** The element is colored grey (meaning "referenced but unprocessed")
  466. ** and enqueued into the 'grey' ring of its set.
  467. ** Non-'white' elements are not recolored or requeued.
  468. ** Processed 'dark' white rootset elements are just recolored.
  469. **
  470. ** This function is to be called by the .mark()-methods during GC,
  471. ** and by Check() (in fact, it is inlined by Check()).
  472. *)
  473.  
  474. BEGIN
  475.   IF     (this = NIL)
  476.       OR (Base IN this.ecFlags)
  477.       OR (SHORTSET{black, Grey} * this.ecFlags # SHORTSET{})
  478.   THEN
  479.     RETURN
  480.   END;
  481.   EXCL(this.ecFlags, white);
  482.   INCL(this.ecFlags, Grey);
  483.   IF SHORTSET{Root, Dark} * this.ecFlags # SHORTSET{Root, Dark} THEN
  484.     (* === Detangle(this) === *)
  485.       IF this.next # NIL THEN
  486.         this.next.prev := this.prev;
  487.         this.prev.next := this.next;
  488.         this.next := NIL;
  489.       END;
  490.     (* ====== *)
  491.     IF Root IN this.ecFlags THEN
  492.       this.next := rGrey;
  493.       this.prev := rGrey.prev;
  494.     ELSE
  495.       this.next := nGrey;
  496.       this.prev := nGrey.prev;
  497.     END;
  498.     this.next.prev := this;
  499.     this.prev.next := this;
  500.   END;
  501. END Mark;
  502.  
  503. (*-------------------------------------------------------------------------*)
  504. PROCEDURE Check * (this, elem : ElementP);
  505.  
  506. (* Check and possibly recolor an element after referencing.
  507. **
  508. ** Argument:
  509. **   this : the element referencing 'elem'.
  510. **   elem : the element to check.
  511. **
  512. ** The element is colored grey (meaning "referenced but unprocessed")
  513. ** and enqueued into the 'grey' ring of its set if it is referenced
  514. ** by a non-white element 'this'.
  515. ** Non-'white' elements are not recolored or requeued.
  516. ** Processed 'dark' white rootset elements are just recolored.
  517. **
  518. ** This function is to be called if 'elem' was just referenced
  519. ** by 'this' element.
  520. *)
  521.  
  522. BEGIN
  523.   IF     (this = NIL)
  524.       OR (Base IN this.ecFlags)
  525.       OR (SHORTSET{Root, black, Grey} * this.ecFlags = SHORTSET{})
  526.       OR (elem = NIL)
  527.       OR (Base IN elem.ecFlags)
  528.       OR (SHORTSET{black, Grey} * elem.ecFlags # SHORTSET{})
  529.   THEN
  530.     RETURN
  531.   END;
  532.   (* === Mark(elem) === *)
  533.     EXCL(elem.ecFlags, white);
  534.     INCL(elem.ecFlags, Grey);
  535.     IF SHORTSET{Root, Dark} * elem.ecFlags # SHORTSET{Root, Dark} THEN
  536.       (* === Detangle(elem) === *)
  537.         IF elem.next # NIL THEN
  538.           elem.next.prev := elem.prev;
  539.           elem.prev.next := elem.next;
  540.           elem.next := NIL;
  541.         END;
  542.       (* ====== *)
  543.       IF Root IN elem.ecFlags THEN
  544.         elem.next := rGrey;
  545.         elem.prev := rGrey.prev;
  546.       ELSE
  547.         elem.next := nGrey;
  548.         elem.prev := nGrey.prev;
  549.       END;
  550.       elem.next.prev := elem;
  551.       elem.prev.next := elem;
  552.     END;
  553.   (* ====== *)
  554. END Check;
  555.  
  556. (*-------------------------------------------------------------------------*)
  557. PROCEDURE GC * (steps : LONGINT) : BOOLEAN;
  558.  
  559. (* Perform a garbage collection - one step of a full run.
  560. **
  561. ** Argument:
  562. **   steps : total number of steps (calls) the GC shall need.
  563. **           A value < 2 enforces a completing/full run.
  564. **
  565. ** Result:
  566. **   TRUE if the GC is complete.
  567. **
  568. ** Perform one step (or a completing/complete run) of the Mark-and-Sweep-GC
  569. ** for the elements.
  570. ** The procedure loops either over n elements or until the end of one run.
  571. ** 'n' is derived from the number of existing elements divided by the
  572. ** given number of 'steps' to do.
  573. ** If the GC is complete, a call to ShortenLists() should be done.
  574. **
  575. ** Note that a 'complete' GC does not necessarily mean that all unreferenced
  576. ** memory has been returned already IF it was just completing a previous GC.
  577. **
  578. ** It is not guaranteed that a GC really needs just 'steps' calls to complete.
  579. *)
  580.  
  581. VAR
  582.   todo    : LONGINT;
  583.   elem    : ElementP;
  584.   elem2   : ElementP;
  585.   rc      : INTEGER;
  586.   fullRun : BOOLEAN;
  587. BEGIN
  588.   INC(gcCalls);
  589.  
  590.     (* In case of 'fullRun's, the exact number of elements to do
  591.     ** can't be predicted, so just loop until done.
  592.     *)
  593.  
  594.   IF (steps < 2) THEN
  595.     todo := 1;
  596.     fullRun := TRUE;
  597.   ELSE
  598.     todo := (nrRoot+nrNormal) DIV steps + 1;
  599.     fullRun := FALSE;
  600.   END;
  601.  
  602.   (*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  603.   ** Mark all referenced elements
  604.   *)
  605.  
  606.   IF ~sweeping THEN
  607.  
  608.     (* ------ Walk through the unprocessed grey rootset elements ------ *)
  609.  
  610.     IF rGrey.next # rGrey THEN
  611.  
  612.       elem := rGrey.next;
  613.  
  614.       WHILE (elem # rGrey) & (todo > 0) DO
  615.         INC(gcExa);
  616.         elem.mark;
  617.         EXCL(elem.ecFlags, Grey);
  618.         INCL(elem.ecFlags, black);
  619.         elem := elem.next;
  620.         IF ~fullRun THEN DEC (todo); END;
  621.       END;
  622.  
  623.       elem.prev.next   := rBlack;
  624.       rBlack.prev.next := rGrey.next;
  625.       rGrey.next.prev  := rBlack.prev;
  626.       rBlack.prev      := elem.prev;
  627.  
  628.       rGrey.next := elem;
  629.       elem.prev := rGrey;
  630.  
  631.       IF todo < 1 THEN RETURN FALSE; END;
  632.     END;
  633.  
  634.     (* ------ Walk through the unprocessed white rootset elements ------ *)
  635.  
  636.     IF rWhite.next # rWhite THEN
  637.  
  638.       elem := rWhite.next;
  639.  
  640.       WHILE (elem # rWhite) & (todo > 0) DO
  641.         INC (gcExa);
  642.         elem.mark;
  643.         IF Grey IN elem.ecFlags THEN
  644.           EXCL(elem.ecFlags, Grey);
  645.           INCL(elem.ecFlags, black);
  646.           elem := elem.next;
  647.         ELSE
  648.             (* Mark and requeue as Dark *)
  649.           elem2 := elem.next;
  650.           INCL(elem.ecFlags, Dark);
  651.           elem.next.prev := elem.prev;
  652.           elem.prev.next := elem.next;
  653.           rDark.prev.next := elem;
  654.           elem.prev := rDark.prev;
  655.           elem.next := rDark;
  656.           rDark.prev := elem;
  657.           elem := elem2;
  658.         END;
  659.  
  660.         IF ~fullRun THEN DEC (todo); END;
  661.       END;
  662.  
  663.       (* ...now all elements of white might have gone into the dark ring.
  664.       ** If not, put those up to 'elem' into the black ring.
  665.       *)
  666.  
  667.       IF (rWhite.next # rWhite) & (elem.prev # rWhite) THEN
  668.         elem.prev.next   := rBlack;
  669.         rBlack.prev.next := rWhite.next;
  670.         rWhite.next.prev := rBlack.prev;
  671.         rBlack.prev      := elem.prev;
  672.  
  673.         rWhite.next := elem;
  674.         elem.prev := rWhite;
  675.       END;
  676.  
  677.       IF todo < 1 THEN RETURN FALSE; END;
  678.     END;
  679.  
  680.     (* ------ Walk through the unprocessed grey normal elements ------ *)
  681.  
  682.     IF nGrey.next # nGrey THEN
  683.  
  684.       elem := nGrey.next;
  685.  
  686.       WHILE (elem # nGrey) & (todo > 0) DO
  687.         INC (gcExa);
  688.         elem.mark;
  689.         EXCL(elem.ecFlags, Grey);
  690.         INCL(elem.ecFlags, black);
  691.         elem := elem.next;
  692.         IF ~fullRun THEN DEC (todo); END;
  693.       END;
  694.  
  695.       elem.prev.next   := nBlack;
  696.       nBlack.prev.next := nGrey.next;
  697.       nGrey.next.prev  := nBlack.prev;
  698.       nBlack.prev      := elem.prev;
  699.  
  700.       nGrey.next := elem;
  701.       elem.prev := nGrey;
  702.  
  703.       IF todo < 1 THEN RETURN FALSE; END;
  704.     END;
  705.  
  706.     (* ------ Mark done, now setup for sweeping. ------ *)
  707.  
  708.     sweeping := TRUE;
  709.     white := black;
  710.     black := 1-white;
  711.  
  712.     (* Remove the dead elements from the normalset.
  713.     ** Requeue the alive elements into the 'white' ring.
  714.     *)
  715.     IF nWhite.next # nWhite THEN
  716.       nGarbage := nWhite.next;
  717.       nWhite.prev.next := NIL;  (* mark the end of the list *)
  718.     ELSE
  719.       nGarbage := NIL;
  720.     END;
  721.     nWhite.next := nWhite;
  722.     nWhite.prev := nWhite;
  723.  
  724.     elem   := nWhite;
  725.     nWhite := nBlack;
  726.     nBlack := elem;
  727.  
  728.     (* Requeue the alive rootset elements into the 'white' ring.
  729.     ** The possibly dead ones are alread in the 'dark' ring.
  730.     *)
  731.     elem   := rWhite;
  732.     rWhite := rBlack;
  733.     rBlack := elem;
  734.  
  735.   END; (* IF ~sweeping *)
  736.  
  737.   (*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  738.   ** Sweep the unreferenced objects.
  739.   *)
  740.  
  741.   (* ------ Try to free all unreferenced objects from normal set ------ *)
  742.  
  743.   WHILE nGarbage # NIL DO
  744.     elem := nGarbage;
  745.     nGarbage := nGarbage.next;
  746.     elem.next := NIL; (* !! *)
  747.     rc := elem.free();
  748.     IF rc = keep THEN
  749.       elem.ecFlags := SHORTSET{white};
  750.       elem.prev := nWhite.prev;
  751.       elem.next := nWhite;
  752.       elem.prev.next := elem;
  753.       elem.next.prev := elem;
  754.     ELSE
  755.       IF Root IN elem.ecFlags THEN DEC(nrRoot);
  756.                               ELSE DEC(nrNormal);
  757.       END;
  758.       Free(elem, rc = dispose);
  759.       INC (gcFree);
  760.     END;
  761.  
  762.     IF ~fullRun THEN
  763.       DEC (todo);
  764.       IF todo < 1 THEN RETURN FALSE; END;
  765.     END;
  766.   END;
  767.  
  768.   (* ------ Try to free all unreferenced objects from root set ------ *)
  769.  
  770.   IF rDark.next # rDark THEN
  771.  
  772.     elem := rDark.next;
  773.  
  774.     WHILE (elem # rDark) & (todo > 0) DO
  775.       (* Dark elements may be marked 'grey' after their processing
  776.       ** so take care for them.
  777.       *)
  778.       IF Grey IN elem.ecFlags THEN
  779.         rc := keep;
  780.       ELSE
  781.         rc := elem.free();
  782.       END;
  783.  
  784.       IF rc = keep THEN
  785.         elem.ecFlags := SHORTSET{Root, white};
  786.         elem := elem.next;
  787.       ELSE
  788.         elem2 := elem.next;
  789.         Free(elem, rc = dispose);
  790.         INC (gcFree);
  791.         elem := elem2;
  792.       END;
  793.  
  794.       IF ~fullRun THEN DEC (todo); END;
  795.     END;
  796.  
  797.     (* ...now all elements of dark might have been freed.
  798.     ** If not, put those up to 'elem' back into the white ring.
  799.     *)
  800.  
  801.     IF (rDark.next # rDark) & (elem.prev # rDark) THEN
  802.       elem.prev.next   := rWhite;
  803.       rWhite.prev.next := rDark.next;
  804.       rDark.next.prev  := rWhite.prev;
  805.       rWhite.prev      := elem.prev;
  806.  
  807.       rDark.next := elem;
  808.       elem.prev := rDark;
  809.     END;
  810.  
  811.     IF todo < 1 THEN RETURN FALSE; END;
  812.   END;
  813.  
  814.   (*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  815.   ** Prepare for a new run of the GC
  816.   *)
  817.  
  818.   INC(gcRuns);
  819.   INC(gcCallsT, gcCalls); gcCalls := 0;
  820.   INC(gcFreeT, gcFree); gcFree := 0;
  821.   INC(gcExaT , gcExa ); gcExa := 0;
  822.  
  823.   sweeping := FALSE;
  824.  
  825.  
  826.   (* If we come here, we are done in any case *)
  827.  
  828.   RETURN TRUE;
  829. END GC;
  830.  
  831. (*=========================================================================*)
  832.  
  833. BEGIN
  834.   nrTypes := 0;
  835.   black := Black; white := White;
  836.  
  837.   freelist := NIL;
  838.   ResizeFreelist(20);  (* Reserve an initial amount of entries *)
  839.  
  840.     (* Init rootset
  841.     *)
  842.   NEW(rWhite);
  843.   rWhite.next := rWhite; rWhite.prev := rWhite;
  844.   rWhite.ecFlags := SHORTSET{Root, Base};
  845.   NEW(rDark);
  846.   rDark.next := rDark; rDark.prev := rDark;
  847.   rDark.ecFlags := SHORTSET{Root, Base};
  848.   NEW(rGrey);
  849.   rGrey.next := rGrey; rGrey.prev := rGrey;
  850.   rGrey.ecFlags := SHORTSET{Root, Base};
  851.   NEW(rBlack);
  852.   rBlack.next := rBlack; rBlack.prev := rBlack;
  853.   rBlack.ecFlags := SHORTSET{Root, Base};
  854.  
  855.   nrRoot := 0;
  856.  
  857.     (* Init normalset
  858.     *)
  859.   NEW(nBlack);
  860.   nBlack.next := nBlack; nBlack.prev := nBlack;
  861.   nBlack.ecFlags := SHORTSET{Base};
  862.   NEW(nGrey);
  863.   nGrey.next := nGrey; nGrey.prev := nGrey;
  864.   nGrey.ecFlags := SHORTSET{Base};
  865.   NEW(nWhite);
  866.   nWhite.next := nWhite; nWhite.prev := nWhite;
  867.   nWhite.ecFlags := SHORTSET{Base};
  868.  
  869.   nrNormal := 0;
  870.  
  871.   sweeping := FALSE;
  872.  
  873.   gcRuns := 0;
  874.   gcReqT := 0;
  875.   gcCallsT := 0; gcCalls := 0;
  876.   gcFreeT  := 0; gcExaT  := 0;
  877.   gcFree   := 0; gcExa   := 0;
  878. END Collector.
  879.  
  880. (***************************************************************************)
  881.