Browse Source

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

Roberto Ierusalimschy 17 years ago
parent
commit
0c8f5fc2fd
5 changed files with 89 additions and 100 deletions
  1. 34 72
      lgc.c
  2. 14 16
      lgc.h
  3. 4 4
      lstate.c
  4. 28 3
      lstate.h
  5. 9 5
      ltests.c

+ 34 - 72
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));

+ 14 - 16
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);

+ 4 - 4
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;

+ 28 - 3
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 */

+ 9 - 5
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);