lua

A copy of the Lua development repository
Log | Files | Refs | README

commit 0c8f5fc2fdcadc8fe6e89c32bf3a1f7456328899
parent 1527d8f00d4a99997cf73f50fe159ddba8681f8f
Author: Roberto Ierusalimschy <roberto@inf.puc-rio.br>
Date:   Thu, 26 Jun 2008 16:42:22 -0300

simplification in the handling of finalizers: no more 'tmudata' list +
no more GCSsweeptmu collector's state

Diffstat:
Mlgc.c | 106++++++++++++++++++++++++++-----------------------------------------------------
Mlgc.h | 30++++++++++++++----------------
Mlstate.c | 8++++----
Mlstate.h | 31++++++++++++++++++++++++++++---
Mltests.c | 14+++++++++-----
5 files changed, 89 insertions(+), 100 deletions(-)

diff --git a/lgc.c b/lgc.c @@ -1,5 +1,5 @@ /* -** $Id: lgc.c,v 2.45 2008/06/23 16:51:28 roberto Exp roberto $ +** $Id: lgc.c,v 2.46 2008/06/23 22:07:44 roberto Exp roberto $ ** Garbage Collector ** See Copyright Notice in lua.h */ @@ -79,7 +79,7 @@ static void removeentry (Node *n) { ** The next function tells whether a key or value can be cleared from ** a weak table. Non-collectable objects are never removed from weak ** tables. Strings behave as `values', so are never removed too. for -** other objects: if really collected, cannot keep them; for userdata +** other objects: if really collected, cannot keep them; for objects ** being finalized, keep them in keys, but not in values */ static int iscleared (const TValue *o, int iskey) { @@ -205,14 +205,11 @@ static void markmt (global_State *g) { static void markbeingfnz (global_State *g) { - GCObject *u = g->tobefnz; - if (u) { - do { - u = gch(u)->next; - lua_assert(testbit(gch(u)->marked, SEPARATED)); - makewhite(g, u); - reallymarkobject(g, u); - } while (u != g->tobefnz); + GCObject *o; + for (o = g->tobefnz; o != NULL; o = gch(o)->next) { + lua_assert(testbit(gch(o)->marked, SEPARATED)); + makewhite(g, o); + reallymarkobject(g, o); } } @@ -228,7 +225,7 @@ static void markroot (lua_State *L) { markvalue(g, gt(g->mainthread)); markvalue(g, registry(L)); markmt(g); - markbeingfnz(g); /* mark any finalizing userdata left from previous cycle */ + markbeingfnz(g); /* mark any finalizing object left from previous cycle */ g->gcstate = GCSpropagate; } @@ -573,17 +570,6 @@ static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count) { return p; } - -/* sweep list where all objects are alive (dead udata were removed - when separated from this list) */ -static GCObject **unmarklist (global_State *g, GCObject **p, lu_mem count) { - for (; *p != NULL && count-- > 0; p = &gch(*p)->next) { - lua_assert(ttisuserdata(gch(*p)) && !isdead(g, *p)); - makewhite(g, *p); - } - return p; -} - /* }====================================================== */ @@ -602,18 +588,14 @@ static void checkSizes (lua_State *L) { static Udata *udata2finalize (global_State *g) { - GCObject *o = gch(g->tobefnz)->next; /* get first element */ - Udata *udata = rawgco2u(o); - /* remove udata from `tobefnz' */ - if (o == g->tobefnz) /* last element? */ - g->tobefnz = NULL; - else - gch(g->tobefnz)->next = udata->uv.next; - udata->uv.next = g->mainthread->next; /* return it to `root' list */ - g->mainthread->next = o; - resetbit(udata->uv.marked, SEPARATED); /* mark it as such */ + GCObject *o = g->tobefnz; /* get first element */ + g->tobefnz = gch(o)->next; /* remove it from 'tobefnz' list */ + gch(o)->next = g->rootgc; /* return it to `root' list */ + g->rootgc = o; + lua_assert(isfinalized(gch(o))); + resetbit(gch(o)->marked, SEPARATED); /* mark it as such */ makewhite(g, o); - return udata; + return rawgco2u(o); } @@ -639,28 +621,21 @@ static void GCTM (lua_State *L) { /* ** Call all GC tag methods */ -void luaC_callGCTM (lua_State *L) { - global_State *g = G(L); - GCObject *last = g->tobefnz; - GCObject *curr; - if (last == NULL) return; /* empty list? */ - do { - curr = gch(g->tobefnz)->next; /* element to be collected */ - GCTM(L); - } while (curr != last); /* go only until original last */ - /* do not finalize new udata created during previous finalizations */ - while (g->tobefnz) - udata2finalize(g); /* simply remove them from list */ +void luaC_callAllGCTM (lua_State *L) { + while (G(L)->tobefnz) GCTM(L); } /* move 'dead' udata that need finalization to list 'tobefnz' */ size_t luaC_separateudata (lua_State *L, int all) { global_State *g = G(L); - size_t deadmem = 0; - GCObject **p = &g->tmudata; + size_t deadmem = 0; /* total size of all objects to be finalized */ + GCObject **p = &g->mainthread->next; GCObject *curr; - while ((curr = *p) != NULL) { + GCObject **lastnext = &g->tobefnz; + /* find last 'next' field in 'tobefnz' list (to insert elements in its end) */ + while (*lastnext != NULL) lastnext = &gch(*lastnext)->next; + while ((curr = *p) != NULL) { /* traverse all finalizable objects */ lua_assert(ttisuserdata(gch(curr)) && !isfinalized(gco2u(curr))); lua_assert(testbit(gch(curr)->marked, SEPARATED)); if (!(all || iswhite(curr))) /* not being collected? */ @@ -668,15 +643,11 @@ size_t luaC_separateudata (lua_State *L, int all) { else { l_setbit(gch(curr)->marked, FINALIZEDBIT); /* won't be finalized again */ deadmem += sizeudata(gco2u(curr)); - *p = gch(curr)->next; /* remove 'curr' from 'tmudata' list */ + *p = gch(curr)->next; /* remove 'curr' from 'rootgc' list */ /* link 'curr' at the end of 'tobefnz' list */ - if (g->tobefnz == NULL) /* list is empty? */ - g->tobefnz = gch(curr)->next = curr; /* creates a circular list */ - else { - gch(curr)->next = gch(g->tobefnz)->next; - gch(g->tobefnz)->next = curr; - g->tobefnz = curr; - } + gch(curr)->next = *lastnext; + *lastnext = curr; + lastnext = &gch(curr)->next; } } return deadmem; @@ -689,13 +660,13 @@ void luaC_checkfinalizer (lua_State *L, Udata *u) { isfinalized(&u->uv) || /* ... or is finalized... */ gfasttm(g, u->uv.metatable, TM_GC) == NULL) /* or has no finalization? */ return; /* nothing to be done */ - else { /* move 'u' from root list to 'tmudata' list */ + else { /* move 'u' to 2nd part of root list */ GCObject **p; for (p = &g->rootgc; *p != obj2gco(u); p = &gch(*p)->next) - lua_assert(*p != NULL); /* 'u' must be in this list */ + lua_assert(*p != obj2gco(g->mainthread)); /* 'u' must be in this list */ *p = u->uv.next; /* remove 'u' from root list */ - u->uv.next = g->tmudata; /* link it in 'tmudata' list */ - g->tmudata = obj2gco(u); + u->uv.next = g->mainthread->next; /* re-link it in list */ + g->mainthread->next = obj2gco(u); l_setbit(u->uv.marked, SEPARATED); /* mark it as such */ } } @@ -716,9 +687,8 @@ void luaC_freeall (lua_State *L) { /* mask to collect all elements */ g->currentwhite = WHITEBITS | bitmask(SFIXEDBIT); sweepwholelist(L, &g->rootgc); - lua_assert(g->rootgc == obj2gco(L)); - sweepwholelist(L, &g->tmudata); - lua_assert(g->tmudata == NULL); + lua_assert(g->rootgc == obj2gco(g->mainthread)); + lua_assert(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); @@ -781,18 +751,10 @@ static l_mem singlestep (lua_State *L) { case GCSsweepstring: { correctestimate(g, sweepwholelist(L, &g->strt.hash[g->sweepstrgc++])); if (g->sweepstrgc >= g->strt.size) { /* nothing more to sweep? */ - g->sweepgc = &g->tmudata; - g->gcstate = GCSsweeptmu; /* end sweep-string phase */ - } - return GCSWEEPCOST; - } - case GCSsweeptmu: { - g->sweepgc = unmarklist(g, g->sweepgc, GCSWEEPMAX); - if (*g->sweepgc == NULL) { /* nothing more to sweep? */ g->sweepgc = &g->rootgc; g->gcstate = GCSsweep; /* sweep all other objects */ } - return GCSWEEPMAX*GCSWEEPCOST; + return GCSWEEPCOST; } case GCSsweep: { correctestimate(g, g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX)); diff --git a/lgc.h b/lgc.h @@ -1,5 +1,5 @@ /* -** $Id: lgc.h,v 2.17 2007/10/29 16:51:20 roberto Exp roberto $ +** $Id: lgc.h,v 2.18 2008/02/19 18:55:09 roberto Exp roberto $ ** Garbage Collector ** See Copyright Notice in lua.h */ @@ -17,26 +17,24 @@ #define GCSpause 0 #define GCSpropagate 1 #define GCSsweepstring 2 -#define GCSsweeptmu 3 -#define GCSsweep 4 -#define GCSfinalize 5 +#define GCSsweep 3 +#define GCSfinalize 4 -#define issweep(g) \ - (GCSsweepstring <= (g)->gcstate && (g)->gcstate <= GCSsweep) +#define issweep(g) (GCSsweepstring <= (g)->gcstate && (g)->gcstate <= GCSsweep) /* ** some userful bit tricks */ -#define resetbits(x,m) ((x) &= cast(lu_byte, ~(m))) -#define setbits(x,m) ((x) |= (m)) -#define testbits(x,m) ((x) & (m)) -#define bitmask(b) (1<<(b)) -#define bit2mask(b1,b2) (bitmask(b1) | bitmask(b2)) -#define l_setbit(x,b) setbits(x, bitmask(b)) -#define resetbit(x,b) resetbits(x, bitmask(b)) -#define testbit(x,b) testbits(x, bitmask(b)) +#define resetbits(x,m) ((x) &= cast(lu_byte, ~(m))) +#define setbits(x,m) ((x) |= (m)) +#define testbits(x,m) ((x) & (m)) +#define bitmask(b) (1<<(b)) +#define bit2mask(b1,b2) (bitmask(b1) | bitmask(b2)) +#define l_setbit(x,b) setbits(x, bitmask(b)) +#define resetbit(x,b) resetbits(x, bitmask(b)) +#define testbit(x,b) testbits(x, bitmask(b)) #define set2bits(x,b1,b2) setbits(x, (bit2mask(b1, b2))) #define reset2bits(x,b1,b2) resetbits(x, (bit2mask(b1, b2))) @@ -48,7 +46,7 @@ ** bit 1 - object is white (type 1) ** bit 2 - object is black ** bit 3 - for userdata: has been finalized -** bit 4 - for userdata: it's not in rootgc list (it's in tmudata or tobefnz) +** bit 4 - for userdata: it's in 2nd part of rootgc list or in tobefnz ** bit 5 - object is fixed (should not be collected) ** bit 6 - object is "super" fixed (only the main thread) */ @@ -99,7 +97,7 @@ { 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_callGCTM (lua_State *L); +LUAI_FUNC void luaC_callAllGCTM (lua_State *L); LUAI_FUNC void luaC_freeall (lua_State *L); LUAI_FUNC void luaC_step (lua_State *L); LUAI_FUNC void luaC_fullgc (lua_State *L, int isemergency); diff --git a/lstate.c b/lstate.c @@ -1,5 +1,5 @@ /* -** $Id: lstate.c,v 2.43 2008/02/11 15:45:30 roberto Exp roberto $ +** $Id: lstate.c,v 2.44 2008/02/19 18:55:09 roberto Exp roberto $ ** Global State ** See Copyright Notice in lua.h */ @@ -181,7 +181,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { g->gray = NULL; g->grayagain = NULL; g->weak = g->ephemeron = g->allweak = NULL; - g->tmudata = g->tobefnz = NULL; + g->tobefnz = NULL; g->totalbytes = sizeof(LG); g->gcpause = LUAI_GCPAUSE; g->gcstepmul = LUAI_GCMUL; @@ -200,7 +200,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { static void callallgcTM (lua_State *L, void *ud) { UNUSED(ud); - luaC_callGCTM(L); /* call GC metamethods for all udata */ + luaC_callAllGCTM(L); /* call GC metamethods for all udata */ } @@ -209,7 +209,7 @@ LUA_API void lua_close (lua_State *L) { lua_lock(L); luaF_close(L, L->stack); /* close all upvalues for this thread */ luaC_separateudata(L, 1); /* separate all udata with GC metamethods */ - lua_assert(G(L)->tmudata == NULL); + lua_assert(L->next == NULL); L->errfunc = 0; /* no error function during GC metamethods */ do { /* repeat until no more errors */ L->ci = L->base_ci; diff --git a/lstate.h b/lstate.h @@ -1,5 +1,5 @@ /* -** $Id: lstate.h,v 2.32 2008/02/19 18:55:09 roberto Exp roberto $ +** $Id: lstate.h,v 2.33 2008/06/23 16:51:08 roberto Exp roberto $ ** Global State ** See Copyright Notice in lua.h */ @@ -14,6 +14,32 @@ #include "lzio.h" +/* + +** Some notes about garbage-collected objects: All objects in Lua must +** be kept somehow accessible until being freed. +** +** Lua keeps most objects linked in list g->rootgc. The link uses field +** 'next' of the CommonHeader. +** +** Strings are kept in several lists headed by the array g->strt.hash. +** +** Open upvalues are not subject to independent garbage collection. They +** are collected together with their respective threads. Lua keeps a +** double-linked list with all open upvalues (g->uvhead) so that it can +** mark objects referred by them. (They are always gray, so they must +** be remarked in the atomic step. Usually their contents would be marked +** when traversing the respective threads, but the thread may already be +** dead, while the upvalue is still accessible through closures.) +** +** Userdata with finalizers are kept in the list g->rootgc, but after +** the mainthread, which should be otherwise the last element in the +** list, as it was the first one inserted there. +** +** The list g->tobefnz links all userdata being finalized. + +*/ + struct lua_longjmp; /* defined in ldo.c */ @@ -86,8 +112,7 @@ typedef struct global_State { GCObject *weak; /* list of tables with weak values */ GCObject *ephemeron; /* list of ephemeron tables (weak keys) */ GCObject *allweak; /* list of all-weak tables */ - GCObject *tmudata; /* list of userdata with finalizers */ - GCObject *tobefnz; /* last element of list of userdata to be GC */ + GCObject *tobefnz; /* list of userdata to be GC */ Mbuffer buff; /* temporary buffer for string concatentation */ lu_mem GCthreshold; lu_mem totalbytes; /* number of bytes currently allocated */ diff --git a/ltests.c b/ltests.c @@ -1,5 +1,5 @@ /* -** $Id: ltests.c,v 2.51 2008/06/13 17:07:10 roberto Exp roberto $ +** $Id: ltests.c,v 2.52 2008/06/23 16:50:34 roberto Exp roberto $ ** Internal Module for Debugging of the Lua Implementation ** See Copyright Notice in lua.h */ @@ -365,10 +365,15 @@ int lua_checkmemory (lua_State *L) { GCObject *o; UpVal *uv; checkstack(g, g->mainthread); - for (o = g->rootgc; o != NULL; o = gch(o)->next) + for (o = g->rootgc; o != obj2gco(g->mainthread); o = gch(o)->next) { + lua_assert(!testbits(o->gch.marked, bit2mask(SEPARATED, SFIXEDBIT))); checkobject(g, o); - for (o = g->tmudata; o != NULL; o = gch(o)->next) { - lua_assert(!isdead(g, o)); + } + lua_assert(testbit(o->gch.marked, SFIXEDBIT)); + for (o = gch(o)->next; o != NULL; o = gch(o)->next) { + lua_assert(gch(o)->tt == LUA_TUSERDATA && + !isdead(g, o) && + testbit(o->gch.marked, SEPARATED)); checkobject(g, o); } for (uv = g->uvhead.u.l.next; uv != &g->uvhead; uv = uv->u.l.next) { @@ -535,7 +540,6 @@ static int gcstate (lua_State *L) { switch(G(L)->gcstate) { case GCSpropagate: lua_pushstring(L, "propagate"); break; case GCSsweepstring: lua_pushstring(L, "sweep strings"); break; - case GCSsweeptmu: lua_pushstring(L, "sweep udata with __gc"); break; case GCSsweep: lua_pushstring(L, "sweep"); break; case GCSfinalize: lua_pushstring(L, "finalize"); break; default: lua_assert(0);