瀏覽代碼

generational collector (still not complete)

Roberto Ierusalimschy 8 年之前
父節點
當前提交
e4287da3a6
共有 4 個文件被更改,包括 324 次插入102 次删除
  1. 276 83
      lgc.c
  2. 39 15
      lgc.h
  3. 3 2
      lstate.c
  4. 6 2
      lstate.h

+ 276 - 83
lgc.c

@@ -1,5 +1,5 @@
 /*
-** $Id: lgc.c,v 2.215 2016/12/22 13:08:50 roberto Exp roberto $
+** $Id: lgc.c,v 2.216 2017/02/23 21:07:34 roberto Exp roberto $
 ** Garbage Collector
 ** See Copyright Notice in lua.h
 */
@@ -9,7 +9,7 @@
 
 #include "lprefix.h"
 
-
+#include <stdio.h>
 #include <string.h>
 
 #include "lua.h"
@@ -26,12 +26,6 @@
 #include "ltm.h"
 
 
-/*
-** internal state for collector while inside the atomic phase. The
-** collector should never be in this state while running regular code.
-*/
-#define GCSinsideatomic		(GCSpause + 1)
-
 /*
 ** cost of sweeping one element (the size of a small object divided
 ** by some adjust for the sweep speed)
@@ -59,8 +53,9 @@
 #define PAUSEADJ		100
 
 
-/* mask to erase all color bits */
-#define maskcolors	(~(bitmask(BLACKBIT) | WHITEBITS))
+/* mask to erase all color bits (plus gen. related stuff) */
+#define maskcolors	(~(bitmask(BLACKBIT) | WHITEBITS | AGEBITS))
+
 
 /* macro to erase all color bits then sets only the current white bit */
 #define makewhite(g,x)	\
@@ -157,8 +152,10 @@ void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) {
   lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o));
   if (keepinvariant(g)) {  /* must keep invariant? */
     reallymarkobject(g, v);  /* restore invariant */
-    if (isold(o))
-      l_setbit((v)->marked, OLDBIT);
+    if (isold(o)) {
+      lua_assert(!isold(v));
+      setage(v, G_OLD0);
+    }
   }
   else {  /* sweep phase */
     lua_assert(issweepphase(g));
@@ -174,8 +171,12 @@ void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) {
 void luaC_barrierback_ (lua_State *L, Table *t) {
   global_State *g = G(L);
   lua_assert(isblack(t) && !isdead(g, t));
+  lua_assert(issweepphase(g) || getage(t) != G_TOUCHED1);
+  lua_assert(g->gckind != KGC_GEN || isold(t));
+  if (getage(t) != G_TOUCHED2)  /* not already in gray list? */
+    linkgclist(t, g->grayagain);  /* link it in 'grayagain' */
   black2gray(t);  /* make table gray (again) */
-  linkgclist(t, g->grayagain);
+  setage(t, G_TOUCHED1);  /* touched in current cycle */
 }
 
 
@@ -188,10 +189,10 @@ void luaC_barrierback_ (lua_State *L, Table *t) {
 void luaC_upvalbarrier_ (lua_State *L, UpVal *uv) {
   global_State *g = G(L);
   GCObject *o = gcvalue(uv->v);
-  lua_assert(!upisopen(uv));  /* ensured by macro luaC_upvalbarrier */
   if (keepinvariant(g)) {
     markobject(g, o);
-    l_setbit((o)->marked, OLDBIT);
+    if (!isold(o))
+      setage(o, G_OLD0);
   }
 }
 
@@ -379,10 +380,10 @@ static void traverseweakvalue (global_State *g, Table *h) {
         hasclears = 1;  /* table will have to be cleared */
     }
   }
-  if (g->gcstate == GCSpropagate)
-    linkgclist(h, g->grayagain);  /* must retraverse it in atomic phase */
-  else if (hasclears)
+  if (g->gcstate == GCSatomic && hasclears)
     linkgclist(h, g->weak);  /* has to be cleared later */
+  else
+    linkgclist(h, g->grayagain);  /* must retraverse it in atomic phase */
 }
 
 
