Browse Source

finalizers (__gc) for tables

Roberto Ierusalimschy 14 years ago
parent
commit
9b7a12c46d
7 changed files with 63 additions and 57 deletions
  1. 3 2
      lapi.c
  2. 36 36
      lgc.c
  3. 2 2
      lgc.h
  4. 5 1
      lobject.h
  5. 2 2
      lstate.c
  6. 4 4
      lstate.h
  7. 11 10
      ltests.c

+ 3 - 2
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
 ** Lua API
 ** See Copyright Notice in lua.h
 ** See Copyright Notice in lua.h
 */
 */
@@ -760,13 +760,14 @@ LUA_API int lua_setmetatable (lua_State *L, int objindex) {
       hvalue(obj)->metatable = mt;
       hvalue(obj)->metatable = mt;
       if (mt)
       if (mt)
         luaC_objbarrierback(L, gcvalue(obj), mt);
         luaC_objbarrierback(L, gcvalue(obj), mt);
+        luaC_checkfinalizer(L, gcvalue(obj), mt);
       break;
       break;
     }
     }
     case LUA_TUSERDATA: {
     case LUA_TUSERDATA: {
       uvalue(obj)->metatable = mt;
       uvalue(obj)->metatable = mt;
       if (mt) {
       if (mt) {
         luaC_objbarrier(L, rawuvalue(obj), mt);
         luaC_objbarrier(L, rawuvalue(obj), mt);
-        luaC_checkfinalizer(L, rawuvalue(obj));
+        luaC_checkfinalizer(L, gcvalue(obj), mt);
       }
       }
       break;
       break;
     }
     }

+ 36 - 36
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
 ** Garbage Collector
 ** See Copyright Notice in lua.h
 ** See Copyright Notice in lua.h
 */
 */
