From 32d4f304db365599a54b3eb30377ac6e20846749 Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Tue, 24 Aug 2004 17:12:06 -0300 Subject: [PATCH] first implementation of generational GC --- lgc.c | 257 +++++++++++++++++++++++++++--------------------------- lgc.h | 17 ++-- lstate.c | 10 ++- lstate.h | 7 +- lstring.c | 11 +-- lstring.h | 3 +- ltests.c | 16 ++-- 7 files changed, 162 insertions(+), 159 deletions(-) diff --git a/lgc.c b/lgc.c index 86f4e637..561c6350 100644 --- a/lgc.c +++ b/lgc.c @@ -1,5 +1,5 @@ /* -** $Id: lgc.c,v 2.7 2004/04/30 20:13:38 roberto Exp roberto $ +** $Id: lgc.c,v 2.8 2004/08/10 19:17:23 roberto Exp roberto $ ** Garbage Collector ** See Copyright Notice in lua.h */ @@ -25,10 +25,11 @@ #define GCSTEPSIZE (40*sizeof(TValue)) #define STEPMUL 2 -#define GCFREECOST (sizeof(TValue)/10) -#define GCSWEEPCOST (sizeof(TValue)/20) +#define GCSWEEPMAX 40 +#define GCSWEEPCOST 1 #define GCFINALIZECOST (10*sizeof(TValue)) -#define WAITNEXTCYCLE (10 * GCSTEPSIZE) +#define WAITNEXTCYCLE (40 * GCSTEPSIZE) +#define WAITNEXTCYCLEGN (200 * GCSTEPSIZE) #define FIXEDMASK bitmask(FIXEDBIT) @@ -269,60 +270,61 @@ static void traversestack (global_State *g, lua_State *l) { /* -** traverse a given `quantity' of gray objects, -** turning them to black. Returns extra `quantity' traversed. +** traverse one gray object, turning it to black. +** Returns `quantity' traversed. */ -static l_mem propagatemarks (global_State *g, l_mem lim) { - GCObject *o; - while ((o = g->gray) != NULL) { - lua_assert(isgray(o)); - gray2black(o); - switch (o->gch.tt) { - case LUA_TTABLE: { - Table *h = gco2h(o); - g->gray = h->gclist; - if (traversetable(g, h)) /* table is weak? */ - black2gray(o); /* keep it gray */ - lim -= sizeof(Table) + sizeof(TValue) * h->sizearray + - sizeof(Node) * sizenode(h); - break; - } - case LUA_TFUNCTION: { - Closure *cl = gco2cl(o); - g->gray = cl->c.gclist; - traverseclosure(g, cl); - lim -= (cl->c.isC) ? sizeCclosure(cl->c.nupvalues) : - sizeLclosure(cl->l.nupvalues); - break; - } - case LUA_TTHREAD: { - lua_State *th = gco2th(o); - g->gray = th->gclist; - th->gclist = g->grayagain; - g->grayagain = o; - black2gray(o); - traversestack(g, th); - lim -= sizeof(lua_State) + sizeof(TValue) * th->stacksize + - sizeof(CallInfo) * th->size_ci; - break; - } - case LUA_TPROTO: { - Proto *p = gco2p(o); - g->gray = p->gclist; - traverseproto(g, p); - lim -= sizeof(Proto) + sizeof(Instruction) * p->sizecode + - sizeof(Proto *) * p->sizep + - sizeof(TValue) * p->sizek + - sizeof(int) * p->sizelineinfo + - sizeof(LocVar) * p->sizelocvars + - sizeof(TString *) * p->sizeupvalues; - break; - } - default: lua_assert(0); +static l_mem propagatemark (global_State *g) { + GCObject *o = g->gray; + lua_assert(isgray(o)); + gray2black(o); + switch (o->gch.tt) { + case LUA_TTABLE: { + Table *h = gco2h(o); + g->gray = h->gclist; + if (traversetable(g, h)) /* table is weak? */ + black2gray(o); /* keep it gray */ + return sizeof(Table) + sizeof(TValue) * h->sizearray + + sizeof(Node) * sizenode(h); + break; } - if (lim <= 0) return lim; + case LUA_TFUNCTION: { + Closure *cl = gco2cl(o); + g->gray = cl->c.gclist; + traverseclosure(g, cl); + return (cl->c.isC) ? sizeCclosure(cl->c.nupvalues) : + sizeLclosure(cl->l.nupvalues); + break; + } + case LUA_TTHREAD: { + lua_State *th = gco2th(o); + g->gray = th->gclist; + th->gclist = g->grayagain; + g->grayagain = o; + black2gray(o); + traversestack(g, th); + return sizeof(lua_State) + sizeof(TValue) * th->stacksize + + sizeof(CallInfo) * th->size_ci; + break; + } + case LUA_TPROTO: { + Proto *p = gco2p(o); + g->gray = p->gclist; + traverseproto(g, p); + return sizeof(Proto) + sizeof(Instruction) * p->sizecode + + sizeof(Proto *) * p->sizep + + sizeof(TValue) * p->sizek + + sizeof(int) * p->sizelineinfo + + sizeof(LocVar) * p->sizelocvars + + sizeof(TString *) * p->sizeupvalues; + break; + } + default: lua_assert(0); return 0; } - return lim; +} + + +static void propagateall (global_State *g) { + while (g->gray) propagatemark(g); } @@ -384,6 +386,7 @@ static void freeobj (lua_State *L, GCObject *o) { break; } case LUA_TSTRING: { + G(L)->strt.nuse--; luaM_free(L, o, sizestring(gco2ts(o)->len)); break; } @@ -396,59 +399,50 @@ static void freeobj (lua_State *L, GCObject *o) { } -static l_mem sweepwholelist (lua_State *L, GCObject **p, int keepfixed, - lu_int32 *count); + +#define sweepwholelist(L,p) sweeplist(L,p,LUA_MAXINT32) -static GCObject **sweeplist (lua_State *L, GCObject **p, int keepfixed, - l_mem *plim, lu_int32 *count) { +static GCObject **sweeplist (lua_State *L, GCObject **p, lu_int32 count) { GCObject *curr; global_State *g = G(L); - l_mem lim = *plim; - int deadmask = otherwhite(g); - if (keepfixed) deadmask |= FIXEDMASK; + int whitebit = otherwhite(g); + int deadmask = whitebit | FIXEDMASK; while ((curr = *p) != NULL) { - if (((curr->gch.marked ^ FIXEDMASK) & deadmask) != deadmask) { - makewhite(g, curr); + if ((curr->gch.marked ^ whitebit) & deadmask) { + lua_assert(!isdead(g, curr) || testbit(curr->gch.marked, FIXEDBIT)); + if (!G(L)->gcgenerational || isdead(g, curr)) + makewhite(g, curr); if (curr->gch.tt == LUA_TTHREAD) - lim -= sweepwholelist(L, &gco2th(curr)->openupval, keepfixed, count); + sweepwholelist(L, &gco2th(curr)->openupval); p = &curr->gch.next; - lim -= GCSWEEPCOST; } else { - lua_assert(iswhite(curr)); + lua_assert(isdead(g, curr)); *p = curr->gch.next; if (curr == g->rootgc) /* is the first element of the list? */ g->rootgc = curr->gch.next; /* adjust first */ freeobj(L, curr); - lim -= GCFREECOST; - if (count) (*count)--; } - if (lim <= 0) break; + if (count-- == 0) break; } - *plim = lim; return p; } -static l_mem sweepwholelist (lua_State *L, GCObject **p, int keepfixed, - lu_int32 *count) { - l_mem lim = MAXLMEM; - /* empty lists are quite common here, so avoid useless calls */ - if (*p) sweeplist(L, p, keepfixed, &lim, count); - return MAXLMEM - lim; +static void sweepstrings (lua_State *L) { + global_State *g = G(L); + sweepwholelist(L, &G(L)->strt.hash[g->sweepstrgc++]); } -static l_mem sweepstrings (lua_State *L, int keepfixed, l_mem lim) { - int i; - global_State *g = G(L); - for (i = g->sweepstrgc; i < g->strt.size; i++) { /* for each list */ - lim -= sweepwholelist(L, &G(L)->strt.hash[i], keepfixed, &g->strt.nuse); - if (lim <= 0) break; +static void freelist (lua_State *L, GCObject **p) { + while (*p) { + GCObject *curr = *p; + *p = (*p)->gch.next; + if (curr != obj2gco(L)) + freeobj(L, curr); } - g->sweepstrgc = i+1; - return lim; } @@ -497,19 +491,12 @@ void luaC_callGCTM (lua_State *L) { } -void luaC_sweepall (lua_State *L) { +void luaC_freeall (lua_State *L) { global_State *g = G(L); - l_mem dummy = MAXLMEM; - /* finish (occasional) current sweep */ - sweepstrings(L, 0, MAXLMEM); - sweeplist(L, &g->rootgc, 0, &dummy, NULL); - /* do a whole new sweep */ - markobject(g, g->mainthread); /* cannot collect main thread */ - g->currentwhite = otherwhite(g); - g->sweepgc = &g->rootgc; - g->sweepstrgc = 0; - sweepstrings(L, 0, MAXLMEM); - sweeplist(L, &g->rootgc, 0, &dummy, NULL); + int i; + freelist(L, &g->rootgc); + for (i = 0; i < g->strt.size; i++) /* free all string lists */ + freelist(L, &G(L)->strt.hash[i]); } @@ -553,64 +540,68 @@ static void atomic (lua_State *L) { g->weak = NULL; lua_assert(!iswhite(obj2gco(g->mainthread))); markobject(g, L); /* mark running thread */ - propagatemarks(g, MAXLMEM); + propagateall(g); /* remark gray again */ g->gray = g->grayagain; g->grayagain = NULL; - propagatemarks(g, MAXLMEM); + propagateall(g); luaC_separateudata(L, 0); /* separate userdata to be preserved */ marktmu(g); /* mark `preserved' userdata */ - propagatemarks(g, MAXLMEM); /* remark, to propagate `preserveness' */ + propagateall(g); /* remark, to propagate `preserveness' */ cleartable(g->weak); /* remove collected objects from weak tables */ /* flip current white */ g->currentwhite = otherwhite(g); + g->sweepstrgc = 0; + g->sweepgc = &g->rootgc; g->gcstate = GCSsweepstring; + if (g->gcgenerational++ > 20) g->gcgenerational = 0; } -static l_mem singlestep (lua_State *L, l_mem lim) { +static l_mem singlestep (lua_State *L) { global_State *g = G(L); + /*lua_checkmemory(L);*/ switch (g->gcstate) { case GCSpropagate: { if (g->gray) - lim = propagatemarks(g, lim); + return propagatemark(g); else { /* no more `gray' objects */ atomic(L); /* finish mark phase */ - lim = 0; + return 0; } - break; } case GCSsweepstring: { - lim = sweepstrings(L, 1, lim); - if (g->sweepstrgc >= g->strt.size) { /* nothing more to sweep? */ - g->sweepstrgc = 0; + sweepstrings(L); + if (g->sweepstrgc >= g->strt.size) /* nothing more to sweep? */ g->gcstate = GCSsweep; /* end sweep-string phase */ - } - break; + return GCSWEEPCOST; } case GCSsweep: { - g->sweepgc = sweeplist(L, g->sweepgc, 1, &lim, NULL); + g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX); if (*g->sweepgc == NULL) { /* nothing more to sweep? */ checkSizes(L); - g->sweepgc = &g->rootgc; g->gcstate = GCSfinalize; /* end sweep phase */ } - break; + return GCSWEEPCOST; } case GCSfinalize: { if (g->tmudata) { GCTM(L); - lim -= GCFINALIZECOST; + return GCFINALIZECOST; } else { /* no more `udata' to finalize */ - markroot(L); /* may restart collection */ - lim = 0; + if (g->gcgenerational) { + atomic(L); + return WAITNEXTCYCLEGN; + } + else { + markroot(L); /* may restart collection */ + return WAITNEXTCYCLE; + } } - break; } - default: lua_assert(0); + default: lua_assert(0); return 0; } - return lim; } @@ -618,11 +609,7 @@ void luaC_step (lua_State *L) { global_State *g = G(L); l_mem lim = (g->nblocks - (g->GCthreshold - GCSTEPSIZE)) * STEPMUL; do { - lim = singlestep(L, lim); - if (g->gcstate == GCSfinalize && g->tmudata == NULL) { - lim = -WAITNEXTCYCLE; - break; /* do not start new collection */ - } + lim -= singlestep(L); } while (lim > 0); g->GCthreshold = g->nblocks + GCSTEPSIZE - lim/STEPMUL; lua_assert((long)g->nblocks + (long)GCSTEPSIZE >= lim/STEPMUL); @@ -631,12 +618,26 @@ void luaC_step (lua_State *L) { void luaC_fullgc (lua_State *L) { global_State *g = G(L); + if (g->gcstate == GCSpropagate || g->gcgenerational) { + g->gcgenerational = 0; + /* reset sweep marks to sweep all elements (returning them to white) */ + g->sweepstrgc = 0; + g->sweepgc = &g->rootgc; + /* reset other collector lists */ + g->gray = NULL; + g->grayagain = NULL; + g->weak = NULL; + g->gcstate = GCSsweepstring; + } + /* finish any pending sweep phase */ while (g->gcstate != GCSfinalize) { - singlestep(L, MAXLMEM); + singlestep(L); } markroot(L); + lua_assert(!g->gcgenerational); while (g->gcstate != GCSfinalize) { - singlestep(L, MAXLMEM); + singlestep(L); + g->gcgenerational = 0; /* keep it in this mode */ } g->GCthreshold = g->nblocks + GCSTEPSIZE; luaC_callGCTM(L); /* call finalizers */ @@ -646,7 +647,7 @@ void luaC_fullgc (lua_State *L) { void luaC_barrierf (lua_State *L, GCObject *o, GCObject *v) { global_State *g = G(L); lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o)); - lua_assert(g->gcstate != GCSfinalize); + lua_assert(g->gcgenerational || g->gcstate != GCSfinalize); if (g->gcstate != GCSpropagate) /* sweeping phases? */ black2gray(o); /* just mark as gray to avoid other barriers */ else /* breaking invariant! */ @@ -657,7 +658,7 @@ void luaC_barrierf (lua_State *L, GCObject *o, GCObject *v) { void luaC_barrierback (lua_State *L, GCObject *o, GCObject *v) { global_State *g = G(L); lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o)); - lua_assert(g->gcstate != GCSfinalize); + lua_assert(g->gcgenerational || g->gcstate != GCSfinalize); black2gray(o); /* make table gray (again) */ gco2h(o)->gclist = g->grayagain; g->grayagain = o; @@ -679,7 +680,7 @@ void luaC_linkupval (lua_State *L, UpVal *uv) { o->gch.next = g->rootgc; /* link upvalue into `rootgc' list */ g->rootgc = o; if (isgray(o)) { - if (g->gcstate == GCSpropagate) { + if (g->gcstate == GCSpropagate || g->gcgenerational) { gray2black(o); /* closed upvalues need barrier */ luaC_barrier(L, uv, uv->v); } diff --git a/lgc.h b/lgc.h index 56d5af39..c1d102f9 100644 --- a/lgc.h +++ b/lgc.h @@ -1,5 +1,5 @@ /* -** $Id: lgc.h,v 2.5 2004/03/15 21:04:33 roberto Exp roberto $ +** $Id: lgc.h,v 2.6 2004/08/10 19:17:23 roberto Exp roberto $ ** Garbage Collector ** See Copyright Notice in lua.h */ @@ -39,12 +39,13 @@ /* ** Layout for bit use in `marked' field: -** bit 0 - object is gray -** bit 1 - object is black -** bit 2 - For userdata: is finalized; - for tables: has weak keys -** bit 3 - for tables: has weak values -** bit 4 - object is fixed (should not be collected) +** bit 0 - object is white (type 0) +** bit 1 - object is white (type 1) +** bit 2 - object is black +** bit 3 - for userdata: has been finalized +** bit 3 - for tables: has weak keys +** bit 4 - for tables: has weak values +** bit 5 - object is fixed (should not be collected) */ #define WHITE0BIT 0 @@ -86,7 +87,7 @@ size_t luaC_separateudata (lua_State *L, int all); void luaC_callGCTM (lua_State *L); -void luaC_sweepall (lua_State *L); +void luaC_freeall (lua_State *L); void luaC_step (lua_State *L); void luaC_fullgc (lua_State *L); void luaC_link (lua_State *L, GCObject *o, lu_byte tt); diff --git a/lstate.c b/lstate.c index 0c7ca889..11f57728 100644 --- a/lstate.c +++ b/lstate.c @@ -1,5 +1,5 @@ /* -** $Id: lstate.c,v 2.9 2004/06/17 14:06:52 roberto Exp roberto $ +** $Id: lstate.c,v 2.10 2004/06/17 14:25:31 roberto Exp roberto $ ** Global State ** See Copyright Notice in lua.h */ @@ -122,9 +122,10 @@ static void preinit_state (lua_State *L, global_State *g) { static void close_state (lua_State *L) { global_State *g = G(L); luaF_close(L, L->stack); /* close all upvalues for this thread */ - luaC_sweepall(L); /* collect all elements */ - lua_assert(g->rootgc == obj2gco(L)); - luaS_freeall(L); + luaC_freeall(L); /* collect all objects */ + lua_assert(g->rootgc == NULL); + lua_assert(g->strt.nuse == 0); + luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size, TString *); luaZ_freebuffer(L, &g->buff); freestack(L, L); lua_assert(g->nblocks == sizeof(LG)); @@ -177,6 +178,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { luaZ_initbuffer(L, &g->buff); g->panic = NULL; g->gcstate = GCSfinalize; + g->gcgenerational = 0; g->rootgc = obj2gco(L); g->sweepstrgc = 0; g->sweepgc = &g->rootgc; diff --git a/lstate.h b/lstate.h index bec73a5b..8c85b6f6 100644 --- a/lstate.h +++ b/lstate.h @@ -1,5 +1,5 @@ /* -** $Id: lstate.h,v 2.4 2004/05/31 18:51:50 roberto Exp roberto $ +** $Id: lstate.h,v 2.5 2004/06/02 19:07:55 roberto Exp roberto $ ** Global State ** See Copyright Notice in lua.h */ @@ -69,7 +69,9 @@ typedef struct global_State { stringtable strt; /* hash table for strings */ lua_Alloc realloc; /* function to reallocate memory */ void *ud; /* auxiliary data to `realloc' */ - int currentwhite; + lu_byte currentwhite; + lu_byte gcstate; /* state of garbage collector */ + lu_byte gcgenerational; GCObject *rootgc; /* list of all collectable objects */ GCObject *firstudata; /* udata go to the end of `rootgc' */ GCObject **sweepgc; /* position of sweep in `rootgc' */ @@ -78,7 +80,6 @@ typedef struct global_State { GCObject *grayagain; /* list of objects to be traversed atomically */ GCObject *weak; /* list of weak tables (to be cleared) */ GCObject *tmudata; /* list of userdata to be GC */ - int gcstate; /* state of garbage collector */ Mbuffer buff; /* temporary buffer for string concatentation */ lu_mem GCthreshold; lu_mem nblocks; /* number of `bytes' currently allocated */ diff --git a/lstring.c b/lstring.c index 8ecfb44e..13b75086 100644 --- a/lstring.c +++ b/lstring.c @@ -1,5 +1,5 @@ /* -** $Id: lstring.c,v 2.1 2003/12/10 12:13:36 roberto Exp roberto $ +** $Id: lstring.c,v 2.2 2004/04/30 20:13:38 roberto Exp roberto $ ** String table (keeps all strings handled by Lua) ** See Copyright Notice in lua.h */ @@ -19,17 +19,12 @@ -void luaS_freeall (lua_State *L) { - lua_assert(G(L)->strt.nuse==0); - luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size, TString *); -} - - void luaS_resize (lua_State *L, int newsize) { GCObject **newhash; stringtable *tb; int i; - if (G(L)->sweepstrgc > 0) return; /* cannot resize during GC traverse */ + if (G(L)->gcstate == GCSsweepstring) + return; /* cannot resize during GC traverse */ newhash = luaM_newvector(L, newsize, GCObject *); tb = &G(L)->strt; for (i=0; igcstate == GCSpropagate) + if (g->gcstate == GCSpropagate || g->gcgenerational) return !isblack(f) || !iswhite(t); - else if (g->gcstate == GCSfinalize) + else if (g->gcstate == GCSfinalize && !g->gcgenerational) return iswhite(f); else return 1; @@ -175,7 +175,8 @@ static void printobj (global_State *g, GCObject *o) { static int testobjref (global_State *g, GCObject *f, GCObject *t) { int r = testobjref1(g,f,t); if (!r) { - printf("%d(%02X) - ", g->gcstate, g->currentwhite); + printf("%d(%02X) %c - ", g->gcstate, g->currentwhite, + g->gcgenerational ? 'G' : ' '); printobj(g, f); printf("\t-> "); printobj(g, t); @@ -290,9 +291,12 @@ static void checkstack (global_State *g, lua_State *L1) { static void checkobject (global_State *g, GCObject *o) { if (isdead(g, o)) - lua_assert(g->gcstate == GCSsweepstring || g->gcstate == GCSsweep); +/* lua_assert(g->gcstate == GCSsweepstring || g->gcstate == GCSsweep);*/ +{ if (!(g->gcstate == GCSsweepstring || g->gcstate == GCSsweep)) +printf(">>> %d %s %02x\n", g->gcstate, luaT_typenames[o->gch.tt], o->gch.marked); +} else { - if (g->gcstate == GCSfinalize) + if (g->gcstate == GCSfinalize && !g->gcgenerational) lua_assert(iswhite(o)); switch (o->gch.tt) { case LUA_TUPVAL: {