浏览代码

local collection now calls finalizers

Roberto Ierusalimschy 12 年之前
父节点
当前提交
aeff4f79fa
共有 6 个文件被更改,包括 144 次插入80 次删除
  1. 16 3
      bugs
  2. 87 33
      lgc.c
  3. 6 5
      lgc.h
  4. 2 4
      lstate.c
  5. 3 2
      lstate.h
  6. 30 33
      ltests.c

+ 16 - 3
bugs

@@ -1880,8 +1880,8 @@ patch = [[
 +++ lundump.c   2008/04/04 19:51:41     2.7.1.4
 @@ -1,5 +1,5 @@
  /*
--** $Id: bugs,v 1.125 2013/07/05 18:02:28 roberto Exp roberto $
-+** $Id: bugs,v 1.125 2013/07/05 18:02:28 roberto Exp roberto $
+-** $Id: bugs,v 1.126 2013/08/30 15:51:12 roberto Exp roberto $
++** $Id: bugs,v 1.126 2013/08/30 15:51:12 roberto Exp roberto $
  ** load precompiled Lua chunks
  ** See Copyright Notice in lua.h
  */
@@ -3133,7 +3133,20 @@ patch = [[
    return ts;
 ]]
 }
-]=]
+]
+
+
+Bug{
+what = [[Call to macro 'luai_userstateclose' should be done only
+after the calls to __gc methods.]],
+report = [[Jean-Luc Jumpertz, 2013/09/02]],
+since = [[ ]],
+fix = nil,
+example = [[No example]],
+patch = [[
+]]
+}
+]=]=]
 
 
 --[=[

+ 87 - 33
lgc.c

@@ -1,5 +1,5 @@
 /*
-** $Id: lgc.c,v 2.156 2013/08/29 13:34:16 roberto Exp roberto $
+** $Id: lgc.c,v 2.157 2013/08/30 19:14:26 roberto Exp roberto $
 ** Garbage Collector
 ** See Copyright Notice in lua.h
 */
@@ -777,8 +777,16 @@ static GCObject *udata2finalize (global_State *g) {
   GCObject *o = g->tobefnz;  /* get first element */
   lua_assert(tofinalize(o));
   g->tobefnz = gch(o)->next;  /* remove it from 'tobefnz' list */
-  gch(o)->next = g->allgc;  /* return it to 'allgc' list */
-  g->allgc = o;
+  if (islocal(o)) {
+    lua_assert(!testbit(gch(o)->marked, LOCALMARK));
+    gch(o)->next = g->localgc;  /* return it to 'localgc' list */
+    g->localgc = o;
+  }
+  else {  /* return it to 'allgc' list */
+    gch(o)->next = g->allgc;
+    g->allgc = o;
+    l_setbit(gch(o)->marked, LOCALMARK);
+  }
   resetbit(gch(o)->marked, FINALIZEDBIT);  /* object is back in 'allgc' */
   if (!keepinvariant(g))  /* not keeping invariant? */
     makewhite(g, o);  /* "sweep" object */
@@ -825,23 +833,38 @@ static void GCTM (lua_State *L, int propagateerrors) {
 
 
 /*
-** move all unreachable objects (or 'all' objects) that need
-** finalization from list 'finobj' to list 'tobefnz' (to be finalized)
+** call all pending finalizers
 */
-static void separatetobefnz (lua_State *L, int all) {
+static void callallpendingfinalizers (lua_State *L, int propagateerrors) {
   global_State *g = G(L);
-  GCObject **p = &g->finobj;
+  while (g->tobefnz)
+    GCTM(L, propagateerrors);
+}
+
+
+/*
+** find last 'next' field in list 'p' list (to add elements in its end)
+*/
+static GCObject **findlast (GCObject **p) {
+  while (*p != NULL)
+    p = &gch(*p)->next;
+  return p;
+}
+
+
+/*
+** move all unreachable objects (or 'all' objects) that need
+** finalization from list 'p' to list 'tobefnz' (to be finalized)
+*/
+static void separatetobefnz_aux (global_State *g, GCObject **p, int all) {
   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;
+  GCObject **lastnext = findlast(&g->tobefnz);
   while ((curr = *p) != NULL) {  /* traverse all finalizable objects */
     lua_assert(tofinalize(curr));
     if (!(iswhite(curr) || all))  /* not being collected? */
       p = &gch(curr)->next;  /* don't bother with it */
     else {
-      *p = gch(curr)->next;  /* remove 'curr' from 'finobj' list */
+      *p = gch(curr)->next;  /* remove 'curr' from "fin" list */
       gch(curr)->next = *lastnext;  /* link at the end of 'tobefnz' list */
       *lastnext = curr;
       lastnext = &gch(curr)->next;
@@ -850,9 +873,15 @@ static void separatetobefnz (lua_State *L, int all) {
 }
 
 
+static void separatetobefnz (global_State *g, int all) {
+  separatetobefnz_aux(g, &g->localfin, all);
+  separatetobefnz_aux(g, &g->finobj, all);
+}
+
+
 /*
 ** if object 'o' has a finalizer, remove it from 'allgc' list (must
-** search the list to find it) and link it in 'finobj' list.
+** search the list to find it) and link it in 'localfin' or 'finobj' list.
 */
 void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) {
   global_State *g = G(L);
@@ -869,11 +898,11 @@ void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) {
     /* search for pointer pointing to 'o' */
     p = (testbit(ho->marked, LOCALMARK)) ? &g->allgc : &g->localgc;
     for (; *p != o; p = &gch(*p)->next) { /* empty */ }
-    *p = ho->next;  /* remove 'o' from 'allgc' list */
-    ho->next = g->finobj;  /* link it in list 'finobj' */
-    g->finobj = o;
+    *p = ho->next;  /* remove 'o' from its list */
+    p = (testbit(ho->marked, LOCALMARK)) ? &g->finobj : &g->localfin;
+    ho->next = *p;  /* link it in a "fin" list */
+    *p = o;
     l_setbit(ho->marked, FINALIZEDBIT);  /* mark it as such */
-    l_setbit(ho->marked, LOCALMARK);  /* not in 'localgc' anymore */
     if (!keepinvariant(g))  /* not keeping invariant? */
       makewhite(g, o);  /* "sweep" object */
   }
@@ -918,7 +947,8 @@ static void localmark (global_State *g) {
 }
 
 
-static void localsweep (lua_State *L, global_State *g, GCObject **p) {
+static void localsweep (lua_State *L, global_State *g) {
+  GCObject **p = &g->localgc;
   while (*p != NULL) {
     GCObject *curr = *p;
     if (!islocal(curr)) {  /* is 'curr' no more local? */
@@ -946,11 +976,41 @@ static void localsweep (lua_State *L, global_State *g, GCObject **p) {
 }
 
 
+static void separatelocal (global_State *g, int all) {
+  GCObject **p = &g->localfin;
+  GCObject **lastnext = findlast(&g->tobefnz);
+  while (*p != NULL) {
+    GCObject *curr = *p;
+    if (!islocal(curr)) {  /* is 'curr' no more local? */
+      *p = curr->gch.next;  /* remove 'curr' from list */
+      curr->gch.next = g->finobj;  /* link 'curr' in 'finobj' list */
+      g->finobj = curr;
+      /* mark it as out of local list */
+      l_setbit(curr->gch.marked, LOCALMARK);
+    }
+    else {  /* still local */
+      if (testbit(curr->gch.marked, LOCALMARK) && !all) {  /* locally alive? */
+        resetbit(curr->gch.marked, LOCALMARK);
+        p = &curr->gch.next;  /* go to next element */
+      }
+      else {  /* object is "dead" */
+        *p = curr->gch.next;  /* remove 'curr' from list */
+        curr->gch.next = *lastnext;  /* link at the end of 'tobefnz' list */
+        *lastnext = curr;
+        lastnext = &curr->gch.next;
+      }
+    }
+  }
+}
+
+
 static void luaC_localcollection (lua_State *L) {
   global_State *g = G(L);
   lua_assert(g->gcstate == GCSpause);
   localmark(g);
-  localsweep(L, g, &g->localgc);
+  localsweep(L, g);
+  separatelocal(g, 0);
+  callallpendingfinalizers(L, 1);
 }
 
 /* }====================================================== */
@@ -997,24 +1057,15 @@ static int entersweep (lua_State *L) {
 }
 
 
-/*
-** call all pending finalizers
-*/
-static void callallpendingfinalizers (lua_State *L, int propagateerrors) {
-  global_State *g = G(L);
-  while (g->tobefnz)
-    GCTM(L, propagateerrors);
-}
-
-
 void luaC_freeallobjects (lua_State *L) {
   global_State *g = G(L);
-  separatetobefnz(L, 1);  /* separate all objects with finalizers */
-  lua_assert(g->finobj == NULL);
+  separatetobefnz(g, 1);  /* separate all objects with finalizers */
+  lua_assert(g->finobj == NULL && g->localfin == NULL);
   callallpendingfinalizers(L, 0);
   g->currentwhite = WHITEBITS; /* this "white" makes all objects look dead */
   g->gckind = KGC_NORMAL;
-  sweepwholelist(L, &g->finobj);  /* finalizers can create objs. in 'finobj' */
+  sweepwholelist(L, &g->localfin);  /* finalizers can create objs. with fins. */
+  sweepwholelist(L, &g->finobj);
   sweepwholelist(L, &g->localgc);
   sweepwholelist(L, &g->allgc);
   sweepwholelist(L, &g->fixedgc);  /* collect fixed objects */
@@ -1045,7 +1096,7 @@ static l_mem atomic (lua_State *L) {
   clearvalues(g, g->allweak, NULL);
   origweak = g->weak; origall = g->allweak;
   work += g->GCmemtrav;  /* stop counting (objects being finalized) */
-  separatetobefnz(L, 0);  /* separate objects to be finalized */
+  separatetobefnz(g, 0);  /* separate objects to be finalized */
   markbeingfnz(g);  /* mark objects that will be finalized */
   propagateall(g);  /* remark, to propagate `preserveness' */
   work -= g->GCmemtrav;  /* restart counting */
@@ -1106,6 +1157,9 @@ static lu_mem singlestep (lua_State *L) {
       return work + sw * GCSWEEPCOST;
     }
     case GCSsweeplocal: {
+      return sweepstep(L, g, GCSsweeplocfin, &g->localfin);
+    }
+    case GCSsweeplocfin: {
       return sweepstep(L, g, GCSsweepfin, &g->finobj);
     }
     case GCSsweepfin: {

+ 6 - 5
lgc.h

@@ -1,5 +1,5 @@
 /*
-** $Id: lgc.h,v 2.69 2013/08/29 13:49:57 roberto Exp roberto $
+** $Id: lgc.h,v 2.70 2013/08/30 19:14:26 roberto Exp roberto $
 ** Garbage Collector
 ** See Copyright Notice in lua.h
 */
@@ -39,10 +39,11 @@
 #define GCSpropagate	0
 #define GCSatomic	1
 #define GCSsweeplocal	2
-#define GCSsweepfin	3
-#define GCSsweepall	4
-#define GCSsweepmainth	5
-#define GCSpause	6
+#define GCSsweeplocfin	3
+#define GCSsweepfin	4
+#define GCSsweepall	5
+#define GCSsweepmainth	6
+#define GCSpause	7
 
 
 #define issweepphase(g)  \

+ 2 - 4
lstate.c

@@ -1,5 +1,5 @@
 /*
-** $Id: lstate.c,v 2.108 2013/08/28 18:30:26 roberto Exp roberto $
+** $Id: lstate.c,v 2.109 2013/08/30 19:14:26 roberto Exp roberto $
 ** Global State
 ** See Copyright Notice in lua.h
 */
@@ -296,9 +296,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
   g->panic = NULL;
   g->version = lua_version(NULL);
   g->gcstate = GCSpause;
-  g->allgc = NULL;
-  g->localgc = NULL;
-  g->finobj = NULL;
+  g->localgc = g->localfin = g->allgc = g->finobj = NULL;
   g->tobefnz = NULL;
   g->fixedgc = NULL;
   g->sweepgc = NULL;

+ 3 - 2
lstate.h

@@ -1,5 +1,5 @@
 /*
-** $Id: lstate.h,v 2.91 2013/08/27 18:53:35 roberto Exp roberto $
+** $Id: lstate.h,v 2.92 2013/08/30 19:14:26 roberto Exp roberto $
 ** Global State
 ** See Copyright Notice in lua.h
 */
@@ -121,8 +121,9 @@ typedef struct global_State {
   lu_byte gcrunning;  /* true if GC is running */
   GCObject *allgc;  /* list of all collectable objects */
   GCObject *localgc;  /* list of local objects */
-  GCObject *finobj;  /* list of collectable objects with finalizers */
+  GCObject *localfin;  /* list of local objects with finalizers */
   GCObject **sweepgc;  /* current position of sweep in list */
+  GCObject *finobj;  /* list of collectable objects with finalizers */
   GCObject *gray;  /* list of gray objects */
   GCObject *grayagain;  /* list of objects to be traversed atomically */
   GCObject *weak;  /* list of tables with weak values */

+ 30 - 33
ltests.c

@@ -1,5 +1,5 @@
 /*
-** $Id: ltests.c,v 2.151 2013/08/27 20:04:00 roberto Exp roberto $
+** $Id: ltests.c,v 2.152 2013/08/30 19:14:26 roberto Exp roberto $
 ** Internal Module for Debugging of the Lua Implementation
 ** See Copyright Notice in lua.h
 */
@@ -202,17 +202,8 @@ static int testobjref2 (GCObject *f, GCObject *t) {
 
 
 static void printobj (global_State *g, GCObject *o) {
-  int i = 1;
-  GCObject *p;
-  for (p = g->allgc; p != o && p != NULL; p = gch(p)->next) i++;
-  if (p == NULL) {
-    i = 1;
-    for (p = g->finobj; p != o && p != NULL; p = gch(p)->next) i++;
-    if (p == NULL) i = 0;  /* zero means 'not found' */
-    else i = -i;  /* negative means 'found in findobj list */
-  }
-  printf("||%d:%s(%p)-%s-%c(%02X)||",
-           i, ttypename(novariant(gch(o)->tt)), (void *)o,
+  printf("||%s(%p)-%s-%c(%02X)||",
+           ttypename(novariant(gch(o)->tt)), (void *)o,
            islocal(o)?"L":"NL",
            isdead(g,o)?'d':isblack(o)?'b':iswhite(o)?'w':'g', gch(o)->marked);
 }
@@ -344,9 +335,10 @@ static void checkstack (global_State *g, lua_State *L1) {
 
 static void checkobject (global_State *g, GCObject *o, int maybedead) {
   if (isdead(g, o))
-    lua_assert(maybedead);
+    lua_assert(maybedead && issweepphase(g));
   else {
     lua_assert(g->gcstate != GCSpause || iswhite(o));
+    lua_assert(!islocal(o) || !testbit(gch(o)->marked, LOCALMARK));
     switch (gch(o)->tt) {
       case LUA_TUSERDATA: {
         Table *mt = gco2u(o)->metatable;
@@ -384,18 +376,18 @@ static void checkobject (global_State *g, GCObject *o, int maybedead) {
 
 #define TESTGRAYBIT		7
 
-static void checkgraylist (global_State *g, GCObject *l) {
-  UNUSED(g);  /* better to keep it available if we need to print an object */
-  while (l) {
-    lua_assert(isgray(l));
-    lua_assert(!testbit(l->gch.marked, TESTGRAYBIT));
-    l_setbit(l->gch.marked, TESTGRAYBIT);
-    switch (gch(l)->tt) {
-      case LUA_TTABLE: l = gco2t(l)->gclist; break;
-      case LUA_TLCL: l = gco2lcl(l)->gclist; break;
-      case LUA_TCCL: l = gco2ccl(l)->gclist; break;
-      case LUA_TTHREAD: l = gco2th(l)->gclist; break;
-      case LUA_TPROTO: l = gco2p(l)->gclist; break;
+static void checkgraylist (global_State *g, GCObject *o) {
+  ((void)g);  /* better to keep it available if we need to print an object */
+  while (o) {
+    lua_assert(isgray(o));
+    lua_assert(!testbit(o->gch.marked, TESTGRAYBIT));
+    l_setbit(o->gch.marked, TESTGRAYBIT);
+    switch (gch(o)->tt) {
+      case LUA_TTABLE: o = gco2t(o)->gclist; break;
+      case LUA_TLCL: o = gco2lcl(o)->gclist; break;
+      case LUA_TCCL: o = gco2ccl(o)->gclist; break;
+      case LUA_TTHREAD: o = gco2th(o)->gclist; break;
+      case LUA_TPROTO: o = gco2p(o)->gclist; break;
       default: lua_assert(0);  /* other objects cannot be gray */
     }
   }
@@ -451,30 +443,35 @@ int lua_checkmemory (lua_State *L) {
     if (gch(o)->tt == LUA_TTHREAD) isthread = 1;  /* now travesing threads... */
     else lua_assert(!isthread);  /* ... and only threads */
     checkobject(g, o, maybedead);
-    lua_assert(!tofinalize(o));
-    lua_assert(testbit(o->gch.marked, LOCALMARK));
+    lua_assert(!tofinalize(o) && testbit(o->gch.marked, LOCALMARK));
   }
   /* check 'finobj' list */
   checkgray(g, g->finobj);
   for (o = g->finobj; o != NULL; o = gch(o)->next) {
-    lua_assert(tofinalize(o));
+    checkobject(g, o, 0);
+    lua_assert(tofinalize(o) && testbit(o->gch.marked, LOCALMARK));
     lua_assert(gch(o)->tt == LUA_TUSERDATA ||
                gch(o)->tt == LUA_TTABLE);
-    checkobject(g, o, 0);
   }
   /* check 'tobefnz' list */
   checkgray(g, g->tobefnz);
   for (o = g->tobefnz; o != NULL; o = gch(o)->next) {
     lua_assert(!iswhite(o) || g->gcstate == GCSpause);
     lua_assert(!isdead(g, o) && tofinalize(o));
-    lua_assert(gch(o)->tt == LUA_TUSERDATA ||
-               gch(o)->tt == LUA_TTABLE);
+    lua_assert(gch(o)->tt == LUA_TUSERDATA || gch(o)->tt == LUA_TTABLE);
   }
   /* check 'localgc' list */
   checkgray(g, g->localgc);
   for (o = g->localgc; o != NULL; o = gch(o)->next) {
     checkobject(g, o, 1);
-    lua_assert(!testbit(o->gch.marked, LOCALMARK));
+    lua_assert(!tofinalize(o) && !testbit(o->gch.marked, LOCALMARK));
+  }
+  /* check 'localfin' list */
+  checkgray(g, g->localfin);
+  for (o = g->localfin; o != NULL; o = gch(o)->next) {
+    checkobject(g, o, 0);
+    lua_assert(tofinalize(o) && !testbit(o->gch.marked, LOCALMARK));
+    lua_assert(gch(o)->tt == LUA_TUSERDATA || gch(o)->tt == LUA_TTABLE);
   }
   return 0;
 }
@@ -653,7 +650,7 @@ static int gc_local (lua_State *L) {
 
 static int gc_state (lua_State *L) {
   static const char *statenames[] = {"propagate", "atomic",
-    "sweeplocal", "sweepfin", "sweepall", "sweepmainth",
+    "sweeplocal", "sweeplocfin", "sweepfin", "sweepall", "sweepmainth",
     "pause", ""};
   int option = luaL_checkoption(L, 1, "", statenames);
   if (option == GCSpause + 1) {