From 9b7a12c46d85666e0e60efd79b39495589e497e8 Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Fri, 26 Nov 2010 12:32:31 -0200 Subject: [PATCH] finalizers (__gc) for tables --- lapi.c | 5 ++-- lgc.c | 72 +++++++++++++++++++++++++++---------------------------- lgc.h | 4 ++-- lobject.h | 6 ++++- lstate.c | 4 ++-- lstate.h | 8 +++---- ltests.c | 21 ++++++++-------- 7 files changed, 63 insertions(+), 57 deletions(-) diff --git a/lapi.c b/lapi.c index 1e97cc46..a1796db3 100644 --- a/lapi.c +++ b/lapi.c @@ -1,5 +1,5 @@ /* -** $Id: lapi.c,v 2.139 2010/10/25 20:31:11 roberto Exp roberto $ +** $Id: lapi.c,v 2.141 2010/11/18 19:15:00 roberto Exp roberto $ ** Lua API ** See Copyright Notice in lua.h */ @@ -760,13 +760,14 @@ LUA_API int lua_setmetatable (lua_State *L, int objindex) { hvalue(obj)->metatable = mt; if (mt) luaC_objbarrierback(L, gcvalue(obj), mt); + luaC_checkfinalizer(L, gcvalue(obj), mt); break; } case LUA_TUSERDATA: { uvalue(obj)->metatable = mt; if (mt) { luaC_objbarrier(L, rawuvalue(obj), mt); - luaC_checkfinalizer(L, rawuvalue(obj)); + luaC_checkfinalizer(L, gcvalue(obj), mt); } break; } diff --git a/lgc.c b/lgc.c index f7072ab7..494791ed 100644 --- a/lgc.c +++ b/lgc.c @@ -1,5 +1,5 @@ /* -** $Id: lgc.c,v 2.102 2010/09/03 14:14:01 roberto Exp roberto $ +** $Id: lgc.c,v 2.104 2010/11/18 19:15:00 roberto Exp roberto $ ** Garbage Collector ** See Copyright Notice in lua.h */ @@ -68,7 +68,7 @@ #define stringmark(s) ((void)((s) && resetbits((s)->tsv.marked, WHITEBITS))) -#define isfinalized(u) testbit((u)->marked, FINALIZEDBIT) +#define isfinalized(x) testbit(gch(x)->marked, FINALIZEDBIT) #define checkdeadkey(n) lua_assert(!ttisdeadkey(gkey(n)) || ttisnil(gval(n))) @@ -114,12 +114,11 @@ static void removeentry (Node *n) { */ static int iscleared (const TValue *o, int iskey) { if (!iscollectable(o)) return 0; - if (ttisstring(o)) { + else if (ttisstring(o)) { stringmark(rawtsvalue(o)); /* strings are `values', so are never weak */ return 0; } - return iswhite(gcvalue(o)) || - (ttisuserdata(o) && (!iskey && isfinalized(uvalue(o)))); + else return iswhite(gcvalue(o)) || (!iskey && isfinalized(gcvalue(o))); } @@ -687,19 +686,18 @@ static void checkSizes (lua_State *L) { } -static Udata *udata2finalize (global_State *g) { +static GCObject *udata2finalize (global_State *g) { GCObject *o = g->tobefnz; /* get first element */ - Udata *u = rawgco2u(o); - lua_assert(isfinalized(&u->uv)); + lua_assert(isfinalized(o)); lua_assert(!isold(o)); - g->tobefnz = u->uv.next; /* remove it from 'tobefnz' list */ - u->uv.next = g->allgc; /* return it to 'allgc' list */ + g->tobefnz = gch(o)->next; /* remove it from 'tobefnz' list */ + gch(o)->next = g->allgc; /* return it to 'allgc' list */ g->allgc = o; - resetbit(u->uv.marked, SEPARATED); /* mark that it is not in 'tobefnz' */ + resetbit(gch(o)->marked, SEPARATED); /* mark that it is not in 'tobefnz' */ resetoldbit(o); /* see MOVE OLD rule */ if (!keepinvariant(g)) /* not keeping invariant? */ makewhite(g, o); /* "sweep" object */ - return u; + return o; } @@ -711,8 +709,10 @@ static void dothecall (lua_State *L, void *ud) { static void GCTM (lua_State *L, int propagateerrors) { global_State *g = G(L); - Udata *udata = udata2finalize(g); - const TValue *tm = gfasttm(g, udata->uv.metatable, TM_GC); + const TValue *tm; + TValue v; + setgcovalue(L, &v, udata2finalize(g)); + tm = luaT_gettmbyobj(L, &v, TM_GC); if (tm != NULL && ttisfunction(tm)) { /* is there a finalizer? */ int status; lu_byte oldah = L->allowhook; @@ -720,7 +720,7 @@ static void GCTM (lua_State *L, int propagateerrors) { L->allowhook = 0; /* stop debug hooks during GC tag method */ stopgc(g); /* avoid GC steps */ setobj2s(L, L->top, tm); /* push finalizer... */ - setuvalue(L, L->top+1, udata); /* ... and its argument */ + setobj2s(L, L->top + 1, &v); /* ... and its argument */ L->top += 2; /* and (next line) call the finalizer */ status = luaD_pcall(L, dothecall, NULL, savestack(L, L->top - 2), 0); L->allowhook = oldah; /* restore hooks */ @@ -738,25 +738,25 @@ static void GCTM (lua_State *L, int propagateerrors) { /* -** move all unreachable udata that need finalization from list 'udgc' to -** list 'tobefnz' +** move all unreachable objects that need finalization from list 'finobj' +** to list 'tobefnz' */ void luaC_separateudata (lua_State *L, int all) { global_State *g = G(L); - GCObject **p = &g->udgc; + GCObject **p = &g->finobj; GCObject *curr; GCObject **lastnext = &g->tobefnz; /* find last 'next' field in 'tobefnz' list (to add elements in its end) */ while (*lastnext != NULL) lastnext = &gch(*lastnext)->next; while ((curr = *p) != NULL) { /* traverse all finalizable objects */ - lua_assert(gch(curr)->tt == LUA_TUSERDATA && !isfinalized(gco2u(curr))); + lua_assert(!isfinalized(curr)); lua_assert(testbit(gch(curr)->marked, SEPARATED)); if (!(all || iswhite(curr))) /* not being collected? */ p = &gch(curr)->next; /* don't bother with it */ else { l_setbit(gch(curr)->marked, FINALIZEDBIT); /* won't be finalized again */ - *p = gch(curr)->next; /* remove 'curr' from 'udgc' list */ + *p = gch(curr)->next; /* remove 'curr' from 'finobj' list */ gch(curr)->next = *lastnext; /* link at the end of 'tobefnz' list */ *lastnext = curr; lastnext = &gch(curr)->next; @@ -766,23 +766,23 @@ void luaC_separateudata (lua_State *L, int all) { /* -** if userdata 'u' has a finalizer, remove it from 'allgc' list (must -** search the list to find it) and link it in 'udgc' list. +** if object 'o' has a finalizer, remove it from 'allgc' list (must +** search the list to find it) and link it in 'finobj' list. */ -void luaC_checkfinalizer (lua_State *L, Udata *u) { +void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) { global_State *g = G(L); - if (testbit(u->uv.marked, SEPARATED) || /* udata is already separated... */ - isfinalized(&u->uv) || /* ... or is finalized... */ - gfasttm(g, u->uv.metatable, TM_GC) == NULL) /* or has no finalizer? */ + if (testbit(gch(o)->marked, SEPARATED) || /* obj. is already separated... */ + isfinalized(o) || /* ... or is finalized... */ + gfasttm(g, mt, TM_GC) == NULL) /* or has no finalizer? */ return; /* nothing to be done */ - else { /* move 'u' to 'udgc' list */ + else { /* move 'o' to 'finobj' list */ GCObject **p; - for (p = &g->allgc; *p != obj2gco(u); p = &gch(*p)->next) ; - *p = u->uv.next; /* remove 'u' from root list */ - u->uv.next = g->udgc; /* link it in list 'udgc' */ - g->udgc = obj2gco(u); - l_setbit(u->uv.marked, SEPARATED); /* mark it as such */ - resetoldbit(obj2gco(u)); /* see MOVE OLD rule */ + for (p = &g->allgc; *p != o; p = &gch(*p)->next) ; + *p = gch(o)->next; /* remove 'o' from root list */ + gch(o)->next = g->finobj; /* link it in list 'finobj' */ + g->finobj = o; + l_setbit(gch(o)->marked, SEPARATED); /* mark it as such */ + resetoldbit(o); /* see MOVE OLD rule */ } } @@ -837,8 +837,8 @@ void luaC_freeallobjects (lua_State *L) { /* following "white" makes all objects look dead */ g->currentwhite = WHITEBITS; g->gckind = KGC_NORMAL; - sweepwholelist(L, &g->udgc); - lua_assert(g->udgc == NULL); + sweepwholelist(L, &g->finobj); + lua_assert(g->finobj == NULL); sweepwholelist(L, &g->allgc); lua_assert(g->allgc == NULL); for (i = 0; i < g->strt.size; i++) /* free all string lists */ @@ -905,7 +905,7 @@ static l_mem singlestep (lua_State *L) { return GCSWEEPCOST; } else { /* no more strings to sweep */ - g->sweepgc = &g->udgc; /* prepare to sweep userdata */ + g->sweepgc = &g->finobj; /* prepare to sweep finalizable objects */ g->gcstate = GCSsweepudata; return 0; } diff --git a/lgc.h b/lgc.h index c7cfdf7c..4c5d2eca 100644 --- a/lgc.h +++ b/lgc.h @@ -1,5 +1,5 @@ /* -** $Id: lgc.h,v 2.43 2010/06/07 16:55:34 roberto Exp roberto $ +** $Id: lgc.h,v 2.45 2010/11/18 19:15:00 roberto Exp $ ** Garbage Collector ** See Copyright Notice in lua.h */ @@ -137,7 +137,7 @@ LUAI_FUNC GCObject *luaC_newobj (lua_State *L, int tt, size_t sz, LUAI_FUNC void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v); LUAI_FUNC void luaC_barrierback_ (lua_State *L, GCObject *o); LUAI_FUNC void luaC_barrierproto_ (lua_State *L, Proto *p, Closure *c); -LUAI_FUNC void luaC_checkfinalizer (lua_State *L, Udata *u); +LUAI_FUNC void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt); LUAI_FUNC void luaC_checkupvalcolor (global_State *g, UpVal *uv); LUAI_FUNC void luaC_changemode (lua_State *L, int mode); diff --git a/lobject.h b/lobject.h index a23cc560..b9e554b4 100644 --- a/lobject.h +++ b/lobject.h @@ -1,5 +1,5 @@ /* -** $Id: lobject.h,v 2.41 2010/06/04 13:25:10 roberto Exp roberto $ +** $Id: lobject.h,v 2.42 2010/07/26 15:53:23 roberto Exp roberto $ ** Type definitions for Lua objects ** See Copyright Notice in lua.h */ @@ -153,6 +153,10 @@ typedef struct lua_TValue { #define setbvalue(obj,x) \ { TValue *i_o=(obj); i_o->value_.b=(x); i_o->tt_=LUA_TBOOLEAN; } +#define setgcovalue(L,obj,x) \ + { TValue *i_o=(obj); GCObject *i_g=(x); \ + i_o->value_.gc=i_g; i_o->tt_=gch(i_g)->tt; } + #define setsvalue(L,obj,x) \ { TValue *i_o=(obj); \ i_o->value_.gc=cast(GCObject *, (x)); i_o->tt_=LUA_TSTRING; \ diff --git a/lstate.c b/lstate.c index ef4f89cd..fee415b6 100644 --- a/lstate.c +++ b/lstate.c @@ -1,5 +1,5 @@ /* -** $Id: lstate.c,v 2.85 2010/04/30 18:36:22 roberto Exp roberto $ +** $Id: lstate.c,v 2.86 2010/09/03 14:14:01 roberto Exp roberto $ ** Global State ** See Copyright Notice in lua.h */ @@ -252,7 +252,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { g->version = lua_version(NULL); g->gcstate = GCSpause; g->allgc = NULL; - g->udgc = NULL; + g->finobj = NULL; g->tobefnz = NULL; g->gray = g->grayagain = NULL; g->weak = g->ephemeron = g->allweak = NULL; diff --git a/lstate.h b/lstate.h index a8923ca3..40c3ac16 100644 --- a/lstate.h +++ b/lstate.h @@ -1,5 +1,5 @@ /* -** $Id: lstate.h,v 2.67 2010/09/30 17:21:31 roberto Exp roberto $ +** $Id: lstate.h,v 2.68 2010/10/29 17:52:46 roberto Exp roberto $ ** Global State ** See Copyright Notice in lua.h */ @@ -32,9 +32,9 @@ ** 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->udgc. +** Objects with finalizers are kept in the list g->finobj. ** -** The list g->tobefnz links all userdata being finalized. +** The list g->tobefnz links all objects being finalized. */ @@ -125,7 +125,7 @@ typedef struct global_State { lu_byte gckind; /* kind of GC running */ int sweepstrgc; /* position of sweep in `strt' */ GCObject *allgc; /* list of all collectable objects */ - GCObject *udgc; /* list of collectable userdata with finalizers */ + GCObject *finobj; /* list of collectable objects with finalizers */ GCObject **sweepgc; /* current position of sweep */ GCObject *gray; /* list of gray objects */ GCObject *grayagain; /* list of objects to be traversed atomically */ diff --git a/ltests.c b/ltests.c index 19d33ec8..7d629f48 100644 --- a/ltests.c +++ b/ltests.c @@ -1,5 +1,5 @@ /* -** $Id: ltests.c,v 2.112 2010/07/28 15:51:59 roberto Exp roberto $ +** $Id: ltests.c,v 2.113 2010/11/16 17:43:29 roberto Exp roberto $ ** Internal Module for Debugging of the Lua Implementation ** See Copyright Notice in lua.h */ @@ -429,20 +429,21 @@ int lua_checkmemory (lua_State *L) { checkobject(g, o); lua_assert(!testbit(o->gch.marked, SEPARATED)); } - /* check 'udgc' list */ - checkold(g, g->udgc); - for (o = g->udgc; o != NULL; o = gch(o)->next) { - lua_assert(gch(o)->tt == LUA_TUSERDATA && - !isdead(g, o) && - testbit(o->gch.marked, SEPARATED)); + /* check 'finobj' list */ + checkold(g, g->finobj); + for (o = g->finobj; o != NULL; o = gch(o)->next) { + lua_assert(!isdead(g, o) && testbit(o->gch.marked, SEPARATED)); + lua_assert(gch(o)->tt == LUA_TUSERDATA || + gch(o)->tt == LUA_TTABLE); checkobject(g, o); } /* check 'tobefnz' list */ checkold(g, g->tobefnz); for (o = g->tobefnz; o != NULL; o = gch(o)->next) { - lua_assert(gch(o)->tt == LUA_TUSERDATA); - lua_assert(isblack(o)); - lua_assert(testbit(o->gch.marked, SEPARATED)); + lua_assert(!iswhite(o)); + lua_assert(!isdead(g, o) && testbit(o->gch.marked, SEPARATED)); + lua_assert(gch(o)->tt == LUA_TUSERDATA || + gch(o)->tt == LUA_TTABLE); } /* check 'uvhead' list */ for (uv = g->uvhead.u.l.next; uv != &g->uvhead; uv = uv->u.l.next) {