From ce444bff336b333251ac3745f311bd9033be8f6a Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Wed, 18 Nov 2009 11:13:47 -0200 Subject: [PATCH] (huge) simplification of GC management --- lgc.c | 133 ++++++++++++++++++------------------------------------- lgc.h | 7 ++- lstate.c | 14 ++---- lstate.h | 6 +-- 4 files changed, 50 insertions(+), 110 deletions(-) diff --git a/lgc.c b/lgc.c index 91ed52dd..b706f37c 100644 --- a/lgc.c +++ b/lgc.c @@ -1,5 +1,5 @@ /* -** $Id: lgc.c,v 2.60 2009/11/06 17:06:19 roberto Exp roberto $ +** $Id: lgc.c,v 2.61 2009/11/09 18:29:21 roberto Exp roberto $ ** Garbage Collector ** See Copyright Notice in lua.h */ @@ -52,9 +52,6 @@ reallymarkobject(g, obj2gco(t)); } -#define setthreshold(g) (g->GCthreshold = (g->estimate/100) * g->gcpause) - - static void reallymarkobject (global_State *g, GCObject *o); @@ -94,6 +91,7 @@ static int iscleared (const TValue *o, int iskey) { (ttisuserdata(o) && (!iskey && isfinalized(uvalue(o)))); } + 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)); @@ -363,7 +361,6 @@ static void traverseproto (global_State *g, Proto *f) { } - static void traverseclosure (global_State *g, Closure *cl) { markobject(g, cl->c.env); if (cl->c.isC) { @@ -443,10 +440,8 @@ static l_mem propagatemark (global_State *g) { } -static size_t propagateall (global_State *g) { - size_t m = 0; - while (g->gray) m += propagatemark(g); - return m; +static void propagateall (global_State *g) { + while (g->gray) propagatemark(g); } @@ -615,6 +610,8 @@ static void GCTM (lua_State *L, int propagateerrors) { setuvalue(L, L->top+1, udata); L->top += 2; status = luaD_pcall(L, dothecall, NULL, savestack(L, L->top - 2), 0); + L->allowhook = oldah; /* restore hooks */ + g->GCthreshold = oldt; /* restore threshold */ if (status != LUA_OK && propagateerrors) { /* error while running __gc? */ if (status == LUA_ERRRUN) { /* is there an error msg.? */ luaO_pushfstring(L, "error in __gc tag method (%s)", @@ -623,24 +620,13 @@ static void GCTM (lua_State *L, int propagateerrors) { } luaD_throw(L, status); /* re-send error */ } - L->allowhook = oldah; /* restore hooks */ - g->GCthreshold = oldt; /* restore threshold */ } } -/* -** Call all GC tag methods (without raising errors) -*/ -void luaC_callAllGCTM (lua_State *L) { - while (G(L)->tobefnz) GCTM(L, 0); -} - - /* move 'dead' udata that need finalization to list 'tobefnz' */ -size_t luaC_separateudata (lua_State *L, int all) { +void luaC_separateudata (lua_State *L, int all) { global_State *g = G(L); - size_t deadmem = 0; /* total size of all objects to be finalized */ GCObject **p = &g->mainthread->next; GCObject *curr; GCObject **lastnext = &g->tobefnz; @@ -653,7 +639,6 @@ size_t luaC_separateudata (lua_State *L, int all) { p = &gch(curr)->next; /* don't bother with it */ else { l_setbit(gch(curr)->marked, FINALIZEDBIT); /* won't be finalized again */ - deadmem += sizeudata(gco2u(curr)); *p = gch(curr)->next; /* remove 'curr' from 'rootgc' list */ /* link 'curr' at the end of 'tobefnz' list */ gch(curr)->next = *lastnext; @@ -661,7 +646,6 @@ size_t luaC_separateudata (lua_State *L, int all) { lastnext = &gch(curr)->next; } } - return deadmem; } @@ -691,15 +675,15 @@ void luaC_checkfinalizer (lua_State *L, Udata *u) { ** ======================================================= */ -void luaC_freeall (lua_State *L) { +void luaC_freeallobjects (lua_State *L) { global_State *g = G(L); int i; - lua_assert(g->tobefnz == NULL); - /* mask to collect all elements */ + while (g->tobefnz) GCTM(L, 0); /* Call all pending finalizers */ + /* following "white" makes all objects look dead */ g->currentwhite = WHITEBITS | bitmask(SFIXEDBIT); sweepwholelist(L, &g->rootgc); - lua_assert(g->rootgc == obj2gco(g->mainthread)); - lua_assert(g->mainthread->next == NULL); + lua_assert(g->rootgc == obj2gco(g->mainthread) && + g->mainthread->next == NULL); for (i = 0; i < g->strt.size; i++) /* free all string lists */ sweepwholelist(L, &g->strt.hash[i]); lua_assert(g->strt.nuse == 0); @@ -708,49 +692,37 @@ void luaC_freeall (lua_State *L) { static void atomic (lua_State *L) { global_State *g = G(L); - size_t udsize; /* total size of userdata to be finalized */ - /* global table and registry may be changed by API */ + g->gcstate = GCSatomic; + lua_assert(!iswhite(obj2gco(g->mainthread))); + markobject(g, L); /* mark running thread */ + /* global table, registry, and global metatables may be changed by API */ markvalue(g, &g->l_gt); markvalue(g, &g->l_registry); + markmt(g); /* mark basic metatables */ /* remark occasional upvalues of (maybe) dead threads */ - g->gcstate = GCSatomic; remarkupvals(g); /* traverse objects caught by write barrier and by 'remarkupvals' */ propagateall(g); - /* remark weak tables */ - g->gray = g->weak; - g->weak = NULL; - lua_assert(!iswhite(obj2gco(g->mainthread))); - markobject(g, L); /* mark running thread */ - markmt(g); /* mark basic metatables (again) */ - propagateall(g); + /* at this point, all strongly accessible objects are marked. + Start marking weakily accessible objects. */ + traverselistofgrays(g, &g->weak); /* remark weak tables */ traverselistofgrays(g, &g->ephemeron); /* remark ephemeron tables */ traverselistofgrays(g, &g->grayagain); /* remark gray again */ convergeephemerons(g); - udsize = luaC_separateudata(L, 0); /* separate userdata to be finalized */ + luaC_separateudata(L, 0); /* separate userdata to be finalized */ markbeingfnz(g); /* mark userdata that will be finalized */ - udsize += propagateall(g); /* remark, to propagate `preserveness' */ + propagateall(g); /* remark, to propagate `preserveness' */ convergeephemerons(g); /* remove collected objects from weak tables */ cleartable(g->weak); cleartable(g->ephemeron); cleartable(g->allweak); - /* flip current white */ - g->currentwhite = cast_byte(otherwhite(g)); - g->sweepstrgc = 0; + g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */ + g->sweepstrgc = 0; /* go to sweep phase */ g->gcstate = GCSsweepstring; - lua_assert(g->totalbytes > udsize); - g->estimate = g->totalbytes - udsize; /* first estimate */ } -#define correctestimate(g,s) { \ - lu_mem old = g->totalbytes; s; \ - lua_assert(old >= g->totalbytes); \ - if (g->estimate >= old - g->totalbytes) \ - g->estimate -= (old - g->totalbytes);} - - static l_mem singlestep (lua_State *L) { global_State *g = G(L); /*lua_checkmemory(L);*/ @@ -768,7 +740,7 @@ static l_mem singlestep (lua_State *L) { } } case GCSsweepstring: { - correctestimate(g, sweepwholelist(L, &g->strt.hash[g->sweepstrgc++])); + sweepwholelist(L, &g->strt.hash[g->sweepstrgc++]); if (g->sweepstrgc >= g->strt.size) { /* nothing more to sweep? */ g->sweepgc = &g->rootgc; g->gcstate = GCSsweep; /* sweep all other objects */ @@ -776,7 +748,7 @@ static l_mem singlestep (lua_State *L) { return GCSWEEPCOST; } case GCSsweep: { - correctestimate(g, g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX)); + g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX); if (*g->sweepgc == NULL) /* nothing more to sweep? */ g->gcstate = GCSfinalize; /* end sweep phase */ return GCSWEEPMAX*GCSWEEPCOST; @@ -784,14 +756,11 @@ static l_mem singlestep (lua_State *L) { case GCSfinalize: { if (g->tobefnz) { GCTM(L, 1); - if (g->estimate > GCFINALIZECOST) - g->estimate -= GCFINALIZECOST; return GCFINALIZECOST; } else { - correctestimate(g, checkSizes(L)); + checkSizes(L); g->gcstate = GCSpause; /* end collection */ - g->gcdept = 0; return 0; } } @@ -802,29 +771,17 @@ static l_mem singlestep (lua_State *L) { void luaC_step (lua_State *L) { global_State *g = G(L); - l_mem lim = (GCSTEPSIZE/100) * g->gcstepmul; + l_mem lim = (GCSTEPSIZE/100) * g->gcstepmul; /* how much to work */ + lu_mem debt = g->totalbytes - g->GCthreshold; lua_assert(g->gckind == KGC_NORMAL); - if (lim == 0) - lim = (MAX_LUMEM-1)/2; /* no limit */ - g->gcdept += g->totalbytes - g->GCthreshold; - do { + do { /* always perform at least one single step */ lim -= singlestep(L); - if (g->gcstate == GCSpause) - break; - } while (lim > 0); - if (g->gcstate != GCSpause) { - if (g->gcdept < GCSTEPSIZE) - g->GCthreshold = g->totalbytes + GCSTEPSIZE; /* - lim/g->gcstepmul;*/ - else { - g->gcdept -= GCSTEPSIZE; - g->GCthreshold = g->totalbytes; - } - } - else { - if (g->estimate > g->totalbytes) - g->estimate = g->totalbytes; - setthreshold(g); - } + } while (lim > 0 && g->gcstate != GCSpause); + g->GCthreshold = (g->gcstate != GCSpause) + ? g->totalbytes + GCSTEPSIZE + : (g->totalbytes/100) * g->gcpause; + /* compensate if GC is "behind schedule" (has some debt to pay) */ + if (g->GCthreshold > debt) g->GCthreshold -= debt; } @@ -832,21 +789,15 @@ void luaC_fullgc (lua_State *L, int isemergency) { global_State *g = G(L); lua_assert(g->gckind == KGC_NORMAL); g->gckind = isemergency ? KGC_EMERGENCY : KGC_FORCED; - if (g->gcstate <= GCSpropagate) { - /* reset other collector lists */ - g->gray = NULL; - g->grayagain = NULL; - g->weak = g->ephemeron = g->allweak = NULL; + if (g->gcstate == GCSpropagate) { /* marking phase? */ + /* must sweep all objects to turn them back to white + (as white does not change, nothing will be collected) */ g->sweepstrgc = 0; g->gcstate = GCSsweepstring; } - lua_assert(g->gcstate != GCSpause && g->gcstate != GCSpropagate); /* finish any pending sweep phase */ - while (g->gcstate != GCSfinalize) { - lua_assert(issweep(g)); - singlestep(L); - } - markroot(L); + while (issweep(g)) singlestep(L); + markroot(L); /* start a new collection */ /* run collector up to finalizers */ while (g->gcstate != GCSfinalize) singlestep(L); @@ -855,7 +806,7 @@ void luaC_fullgc (lua_State *L, int isemergency) { while (g->gcstate != GCSpause) singlestep(L); } - setthreshold(g); + g->GCthreshold = (g->totalbytes/100) * g->gcpause; } /* }====================================================== */ diff --git a/lgc.h b/lgc.h index e79eb5db..8a671322 100644 --- a/lgc.h +++ b/lgc.h @@ -1,5 +1,5 @@ /* -** $Id: lgc.h,v 2.21 2009/06/08 19:35:59 roberto Exp roberto $ +** $Id: lgc.h,v 2.22 2009/11/17 11:56:03 roberto Exp roberto $ ** Garbage Collector ** See Copyright Notice in lua.h */ @@ -95,9 +95,8 @@ #define luaC_objbarriert(L,t,o) \ { if (iswhite(obj2gco(o)) && isblack(obj2gco(t))) luaC_barrierback(L,t); } -LUAI_FUNC size_t luaC_separateudata (lua_State *L, int all); -LUAI_FUNC void luaC_callAllGCTM (lua_State *L); -LUAI_FUNC void luaC_freeall (lua_State *L); +LUAI_FUNC void luaC_separateudata (lua_State *L, int all); +LUAI_FUNC void luaC_freeallobjects (lua_State *L); LUAI_FUNC void luaC_step (lua_State *L); LUAI_FUNC void luaC_fullgc (lua_State *L, int isemergency); LUAI_FUNC void luaC_link (lua_State *L, GCObject *o, lu_byte tt); diff --git a/lstate.c b/lstate.c index cb52eac3..f02c17ed 100644 --- a/lstate.c +++ b/lstate.c @@ -1,5 +1,5 @@ /* -** $Id: lstate.c,v 2.62 2009/10/05 16:44:33 roberto Exp roberto $ +** $Id: lstate.c,v 2.63 2009/10/23 19:12:19 roberto Exp roberto $ ** Global State ** See Copyright Notice in lua.h */ @@ -139,7 +139,6 @@ static void f_luaopen (lua_State *L, void *ud) { luaT_init(L); luaX_init(L); luaS_fix(luaS_newliteral(L, MEMERRMSG)); - g->estimate = g->totalbytes; g->GCthreshold = 4*g->totalbytes; } @@ -170,7 +169,7 @@ 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_freeall(L); /* collect all objects */ + luaC_freeallobjects(L); /* collect all objects */ luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size); luaZ_freebuffer(L, &g->buff); freestack(L); @@ -241,16 +240,10 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { g->version = lua_version(NULL); g->gcstate = GCSpause; g->rootgc = obj2gco(L); - g->sweepstrgc = 0; - g->sweepgc = &g->rootgc; - g->gray = NULL; - g->grayagain = NULL; - g->weak = g->ephemeron = g->allweak = NULL; g->tobefnz = NULL; - g->estimate = g->totalbytes = sizeof(LG); + g->totalbytes = sizeof(LG); g->gcpause = LUAI_GCPAUSE; g->gcstepmul = LUAI_GCMUL; - g->gcdept = 0; for (i=0; imt[i] = NULL; if (luaD_rawrunprotected(L, f_luaopen, NULL) != LUA_OK) { /* memory allocation error: free partial state */ @@ -269,7 +262,6 @@ LUA_API void lua_close (lua_State *L) { luaF_close(L, L->stack); /* close all upvalues for this thread */ luaC_separateudata(L, 1); /* separate all udata with GC metamethods */ lua_assert(L->next == NULL); - luaC_callAllGCTM(L); /* call GC metamethods for all udata */ luai_userstateclose(L); close_state(L); } diff --git a/lstate.h b/lstate.h index dcceefe1..84847eae 100644 --- a/lstate.h +++ b/lstate.h @@ -1,5 +1,5 @@ /* -** $Id: lstate.h,v 2.46 2009/07/15 17:26:14 roberto Exp roberto $ +** $Id: lstate.h,v 2.47 2009/10/23 19:12:19 roberto Exp roberto $ ** Global State ** See Copyright Notice in lua.h */ @@ -132,10 +132,8 @@ typedef struct global_State { GCObject *allweak; /* list of all-weak tables */ GCObject *tobefnz; /* list of userdata to be GC */ Mbuffer buff; /* temporary buffer for string concatentation */ - lu_mem GCthreshold; + lu_mem GCthreshold; /* when totalbytes > GCthreshold, run GC step */ lu_mem totalbytes; /* number of bytes currently allocated */ - lu_mem estimate; /* an estimate of number of bytes actually in use */ - lu_mem gcdept; /* how much GC is `behind schedule' */ int gcpause; /* size of pause between successive GCs */ int gcstepmul; /* GC `granularity' */ lua_CFunction panic; /* to be called in unprotected errors */