@@ -68,7 +68,7 @@
 #define stringmark(s)	((void)((s) && resetbits((s)->tsv.marked, WHITEBITS)))
 #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)))
 #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) {
 static int iscleared (const TValue *o, int iskey) {
   if (!iscollectable(o)) return 0;
   if (!iscollectable(o)) return 0;
-  if (ttisstring(o)) {
+  else if (ttisstring(o)) {
     stringmark(rawtsvalue(o));  /* strings are `values', so are never weak */
     stringmark(rawtsvalue(o));  /* strings are `values', so are never weak */
     return 0;
     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 */
   GCObject *o = g->tobefnz;  /* get first element */
-  Udata *u = rawgco2u(o);
-  lua_assert(isfinalized(&u->uv));
+  lua_assert(isfinalized(o));
   lua_assert(!isold(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;
   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 */
   resetoldbit(o); /* see MOVE OLD rule */
   if (!keepinvariant(g))  /* not keeping invariant? */
   if (!keepinvariant(g))  /* not keeping invariant? */
     makewhite(g, o);  /* "sweep" object */
     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) {
 static void GCTM (lua_State *L, int propagateerrors) {
   global_State *g = G(L);
   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? */
   if (tm != NULL && ttisfunction(tm)) {  /* is there a finalizer? */
     int status;
     int status;
     lu_byte oldah = L->allowhook;
     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 */
     L->allowhook = 0;  /* stop debug hooks during GC tag method */
     stopgc(g);  /* avoid GC steps */
     stopgc(g);  /* avoid GC steps */
     setobj2s(L, L->top, tm);  /* push finalizer... */
     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 */
     L->top += 2;  /* and (next line) call the finalizer */
     status = luaD_pcall(L, dothecall, NULL, savestack(L, L->top - 2), 0);
     status = luaD_pcall(L, dothecall, NULL, savestack(L, L->top - 2), 0);
     L->allowhook = oldah;  /* restore hooks */
     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) {
 void luaC_separateudata (lua_State *L, int all) {
   global_State *g = G(L);
   global_State *g = G(L);
-  GCObject **p = &g->udgc;
+  GCObject **p = &g->finobj;
   GCObject *curr;
   GCObject *curr;
   GCObject **lastnext = &g->tobefnz;
   GCObject **lastnext = &g->tobefnz;
   /* find last 'next' field in 'tobefnz' list (to add elements in its end) */
   /* find last 'next' field in 'tobefnz' list (to add elements in its end) */
   while (*lastnext != NULL)
   while (*lastnext != NULL)
     lastnext = &gch(*lastnext)->next;
     lastnext = &gch(*lastnext)->next;
   while ((curr = *p) != NULL) {  /* traverse all finalizable objects */
   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));
     lua_assert(testbit(gch(curr)->marked, SEPARATED));
     if (!(all || iswhite(curr)))  /* not being collected? */
     if (!(all || iswhite(curr)))  /* not being collected? */
       p = &gch(curr)->next;  /* don't bother with it */
       p = &gch(curr)->next;  /* don't bother with it */
     else {
     else {
       l_setbit(gch(curr)->marked, FINALIZEDBIT); /* won't be finalized again */
       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 */
       gch(curr)->next = *lastnext;  /* link at the end of 'tobefnz' list */
       *lastnext = curr;
       *lastnext = curr;
       lastnext = &gch(curr)->next;
       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);
   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 */
     return;  /* nothing to be done */
-  else {  /* move 'u' to 'udgc' list */
+  else {  /* move 'o' to 'finobj' list */
     GCObject **p;
     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 */
   /* following "white" makes all objects look dead */
   g->currentwhite = WHITEBITS;
   g->currentwhite = WHITEBITS;
   g->gckind = KGC_NORMAL;
   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);
   sweepwholelist(L, &g->allgc);
   lua_assert(g->allgc == NULL);
   lua_assert(g->allgc == NULL);
   for (i = 0; i < g->strt.size; i++)  /* free all string lists */
   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;
         return GCSWEEPCOST;
       }
       }
       else {  /* no more strings to sweep */
       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;
         g->gcstate = GCSsweepudata;
         return 0;
         return 0;
       }
       }

+ 2 - 2
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
 ** Garbage Collector
 ** See Copyright Notice in lua.h
 ** 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_barrier_ (lua_State *L, GCObject *o, GCObject *v);
 LUAI_FUNC void luaC_barrierback_ (lua_State *L, GCObject *o);
 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_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_checkupvalcolor (global_State *g, UpVal *uv);
 LUAI_FUNC void luaC_changemode (lua_State *L, int mode);
 LUAI_FUNC void luaC_changemode (lua_State *L, int mode);
 
 

+ 5 - 1
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
 ** Type definitions for Lua objects
 ** See Copyright Notice in lua.h
 ** See Copyright Notice in lua.h
 */
 */
@@ -153,6 +153,10 @@ typedef struct lua_TValue {
 #define setbvalue(obj,x) \
 #define setbvalue(obj,x) \
   { TValue *i_o=(obj); i_o->value_.b=(x); i_o->tt_=LUA_TBOOLEAN; }
   { 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) \
 #define setsvalue(L,obj,x) \
   { TValue *i_o=(obj); \
   { TValue *i_o=(obj); \
     i_o->value_.gc=cast(GCObject *, (x)); i_o->tt_=LUA_TSTRING; \
     i_o->value_.gc=cast(GCObject *, (x)); i_o->tt_=LUA_TSTRING; \

+ 2 - 2
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
 ** Global State
 ** See Copyright Notice in lua.h
 ** 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->version = lua_version(NULL);
   g->gcstate = GCSpause;
   g->gcstate = GCSpause;
   g->allgc = NULL;
   g->allgc = NULL;
-  g->udgc = NULL;
+  g->finobj = NULL;
   g->tobefnz = NULL;
   g->tobefnz = NULL;
   g->gray = g->grayagain = NULL;
   g->gray = g->grayagain = NULL;
   g->weak = g->ephemeron = g->allweak = NULL;
   g->weak = g->ephemeron = g->allweak = NULL;

+ 4 - 4
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
 ** Global State
 ** See Copyright Notice in lua.h
 ** See Copyright Notice in lua.h
 */
 */
@@ -32,9 +32,9 @@
 ** when traversing the respective threads, but the thread may already be
 ** when traversing the respective threads, but the thread may already be
 ** dead, while the upvalue is still accessible through closures.)
 ** 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 */
   lu_byte gckind;  /* kind of GC running */
   int sweepstrgc;  /* position of sweep in `strt' */
   int sweepstrgc;  /* position of sweep in `strt' */
   GCObject *allgc;  /* list of all collectable objects */
   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 **sweepgc;  /* current position of sweep */
   GCObject *gray;  /* list of gray objects */
   GCObject *gray;  /* list of gray objects */
   GCObject *grayagain;  /* list of objects to be traversed atomically */
   GCObject *grayagain;  /* list of objects to be traversed atomically */

+ 11 - 10
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
 ** Internal Module for Debugging of the Lua Implementation
 ** See Copyright Notice in lua.h
 ** See Copyright Notice in lua.h
 */
 */
@@ -429,20 +429,21 @@ int lua_checkmemory (lua_State *L) {
     checkobject(g, o);
     checkobject(g, o);
     lua_assert(!testbit(o->gch.marked, SEPARATED));
     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);
     checkobject(g, o);
   }
   }
   /* check 'tobefnz' list */
   /* check 'tobefnz' list */
   checkold(g, g->tobefnz);
   checkold(g, g->tobefnz);
   for (o = g->tobefnz; o != NULL; o = gch(o)->next) {
   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 */
   /* check 'uvhead' list */
   for (uv = g->uvhead.u.l.next; uv != &g->uvhead; uv = uv->u.l.next) {
   for (uv = g->uvhead.u.l.next; uv != &g->uvhead; uv = uv->u.l.next) {