585 lines
13 KiB
C
585 lines
13 KiB
C
/*************************************************************************
|
|
*
|
|
* lispGC.c --
|
|
*
|
|
* This module contains the garbage collector.
|
|
* (N.B. This is really inefficient!)
|
|
*
|
|
* (c) 1996 California Institute of Technology
|
|
* Department of Computer Science
|
|
* Pasadena, CA 91125.
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software
|
|
* and its documentation for any purpose and without fee is hereby
|
|
* granted, provided that the above copyright notice appear in all
|
|
* copies. The California Institute of Technology makes no representations
|
|
* about the suitability of this software for any purpose. It is
|
|
* provided "as is" without express or implied warranty. Export of this
|
|
* software outside of the United States of America may require an
|
|
* export license.
|
|
*
|
|
* $Header$
|
|
*
|
|
*************************************************************************/
|
|
|
|
#include <stdio.h>
|
|
#include <sys/time.h>
|
|
#include <sys/resource.h>
|
|
|
|
#include "lisp/lisp.h"
|
|
#include "lispInt.h"
|
|
#include "textio/textio.h"
|
|
#include "utils/malloc.h"
|
|
#include "lispargs.h"
|
|
|
|
/*
|
|
garbage collection trickery. :)
|
|
*/
|
|
#define GC_TO_PTR(type,x) ((type)(((unsigned long) x) & ~3))
|
|
|
|
#define GC_SWMARK(type,x,v) x=(type)((((unsigned long)x)&~3) | ((v)&3))
|
|
#define GC_SWMARKED(x,v) ((((unsigned long) x)&3) == (v))
|
|
#define GC_MARKVAL(x) (((unsigned long)x)&3)
|
|
|
|
#define GC_MARK(type,x) GC_SWMARK(type,x,1)
|
|
#define GC_UNMARK(type,x) GC_SWMARK(type,x,0)
|
|
#define GC_MARKED(x) GC_SWMARKED(x,1)
|
|
|
|
static Sexp *SexpMainAllocQ = NULL;
|
|
static Sexp *SexpMainAllocQTail = NULL;
|
|
|
|
static Sexp *SexpAllocQ = NULL;
|
|
static Sexp *SexpAllocQTail = NULL;
|
|
|
|
static Sexp *SexpFreeQ = NULL;
|
|
static Sexp *SexpFreeQTail = NULL;
|
|
|
|
static LispObj *LispObjMainAllocQ = NULL;
|
|
static LispObj *LispObjMainAllocQTail = NULL;
|
|
|
|
static LispObj *LispObjAllocQ = NULL;
|
|
static LispObj *LispObjAllocQTail = NULL;
|
|
|
|
static LispObj *LispObjFreeQ = NULL;
|
|
static LispObj *LispObjFreeQTail = NULL;
|
|
|
|
static Sexp *MarkedSexpQ;
|
|
static LispObj *MarkedObjQ;
|
|
|
|
int LispGCHasWork;
|
|
int LispCollectAllocQ;
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
*
|
|
* LispNewObj --
|
|
*
|
|
* Get a new object from the free list.
|
|
*
|
|
* Results:
|
|
* Returns the new object.
|
|
*
|
|
* Side effects:
|
|
* Modifies free list.
|
|
*
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
LispObj *
|
|
LispNewObj ()
|
|
{
|
|
LispObj *s;
|
|
if (LispObjFreeQ) {
|
|
s = LispObjFreeQ;
|
|
LispObjFreeQ = LispObjFreeQ->n;
|
|
if (LTYPE(s) == S_STRING)
|
|
freeMagic(LSTR(s));
|
|
}
|
|
else {
|
|
s = (LispObj *) mallocMagic((unsigned) (sizeof(LispObj)));
|
|
}
|
|
s->t = S_INT;
|
|
s->u.l = NULL;
|
|
if (!LispObjAllocQ)
|
|
LispObjAllocQTail = s;
|
|
s->n = LispObjAllocQ;
|
|
LispObjAllocQ = s;
|
|
return s;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
*
|
|
* LispCopyObj --
|
|
*
|
|
* Create a copy of an object.
|
|
*
|
|
* Results:
|
|
* Returns the new object.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
LispObj *
|
|
LispCopyObj (l)
|
|
LispObj *l;
|
|
{
|
|
LispObj *s;
|
|
s = LispNewObj ();
|
|
LTYPE(s) = LTYPE(l);
|
|
switch (LTYPE(s)) {
|
|
case S_LIST:
|
|
LLIST(s) = LLIST(l);
|
|
break;
|
|
case S_SYM:
|
|
LSYM(s) = LSYM(l);
|
|
break;
|
|
case S_MAGIC_BUILTIN:
|
|
LSYM(s) = LSYM(l);
|
|
break;
|
|
case S_LAMBDA_BUILTIN:
|
|
LBUILTIN(s) = LBUILTIN(l);
|
|
break;
|
|
case S_LAMBDA:
|
|
LUSERDEF(s) = LUSERDEF(l);
|
|
break;
|
|
case S_INT:
|
|
LINTEGER(s) = LINTEGER(l);
|
|
break;
|
|
case S_FLOAT:
|
|
LFLOAT(s) = LFLOAT(l);
|
|
break;
|
|
case S_STRING:
|
|
LSTR(s) = (char *) mallocMagic((unsigned) (strlen(LSTR(l))+1));
|
|
strcpy (LSTR(s),LSTR(l));
|
|
break;
|
|
case S_BOOL:
|
|
LBOOL(s) = LBOOL(l);
|
|
break;
|
|
default:
|
|
TxError ("Fatal error in copy-object!\n");
|
|
break;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
*
|
|
* LispNewSexp --
|
|
*
|
|
* Get a Sexp from the free list.
|
|
*
|
|
* Results:
|
|
* Returns the new Sexp.
|
|
*
|
|
* Side effects:
|
|
* Modifies the free list.
|
|
*
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
Sexp *
|
|
LispNewSexp ()
|
|
{
|
|
Sexp *s;
|
|
if (SexpFreeQ) {
|
|
s = SexpFreeQ;
|
|
SexpFreeQ = SexpFreeQ->n;
|
|
}
|
|
else {
|
|
s = (Sexp *) mallocMagic((unsigned) (sizeof(Sexp)));
|
|
}
|
|
CAR(s) = NULL;
|
|
CDR(s) = NULL;
|
|
if (!SexpAllocQ)
|
|
SexpAllocQTail = s;
|
|
s->n = SexpAllocQ;
|
|
SexpAllocQ = s;
|
|
return s;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
*
|
|
* LispCopySexp --
|
|
*
|
|
* Return a copy of an Sexp.
|
|
*
|
|
* Results:
|
|
* Returns the new Sexp.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
Sexp *
|
|
LispCopySexp (s)
|
|
Sexp *s;
|
|
{
|
|
Sexp *t;
|
|
t = LispNewSexp ();
|
|
CAR(t) = CAR(s);
|
|
CDR(t) = CDR(s);
|
|
return t;
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
|
|
/*
|
|
*
|
|
*
|
|
* The garbage collector is rather interesting. It has two modes of
|
|
* operation. When evaluation has not executed any "define", "set!",
|
|
* "set-car!", or "set-cdr!" commands (the only ones which have side-effects),
|
|
* everything allocated during the last evaluation is collected without
|
|
* running a marking algorithm. As a result, normal magic commands will
|
|
* be executed with O(1) garbage collection overhead. Note that strings
|
|
* are free'd when the object is reused.
|
|
*
|
|
* Scheme functions without side-effects will also execute with O(1)
|
|
* garbage collection overhead.
|
|
*
|
|
* The full mark/collect algorithm is executed once every 50 times the
|
|
* real garbage collector is called :P This "constant-factor"
|
|
* speedup actually makes a big difference in practice.
|
|
*
|
|
* The marking algorithm is O(NUSE), where NUSE = # of used nodes. We use
|
|
* the Schorr-Waite-Deutch algorithm for a non-recursive traversal of the
|
|
* nodes in use.
|
|
*
|
|
* The collection phase is O(NALLOC), where NALLOC = # of allocated nodes.
|
|
* It could be reduced to O(NUSE) by changing the allocation list to one
|
|
* that is doubly-linked.
|
|
*
|
|
*
|
|
*/
|
|
|
|
|
|
/*
|
|
*
|
|
* Merge the main and current alloc q into the current alloc q.
|
|
*
|
|
*/
|
|
static
|
|
void
|
|
mergealloc ()
|
|
{
|
|
Sexp *s;
|
|
LispObj *l;
|
|
|
|
if (!SexpAllocQ) {
|
|
SexpAllocQ = SexpMainAllocQ;
|
|
SexpAllocQTail = SexpMainAllocQTail;
|
|
}
|
|
else {
|
|
SexpAllocQTail->n = SexpMainAllocQ;
|
|
if (SexpMainAllocQ)
|
|
SexpAllocQTail = SexpMainAllocQTail;
|
|
}
|
|
if (!LispObjAllocQ) {
|
|
LispObjAllocQ = LispObjMainAllocQ;
|
|
LispObjAllocQTail = LispObjMainAllocQTail;
|
|
}
|
|
else {
|
|
LispObjAllocQTail->n = LispObjMainAllocQ;
|
|
if (LispObjMainAllocQ)
|
|
LispObjAllocQTail = LispObjMainAllocQTail;
|
|
}
|
|
}
|
|
|
|
|
|
#define NIL(x) ((LTYPE(x) != S_LIST && LTYPE(x) != S_LAMBDA) || (LLIST(x) == NULL))
|
|
|
|
static
|
|
void
|
|
mark_sw (l)
|
|
LispObj *l;
|
|
{
|
|
LispObj *m;
|
|
LispObj *t0,*t1,*t2,*t3;
|
|
int mark;
|
|
|
|
/* mark all the nodes */
|
|
m = NULL;
|
|
while (l != NULL) {
|
|
GC_SWMARK(LispObj *, l->n, 1);
|
|
GC_SWMARK(Sexp *, LLIST(l)->n, GC_MARKVAL (LLIST(l)->n)+1);
|
|
if (GC_MARKVAL(LLIST(l)->n) == 3 ||
|
|
(!NIL(CAR(LLIST(l))) && GC_MARKVAL(LLIST(CAR(LLIST(l)))->n) == 0)) {
|
|
t0=l; t1=CAR(LLIST(l)); t2=CDR(LLIST(l)); t3=m;
|
|
CAR(LLIST(l))=t2; CDR(LLIST(l))=t3; m=t0; l=t1;
|
|
}
|
|
else {
|
|
GC_SWMARK (LispObj *, CAR(LLIST(l))->n, 1);
|
|
t0=CAR(LLIST(l)); t1=CDR(LLIST(l)); t2=m;
|
|
CAR(LLIST(l))=t1; CDR(LLIST(l))=t2; m=t0;
|
|
}
|
|
}
|
|
}
|
|
|
|
static
|
|
void
|
|
collect_sw ()
|
|
{
|
|
/* all marked objects have non-zero mark */
|
|
/* stoll through the alloc Q and split it into two things:
|
|
freeQ, mainallocQ
|
|
*/
|
|
SexpMainAllocQ = NULL;
|
|
while (SexpAllocQ) {
|
|
if (GC_MARKVAL (SexpAllocQ->n) != 0) {
|
|
/* used */
|
|
if (!SexpMainAllocQ) {
|
|
SexpMainAllocQ = SexpAllocQ;
|
|
SexpMainAllocQTail = SexpAllocQ;
|
|
}
|
|
else {
|
|
SexpMainAllocQTail->n = SexpAllocQ;
|
|
SexpMainAllocQTail = SexpAllocQ;
|
|
}
|
|
}
|
|
else {
|
|
if (!SexpFreeQ) {
|
|
SexpFreeQ = SexpAllocQ;
|
|
SexpFreeQTail = SexpAllocQ;
|
|
}
|
|
else {
|
|
SexpFreeQTail->n = SexpAllocQ;
|
|
SexpFreeQTail = SexpAllocQ;
|
|
}
|
|
}
|
|
SexpAllocQ = GC_TO_PTR(Sexp*, SexpAllocQ->n);
|
|
}
|
|
if (SexpFreeQ)
|
|
SexpFreeQTail->n = NULL;
|
|
if (SexpMainAllocQ)
|
|
SexpMainAllocQTail->n = NULL;
|
|
SexpAllocQ = NULL;
|
|
|
|
LispObjMainAllocQ = NULL;
|
|
while (LispObjAllocQ) {
|
|
if (GC_MARKVAL (LispObjAllocQ->n) != 0) {
|
|
/* used */
|
|
if (!LispObjMainAllocQ) {
|
|
LispObjMainAllocQ = LispObjAllocQ;
|
|
LispObjMainAllocQTail = LispObjAllocQ;
|
|
}
|
|
else {
|
|
LispObjMainAllocQTail->n = LispObjAllocQ;
|
|
LispObjMainAllocQTail = LispObjAllocQ;
|
|
}
|
|
}
|
|
else {
|
|
if (LTYPE(LispObjAllocQ) == S_STRING) {
|
|
freeMagic(LSTR(LispObjAllocQ));
|
|
LTYPE(LispObjAllocQ) = S_INT;
|
|
}
|
|
if (!LispObjFreeQ) {
|
|
LispObjFreeQ = LispObjAllocQ;
|
|
LispObjFreeQTail = LispObjAllocQ;
|
|
}
|
|
else {
|
|
LispObjFreeQTail->n = LispObjAllocQ;
|
|
LispObjFreeQTail = LispObjAllocQ;
|
|
}
|
|
}
|
|
LispObjAllocQ = GC_TO_PTR(LispObj*, LispObjAllocQ->n);
|
|
}
|
|
if (LispObjFreeQ)
|
|
LispObjFreeQTail->n = NULL;
|
|
if (LispObjMainAllocQ)
|
|
LispObjMainAllocQTail->n = NULL;
|
|
LispObjAllocQ = NULL;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
*
|
|
* LispGC --
|
|
*
|
|
* Run the garbage collector, assuming all reachable nodes are reachable
|
|
* from the Sexp passed to the garbage collector.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
static int skip_gc = 50; /* make this bigger if it is too slow :) */
|
|
|
|
void
|
|
LispGC (fl)
|
|
LispObj *fl;
|
|
{
|
|
LispObj *l;
|
|
|
|
if (LispCollectAllocQ) {
|
|
/*
|
|
* The last evaluation did not have any side-effects.
|
|
* Collect everything allocated on the last pass and put it
|
|
* back into the free list.
|
|
*
|
|
*/
|
|
if (LispObjFreeQ) {
|
|
LispObjFreeQTail->n = LispObjAllocQ;
|
|
if (LispObjAllocQ)
|
|
LispObjFreeQTail = LispObjAllocQTail;
|
|
}
|
|
else {
|
|
LispObjFreeQ = LispObjAllocQ;
|
|
LispObjFreeQTail = LispObjAllocQTail;
|
|
}
|
|
if (SexpFreeQ) {
|
|
SexpFreeQTail->n = SexpAllocQ;
|
|
if (SexpAllocQ)
|
|
SexpFreeQTail = SexpAllocQTail;
|
|
}
|
|
else {
|
|
SexpFreeQ = SexpAllocQ;
|
|
SexpFreeQTail = SexpAllocQTail;
|
|
}
|
|
LispObjAllocQ = NULL;
|
|
SexpAllocQ = NULL;
|
|
return;
|
|
}
|
|
if (skip_gc-- > 0) {
|
|
LispGCHasWork = 1;
|
|
mergealloc ();
|
|
SexpMainAllocQ = SexpAllocQ;
|
|
SexpMainAllocQTail = SexpAllocQTail;
|
|
LispObjMainAllocQ = LispObjAllocQ;
|
|
LispObjMainAllocQTail = LispObjAllocQTail;
|
|
SexpAllocQ = NULL;
|
|
LispObjAllocQ = NULL;
|
|
return;
|
|
}
|
|
skip_gc = 50;
|
|
|
|
if (fl) {
|
|
extern Sexp *LispMainFrame;
|
|
|
|
l = LispFrameLookup (LispNewString ("scm-gc-frequency"), LispMainFrame);
|
|
if (l && LTYPE(l) == S_INT && LINTEGER(l) >= 0)
|
|
skip_gc = LINTEGER(l);
|
|
}
|
|
|
|
if (fl) {
|
|
mergealloc ();
|
|
mark_sw(fl);
|
|
collect_sw ();
|
|
LispObjAllocQ = NULL;
|
|
}
|
|
LispGCHasWork = 0;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
*
|
|
* LispCollectGarbage --
|
|
*
|
|
* Force garbage collection after this evaluation.
|
|
*
|
|
* Results:
|
|
* Returns #t
|
|
*
|
|
* Side effects:
|
|
* none.
|
|
*
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
LispObj *
|
|
LispCollectGarbage (name,s,f)
|
|
char *name;
|
|
Sexp *s;
|
|
Sexp *f;
|
|
{
|
|
LispObj *l;
|
|
extern LispObj *LispMainFrameObj;
|
|
|
|
if (ARG1P(s)) {
|
|
TxPrintf ("Usage: (%s)\n", name);
|
|
RETURN;
|
|
}
|
|
skip_gc = 0;
|
|
LispCollectAllocQ = 0;
|
|
LispGC (LispMainFrameObj);
|
|
l = LispNewObj ();
|
|
LTYPE(l) = S_BOOL;
|
|
LBOOL(l) = 1;
|
|
return l;
|
|
}
|
|
|
|
|
|
|
|
/*------------------------------------------------------------------------
|
|
*
|
|
* LispGCAddSexp --
|
|
*
|
|
* Add an sexp to the list of roots used for garbage collection.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* Modifies LispMainFrameObj list.
|
|
*
|
|
*------------------------------------------------------------------------
|
|
*/
|
|
|
|
void LispGCAddSexp (s)
|
|
Sexp *s;
|
|
{
|
|
extern LispObj *LispMainFrameObj;
|
|
Sexp *t;
|
|
|
|
t = LispNewSexp ();
|
|
CAR(t) = LispNewObj ();
|
|
LTYPE(CAR(t)) = S_LIST;
|
|
LLIST(CAR(t)) = s;
|
|
CDR(t) = LispMainFrameObj;
|
|
|
|
LispMainFrameObj = LispNewObj ();
|
|
LTYPE(LispMainFrameObj) = S_LIST;
|
|
LLIST(LispMainFrameObj) = t;
|
|
}
|
|
|
|
|
|
|
|
/*------------------------------------------------------------------------
|
|
*
|
|
* LispGCRemoveSexp --
|
|
*
|
|
* Remove an Sexp from the list of roots for garbage collection.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* Modifies LispMainFrameObj list.
|
|
*
|
|
*------------------------------------------------------------------------
|
|
*/
|
|
|
|
void LispGCRemoveSexp (s)
|
|
Sexp *s;
|
|
{
|
|
extern LispObj *LispMainFrameObj;
|
|
LispObj *l;
|
|
|
|
if (LLIST(CAR(LLIST(LispMainFrameObj))) != s) {
|
|
TxError ("Fatal internal error. Proceed at your own risk!\n");
|
|
return;
|
|
}
|
|
|
|
LispMainFrameObj = CDR(LLIST(LispMainFrameObj));
|
|
}
|