finalizers (__gc) for tables

This commit is contained in:
Roberto Ierusalimschy
2010-11-26 12:32:31 -02:00
parent 5b33e39855
commit 9b7a12c46d
7 changed files with 63 additions and 57 deletions

72
lgc.c
View File

@@ -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;
}