@@ -431,6 +432,8 @@ static int traverseephemeron (global_State *g, Table *h) {
     linkgclist(h, g->ephemeron);  /* have to propagate again */
   else if (hasclears)  /* table has white keys? */
     linkgclist(h, g->allweak);  /* may have to clean white keys */
+  else if (g->gckind == KGC_GEN)
+    linkgclist(h, g->grayagain);  /* keep it in some list */
   return marked;
 }
 
@@ -450,6 +453,10 @@ static void traversestrongtable (global_State *g, Table *h) {
       markvalue(g, gval(n));  /* mark value */
     }
   }
+  if (g->gckind == KGC_GEN) {
+    linkgclist(h, g->grayagain);  /* keep it in some gray list */
+    black2gray(h);
+  }
 }
 
 
@@ -522,7 +529,7 @@ static lu_mem traverseLclosure (global_State *g, LClosure *cl) {
   for (i = 0; i < cl->nupvalues; i++) {  /* mark its upvalues */
     UpVal *uv = cl->upvals[i];
     if (uv != NULL) {
-      if (upisopen(uv) && g->gcstate != GCSinsideatomic)
+      if (upisopen(uv) && g->gcstate != GCSatomic)
         uv->u.open.touched = 1;  /* can be marked in 'remarkupvals' */
       else
         markvalue(g, uv->v);
@@ -536,11 +543,11 @@ static lu_mem traversethread (global_State *g, lua_State *th) {
   StkId o = th->stack;
   if (o == NULL)
     return 1;  /* stack not completely built yet */
-  lua_assert(g->gcstate == GCSinsideatomic ||
+  lua_assert(g->gcstate == GCSatomic ||
              th->openupval == NULL || isintwups(th));
   for (; o < th->top; o++)  /* mark live elements in the stack */
     markvalue(g, o);
-  if (g->gcstate == GCSinsideatomic) {  /* final traversal? */
+  if (g->gcstate == GCSatomic) {  /* final traversal? */
     StkId lim = th->stack + th->stacksize;  /* real end of stack */
     for (; o < lim; o++)  /* clear not-marked stack slice */
       setnilvalue(o);
@@ -564,7 +571,7 @@ static lu_mem traversethread (global_State *g, lua_State *th) {
 static void propagatemark (global_State *g) {
   lu_mem size;
   GCObject *o = g->gray;
-  lua_assert(isgray(o));
+  lua_assert(ongraylist(o));
   gray2black(o);
   switch (o->tt) {
     case LUA_TTABLE: {
@@ -638,11 +645,10 @@ static void convergeephemerons (global_State *g) {
 
 
 /*
-** clear entries with unmarked keys from all weaktables in list 'l' up
-** to element 'f'
+** clear entries with unmarked keys from all weaktables in list 'l'
 */
-static void clearkeys (global_State *g, GCObject *l, GCObject *f) {
-  for (; l != f; l = gco2t(l)->gclist) {
+static void clearkeys (global_State *g, GCObject *l) {
+  for (; l; l = gco2t(l)->gclist) {
     Table *h = gco2t(l);
     Node *n, *limit = gnodelast(h);
     for (n = gnode(h, 0); n < limit; n++) {
@@ -885,11 +891,13 @@ static void separatetobefnz (global_State *g, int all) {
   GCObject *curr;
   GCObject **p = &g->finobj;
   GCObject **lastnext = findlast(&g->tobefnz);
-  while ((curr = *p) != NULL) {  /* traverse all finalizable objects */
+  while ((curr = *p) != g->finobjold) {  /* traverse all finalizable objects */
     lua_assert(tofinalize(curr));
     if (!(iswhite(curr) || all))  /* not being collected? */
       p = &curr->next;  /* don't bother with it */
     else {
+      if (curr == g->finobjsur)
+        g->finobjsur = curr->next;
       *p = curr->next;  /* remove 'curr' from 'finobj' list */
       curr->next = *lastnext;  /* link at the end of 'tobefnz' list */
       *lastnext = curr;
@@ -915,6 +923,14 @@ void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) {
       if (g->sweepgc == &o->next)  /* should not remove 'sweepgc' object */
         g->sweepgc = sweeptolive(L, g->sweepgc);  /* change 'sweepgc' */
     }
+    else {
+      if (o == g->survival)
+        g->survival = o->next;
+      if (o == g->old)
+        g->old = o->next;
+      if (o == g->reallyold)
+        g->reallyold = o->next;
+    }
     /* search for pointer pointing to 'o' */
     for (p = &g->allgc; *p != o; p = &(*p)->next) { /* empty */ }
     *p = o->next;  /* remove 'o' from 'allgc' list */
@@ -934,31 +950,65 @@ void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) {
 */
 
 
+/* mask to erase all color bits (not changing gen-related stuff) */
+#define maskgencolors	(~(bitmask(BLACKBIT) | WHITEBITS))
+
 #if 0
 static int count (GCObject *p, GCObject *limit) {
   int res = 0;
-  for (; p != NULL && p != limit; p = p->next)
+  for (; p != NULL && p != limit; p = p->next) {
     res++;
+  }
   return res;
 }
 #endif
 
 
-static GCObject **sweepgen (lua_State *L, GCObject **p, GCObject *limit,
-                            int zeromask, int onemask) {
-  global_State *g = G(L);
-  int ow = otherwhite(g);
+static void sweep2old (lua_State *L, GCObject **p) {
+  GCObject *curr;
+  while ((curr = *p) != NULL) {
+    if (iswhite(curr)) {  /* is 'curr' dead? */
+      lua_assert(isdead(G(L), curr));
+      *p = curr->next;  /* remove 'curr' from list */
+      freeobj(L, curr);  /* erase 'curr' */
+    }
+    else {  /* all surviving objects become old */
+      setage(curr, G_OLD);
+      p = &curr->next;  /* go to next element */
+    }
+  }
+}
+
+
+static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p,
+                            GCObject *limit) {
+  int white = luaC_white(g);
   GCObject *curr;
   while ((curr = *p) != limit) {
     int marked = curr->marked;
-    if (isdeadm(ow, marked)) {  /* is 'curr' dead? */
-      lua_assert(!isold(curr));
+    if (iswhite(curr)) {  /* is 'curr' dead? */
+      lua_assert(!isold(curr) && !testbits(curr->marked, white));
       *p = curr->next;  /* remove 'curr' from list */
       freeobj(L, curr);  /* erase 'curr' */
     }
-    else {  /* correct mark */
-      if (!isold(curr))  /* don't change old objects */
-        curr->marked = cast_byte((marked & zeromask) | onemask);
+    else {  /* correct mark and age */
+      switch (getage(curr)) {
+        case G_NEW:  /* make white and go to next age */
+          curr->marked = cast_byte((marked & maskgencolors) | white);
+          changeage(curr, G_NEW, G_SURVIVAL);
+          break;
+        case G_SURVIVAL:  /* go to next age */
+          changeage(curr, G_SURVIVAL, G_OLD1);
+          break;
+        case G_OLD0:  /* go to next age */
+          changeage(curr, G_OLD0, G_OLD1);
+          break;
+        case G_OLD1:  /* go to next age */
+          changeage(curr, G_OLD1, G_OLD);
+          break;
+        default:  /* don't change 'old', 'touched1', and 'touched2' */
+          break;
+      }
       p = &curr->next;  /* go to next element */
     }
   }
@@ -966,51 +1016,184 @@ static GCObject **sweepgen (lua_State *L, GCObject **p, GCObject *limit,
 }
 
 
-static void startgencycle (lua_State *L, global_State *g) {
-  propagateall(g);
-  atomic(L);
+static void whitelist (global_State *g, GCObject *p) {
+  int white = luaC_white(g);
+  for (; p != NULL; p = p->next)
+    p->marked = cast_byte((p->marked & maskcolors) | white);
 }
 
 
-static void finishgencycle (lua_State *L, global_State *g, int mask) {
-  sweepgen(L, &g->finobj, NULL, ~0, mask);
-  sweepgen(L, &g->tobefnz, NULL, ~0, mask);
+static void finishgencycle (lua_State *L, global_State *g) {
+  // sweepgen(L, &g->tobefnz, ~0, mask);
   checkSizes(L, g);
   g->gcstate = GCSpropagate;  /* skip restart */
   callallpendingfinalizers(L);
 }
 
+static void printgray (GCObject *o) {
+  printf("gray:  ");
+  while (o) {
+    printf("%p %d %02x  ", (void*)o, o->tt, o->marked);
+    switch (o->tt) {
+      case LUA_TTABLE: {
+        Table *h = gco2t(o);
+        o = h->gclist;
+        break;
+      }
+      case LUA_TLCL: {
+        LClosure *cl = gco2lcl(o);
+        o = cl->gclist;
+        break;
+      }
+      case LUA_TCCL: {
+        CClosure *cl = gco2ccl(o);
+        o = cl->gclist;
+        break;
+      }
+      case LUA_TTHREAD: {
+        lua_State *th = gco2th(o);
+        o = th->gclist;
+        break;
+      }
+      case LUA_TPROTO: {
+        Proto *p = gco2p(o);
+        o = p->gclist;
+        break;
+      }
+      default: lua_assert(0); return;
+    }
+  }
+  printf("\n");
+}
+
+
+
+static GCObject **correctgraylist (GCObject **p) {
+  GCObject *curr;
+  while ((curr = *p) != NULL) {
+    switch (curr->tt) {
+      case LUA_TTABLE: {
+        Table *h = gco2t(curr);
+        if (getage(h) == G_TOUCHED1) {  /* touched in this cycle? */
+          lua_assert(isgray(h));
+          gray2black(h);  /* make it black, for next barrier */
+          changeage(h, G_TOUCHED1, G_TOUCHED2);
+          p = &h->gclist;  /* go to next element */
+        }
+        else {
+          if (!iswhite(h)) {
+            lua_assert(isold(h));
+            if (getage(h) == G_TOUCHED2)
+              changeage(h, G_TOUCHED2, G_OLD);
+            gray2black(h);  /* make it black */
+          }
+          *p = h->gclist;  /* remove 'curr' from gray list */
+        }
+        break;
+      }
+      case LUA_TTHREAD: {
+        lua_State *th = gco2th(curr);
+        lua_assert(!isblack(th));
+        if (iswhite(th))  /* new object? */
+          *p = th->gclist;  /* remove from gray list */
+        else  /* old threads remain gray */
+          p = &th->gclist;  /* go to next element */
+        break;
+      }
+      default: lua_assert(0);  /* nothing more could be gray here */
+    }
+  }
+  return p;
+}
+
+
+static void correctgraylists (global_State *g) {
+  GCObject **list = correctgraylist(&g->grayagain);
+  *list = g->weak; g->weak = NULL;
+  list = correctgraylist(list);
+  *list = g->allweak; g->allweak = NULL;
+  list = correctgraylist(list);
+  *list = g->ephemeron; g->ephemeron = NULL;
+  correctgraylist(list);
+}
+
+
+static void markold (global_State *g, GCObject *from, GCObject *to) {
+  GCObject *p;
+  for (p = from; p != to; p = p->next) {
+    if (getage(p) == G_OLD1) {
+      lua_assert(!iswhite(p));
+      if (isblack(p)) {
+        black2gray(p);  /* should be '2white', but gray works too */
+        reallymarkobject(g, p);
+      }
+      else
+        lua_assert(p->tt == LUA_TTHREAD);  /* threads are always gray */
+    }
+  }
+}
+
 
 static void youngcollection (lua_State *L, global_State *g) {
   GCObject **psurvival;
   lua_assert(g->gcstate == GCSpropagate);
-  startgencycle(L, g);
+  markold(g, g->survival, g->reallyold);
+  markold(g, g->finobj, g->finobjrold);  /* ??? */
+  atomic(L);
+
   /* sweep nursery */
-  psurvival = sweepgen(L, &g->allgc, g->survival, maskcolors, luaC_white(g));
-  lua_assert(*psurvival == g->survival);
-  /* sweep 'survival' list, making elements old */
-  sweepgen(L, psurvival, g->old, ~0, bitmask(OLDBIT));
-  /* incorporate 'survival' list into old list */
-  g->old = *psurvival;
-  /* surviving young objects go to 'survival' list */
-  g->survival = g->allgc;
-  finishgencycle(L, g, 0);
-lua_checkmemory(L);
+  psurvival = sweepgen(L, g, &g->allgc, g->survival);
+  /* sweep 'survival' and 'old' */
+  sweepgen(L, g, psurvival, g->reallyold);
+  g->reallyold = g->old;
+  g->old = *psurvival;  /* 'survival' survivals are old now */
+  g->survival = g->allgc;  /* all news are survivals */
+
+  /* repeat for 'finobj' lists */
+  psurvival = sweepgen(L, g, &g->finobj, g->finobjsur);
+  /* sweep 'survival' and 'old' */
+  sweepgen(L, g, psurvival, g->finobjrold);
+  g->finobjrold = g->finobjold;
+  g->finobjold = *psurvival;  /* 'survival' survivals are old now */
+  g->finobjsur = g->finobj;  /* all news are survivals */
+
+  sweepgen(L, g, &g->tobefnz, NULL);
+
+  finishgencycle(L, g);
+  correctgraylists(g);
+//printf("check: \n");lua_checkmemory(L);
 }
 
 
 static void entergen (lua_State *L, global_State *g) {
-lua_checkmemory(L);
-  lua_assert(g->old == NULL && g->survival == NULL);
+  lua_assert(g->reallyold == NULL && g->old == NULL && g->survival == NULL);
   luaC_runtilstate(L, bitmask(GCSpause));  /* prepare to start a new cycle */
   luaC_runtilstate(L, bitmask(GCSpropagate));  /* start new cycle */
-  startgencycle(L, g);
+  atomic(L);
   /* sweep all ellements making them old */
-  sweepgen(L, &g->allgc, g->survival, ~0, bitmask(OLDBIT));
-  /* everything alive now is old; 'survival' is empty */
-  g->old = g->survival = g->allgc;
-  finishgencycle(L, g, bitmask(OLDBIT));
-lua_checkmemory(L);
+  sweep2old(L, &g->allgc);
+  /* everything alive now is old */
+  g->reallyold = g->old = g->survival = g->allgc;
+
+  /* repeat for 'finobj' lists */
+  sweep2old(L, &g->finobj);
+  g->finobjrold = g->finobjold = g->finobjsur = g->finobj;
+
+  finishgencycle(L, g);
+  correctgraylists(g);
+  g->gckind = KGC_GEN;
+}
+
+
+static void enterinc (global_State *g) {
+  makewhite(g, g->mainthread);
+  whitelist(g, g->allgc);
+  g->reallyold = g->old = g->survival = NULL;
+  whitelist(g, g->finobj);
+  g->finobjrold = g->finobjold = g->finobjsur = NULL;
+  lua_assert(g->tobefnz == NULL);  /* no need to sweep */
+  g->gcstate = GCSpause;
+  g->gckind = KGC_NORMAL;
 }
 
 
@@ -1019,17 +1202,18 @@ void luaC_changemode (lua_State *L, int newmode) {
   if (newmode != g->gckind) {  /* otherwise, nothing to be done */
     if (newmode == KGC_GEN)  /* entering generational mode? */
       entergen(L, g);
-    else {  /* entering incremental mode */
-lua_checkmemory(L);
-      youngcollection(L, g);
-      g->old = g->survival = NULL;
-lua_checkmemory(L);
-    }
-    g->gckind = newmode;
+    else
+      enterinc(g);  /* entering incremental mode */
   }
 }
 
 
+static void fullgen (lua_State *L, global_State *g) {
+  enterinc(g);
+  entergen(L, g);
+}
+
+
 static void genstep (lua_State *L, global_State *g) {
   lu_mem mem;
   youngcollection(L, g);
@@ -1094,10 +1278,10 @@ static void deletealllist (lua_State *L, GCObject *p) {
 
 void luaC_freeallobjects (lua_State *L) {
   global_State *g = G(L);
+  luaC_changemode(L, KGC_NORMAL);
   separatetobefnz(g, 1);  /* separate all objects with finalizers */
   lua_assert(g->finobj == NULL);
   callallpendingfinalizers(L);
-  lua_assert(g->tobefnz == NULL);
   deletealllist(L, g->finobj);
   deletealllist(L, g->allgc);
   deletealllist(L, g->fixedgc);  /* collect fixed objects */
@@ -1110,9 +1294,10 @@ static l_mem atomic (lua_State *L) {
   l_mem work;
   GCObject *origweak, *origall;
   GCObject *grayagain = g->grayagain;  /* save original list */
+  g->grayagain = NULL;
   lua_assert(g->ephemeron == NULL && g->weak == NULL);
   lua_assert(!iswhite(g->mainthread));
-  g->gcstate = GCSinsideatomic;
+  g->gcstate = GCSatomic;
   g->GCmemtrav = 0;  /* start counting work */
   markobject(g, L);  /* mark running thread */
   /* registry and global metatables may be changed by API */
@@ -1123,7 +1308,6 @@ static l_mem atomic (lua_State *L) {
   propagateall(g);  /* propagate changes */
   work = g->GCmemtrav;  /* stop counting (do not recount 'grayagain') */
   g->gray = grayagain;
-  g->grayagain = NULL;
   propagateall(g);  /* traverse 'grayagain' list */
   g->GCmemtrav = 0;  /* restart counting */
   convergeephemerons(g);
@@ -1141,13 +1325,14 @@ static l_mem atomic (lua_State *L) {
   convergeephemerons(g);
   /* at this point, all resurrected objects are marked. */
   /* remove dead objects from weak tables */
-  clearkeys(g, g->ephemeron, NULL);  /* clear keys from all ephemeron tables */
-  clearkeys(g, g->allweak, NULL);  /* clear keys from all 'allweak' tables */
+  clearkeys(g, g->ephemeron);  /* clear keys from all ephemeron tables */
+  clearkeys(g, g->allweak);  /* clear keys from all 'allweak' tables */
   /* clear values from resurrected weak tables */
   clearvalues(g, g->weak, origweak);
   clearvalues(g, g->allweak, origall);
   luaS_clearcache(g);
   g->currentwhite = cast_byte(otherwhite(g));  /* flip current white */
+  lua_assert(g->gray == NULL);
   work += g->GCmemtrav;  /* complete counting */
   return work;  /* estimate of memory marked by 'atomic' */
 }
@@ -1181,12 +1366,12 @@ static lu_mem singlestep (lua_State *L) {
     case GCSpropagate: {
       g->GCmemtrav = 0;
       if (g->gray == NULL)  /* no more gray objects? */
-        g->gcstate = GCSatomic;  /* finish propagate phase */
+        g->gcstate = GCSenteratomic;  /* finish propagate phase */
       else
         propagatemark(g);  /* traverse one gray object */
       return g->GCmemtrav;  /* memory traversed in this step */
     }
-    case GCSatomic: {
+    case GCSenteratomic: {
       lu_mem work;
       propagateall(g);  /* make sure gray list is empty */
       work = atomic(L);  /* work is what was traversed by 'atomic' */
@@ -1291,23 +1476,31 @@ void luaC_step (lua_State *L) {
 ** to sweep all objects to turn them back to white (as white has not
 ** changed, nothing will be collected).
 */
-void luaC_fullgc (lua_State *L, int isemergency) {
-  global_State *g = G(L);
-  lua_assert(g->gckind == KGC_NORMAL);
-  if (isemergency) g->gckind = KGC_EMERGENCY;  /* set flag */
-  if (keepinvariant(g)) {  /* black objects? */
+static void fullinc (lua_State *L, global_State *g) {
+  if (keepinvariant(g))  /* black objects? */
     entersweep(L); /* sweep everything to turn them back to white */
-  }
   /* finish any pending sweep phase to start a new cycle */
   luaC_runtilstate(L, bitmask(GCSpause));
   luaC_runtilstate(L, bitmask(GCScallfin));  /* run up to finalizers */
   /* estimate must be correct after a full GC cycle */
   lua_assert(g->GCestimate == gettotalbytes(g));
   luaC_runtilstate(L, bitmask(GCSpause));  /* finish collection */
-  g->gckind = KGC_NORMAL;
   setpause(g);
 }
 
+
+void luaC_fullgc (lua_State *L, int isemergency) {
+  global_State *g = G(L);
+  int gckind = g->gckind;
+  if (isemergency)
+    g->gckind = KGC_EMERGENCY;  /* set flag */
+  if (gckind == KGC_NORMAL)
+    fullinc(L, g);
+  else
+    fullgen(L, g);
+  g->gckind = gckind;
+}
+
 /* }====================================================== */
 
 

+ 39 - 15
lgc.h

@@ -1,5 +1,5 @@
 /*
-** $Id: lgc.h,v 2.91 2015/12/21 13:02:14 roberto Exp roberto $
+** $Id: lgc.h,v 2.92 2017/02/23 21:07:34 roberto Exp roberto $
 ** Garbage Collector
 ** See Copyright Notice in lua.h
 */
@@ -37,13 +37,14 @@
 ** Possible states of the Garbage Collector
 */
 #define GCSpropagate	0
-#define GCSatomic	1
-#define GCSswpallgc	2
-#define GCSswpfinobj	3
-#define GCSswptobefnz	4
-#define GCSswpend	5
-#define GCScallfin	6
-#define GCSpause	7
+#define GCSenteratomic	1
+#define GCSatomic	2
+#define GCSswpallgc	3
+#define GCSswpfinobj	4
+#define GCSswptobefnz	5
+#define GCSswpend	6
+#define GCScallfin	7
+#define GCSpause	8
 
 
 #define issweepphase(g)  \
@@ -74,14 +75,17 @@
 #define testbit(x,b)		testbits(x, bitmask(b))
 
 
-/* Layout for bit use in 'marked' field: */
-#define WHITE0BIT	0  /* object is white (type 0) */
-#define WHITE1BIT	1  /* object is white (type 1) */
-#define BLACKBIT	2  /* object is black */
-#define FINALIZEDBIT	3  /* object has been marked for finalization */
-#define OLDBIT		4  /* object is old (gen. mode) */
+/*
+** Layout for bit use in 'marked' field. First three bits are
+** used for object "age" in generational mode.
+*/
+#define WHITE0BIT	3  /* object is white (type 0) */
+#define WHITE1BIT	4  /* object is white (type 1) */
+#define BLACKBIT	5  /* object is black */
+#define FINALIZEDBIT	6  /* object has been marked for finalization */
 #define TESTGRAYBIT	7  /* used by tests (luaL_checkmemory) */
 
+
 #define WHITEBITS	bit2mask(WHITE0BIT, WHITE1BIT)
 
 
@@ -89,7 +93,6 @@
 #define isblack(x)      testbit((x)->marked, BLACKBIT)
 #define isgray(x)  /* neither white nor black */  \
 	(!testbits((x)->marked, WHITEBITS | bitmask(BLACKBIT)))
-#define isold(x)	testbit((x)->marked, OLDBIT)
 
 #define tofinalize(x)	testbit((x)->marked, FINALIZEDBIT)
 
@@ -103,6 +106,27 @@
 #define luaC_white(g)	cast(lu_byte, (g)->currentwhite & WHITEBITS)
 
 
+/* object age in generational mode */
+#define G_NEW		0	/* created in current cycle */
+#define G_SURVIVAL	1	/* created in previous cycle */
+#define G_OLD1		2	/* first full cycle as old */
+#define G_OLD0		3	/* marked old by frw. barrier in this cycle */
+#define G_OLD		4	/* really old object (not to be visited) */
+#define G_TOUCHED1	5	/* old object touched this cycle */
+#define G_TOUCHED2	6	/* old object touched in previous cycle */
+
+#define AGEBITS		7  /* all age bits (111) */
+
+#define getage(o)	((o)->marked & AGEBITS)
+#define setage(o,a)  ((o)->marked = cast_byte(((o)->marked & (~AGEBITS)) | a))
+#define isold(o)	(getage(o) > G_SURVIVAL)
+
+#define changeage(o,f,t)  \
+	check_exp(getage(o) == (f), (o)->marked ^= ((f)^(t)))
+
+#define ongraylist(o)	(isgray(o) || getage(o) == G_TOUCHED2)
+
+
 /*
 ** Does one step of collection when debt becomes positive. 'pre'/'pos'
 ** allows some adjustments to be done only when needed. macro

+ 3 - 2
lstate.c

@@ -1,5 +1,5 @@
 /*
-** $Id: lstate.c,v 2.133 2015/11/13 12:16:51 roberto Exp roberto $
+** $Id: lstate.c,v 2.134 2017/02/23 21:07:34 roberto Exp roberto $
 ** Global State
 ** See Copyright Notice in lua.h
 */
@@ -319,7 +319,8 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
   g->gcstate = GCSpause;
   g->gckind = KGC_NORMAL;
   g->allgc = g->finobj = g->tobefnz = g->fixedgc = NULL;
-  g->survival = g->old = NULL;
+  g->survival = g->old = g->reallyold = NULL;
+  g->finobjsur = g->finobjold = g->finobjrold = NULL;
   g->sweepgc = NULL;
   g->gray = g->grayagain = NULL;
   g->weak = g->ephemeron = g->allweak = NULL;

+ 6 - 2
lstate.h

@@ -1,5 +1,5 @@
 /*
-** $Id: lstate.h,v 2.134 2017/02/15 18:52:13 roberto Exp roberto $
+** $Id: lstate.h,v 2.135 2017/02/23 21:07:34 roberto Exp $
 ** Global State
 ** See Copyright Notice in lua.h
 */
@@ -160,8 +160,12 @@ typedef struct global_State {
   GCObject *tobefnz;  /* list of userdata to be GC */
   GCObject *fixedgc;  /* list of objects not to be collected */
   /* fields for generational collector */
-  GCObject *old;  /* start of old objects */
   GCObject *survival;  /* start of objects that survived one GC cycle */
+  GCObject *old;  /* start of old objects */
+  GCObject *reallyold;  /* old objects with more than one cycle */
+  GCObject *finobjsur;  /* list of survival objects with finalizers */
+  GCObject *finobjold;  /* list of old objects with finalizers */
+  GCObject *finobjrold;  /* list of really old objects with finalizers */
   struct lua_State *twups;  /* list of threads with open upvalues */
   unsigned int gcfinnum;  /* number of finalizers to call in each GC step */
   int gcpause;  /* size of pause between successive GCs */