Преглед изворни кода

GC back to controling pace counting bytes

Memory is the resource we want to save. Still to be reviewed again.
Roberto Ierusalimschy пре 10 месеци
родитељ
комит
ddfa1fbccf
11 измењених фајлова са 247 додато и 202 уклоњено
  1. 3 16
      lapi.c
  2. 1 0
      lauxlib.c
  3. 182 125
      lgc.c
  4. 19 15
      lgc.h
  5. 9 10
      llimits.h
  6. 4 4
      lmem.c
  7. 6 6
      lobject.c
  8. 1 1
      lobject.h
  9. 6 8
      lstate.c
  10. 7 8
      lstate.h
  11. 9 9
      ltests.c

+ 3 - 16
lapi.c

@@ -53,16 +53,6 @@ const char lua_ident[] =
 #define isupvalue(i)		((i) < LUA_REGISTRYINDEX)
 
 
-/* Advance the garbage collector when creating large objects */
-static void advancegc (lua_State *L, size_t delta) {
-  delta >>= 5;  /* one object for each 32 bytes (empirical) */
-  if (delta > 0) {
-    global_State *g = G(L);
-    luaE_setdebt(g, g->GCdebt - cast(l_obj, delta));
-  }
-}
-
-
 /*
 ** Convert an acceptable index to a pointer to its respective value.
 ** Non-valid indices return the special nil value 'G(L)->nilvalue'.
@@ -540,7 +530,6 @@ LUA_API const char *lua_pushlstring (lua_State *L, const char *s, size_t len) {
   ts = (len == 0) ? luaS_new(L, "") : luaS_newlstr(L, s, len);
   setsvalue2s(L, L->top.p, ts);
   api_incr_top(L);
-  advancegc(L, len);
   luaC_checkGC(L);
   lua_unlock(L);
   return getstr(ts);
@@ -557,7 +546,6 @@ LUA_API const char *lua_pushextlstring (lua_State *L,
   setsvalue2s(L, L->top.p, ts);
   api_incr_top(L);
   if (falloc != NULL)  /* non-static string? */
-    advancegc(L, len);  /* count its memory */
   luaC_checkGC(L);
   lua_unlock(L);
   return getstr(ts);
@@ -1190,16 +1178,16 @@ LUA_API int lua_gc (lua_State *L, int what, ...) {
     }
     case LUA_GCCOUNT: {
       /* GC values are expressed in Kbytes: #bytes/2^10 */
-      res = cast_int(g->GCtotalbytes >> 10);
+      res = cast_int(gettotalbytes(g) >> 10);
       break;
     }
     case LUA_GCCOUNTB: {
-      res = cast_int(g->GCtotalbytes & 0x3ff);
+      res = cast_int(gettotalbytes(g) & 0x3ff);
       break;
     }
     case LUA_GCSTEP: {
       lu_byte oldstp = g->gcstp;
-      l_obj n = cast(l_obj, va_arg(argp, size_t));
+      l_mem n = cast(l_mem, va_arg(argp, size_t));
       int work = 0;  /* true if GC did some work */
       g->gcstp = 0;  /* allow GC to run (other bits must be zero here) */
       if (n <= 0)
@@ -1356,7 +1344,6 @@ LUA_API void *lua_newuserdatauv (lua_State *L, size_t size, int nuvalue) {
   u = luaS_newudata(L, size, cast(unsigned short, nuvalue));
   setuvalue(L, s2v(L->top.p), u);
   api_incr_top(L);
-  advancegc(L, size);
   luaC_checkGC(L);
   lua_unlock(L);
   return getudatamem(u);

+ 1 - 0
lauxlib.c

@@ -618,6 +618,7 @@ LUALIB_API void luaL_pushresult (luaL_Buffer *B) {
     box->bsize = 0;  box->box = NULL;
     lua_pushextlstring(L, s, len, allocf, ud);
     lua_closeslot(L, -2);  /* close the box */
+    lua_gc(L, LUA_GCSTEP, len);
   }
   lua_remove(L, -2);  /* remove box or placeholder from the stack */
 }

+ 182 - 125
lgc.c

@@ -18,7 +18,6 @@
 #include "ldo.h"
 #include "lfunc.h"
 #include "lgc.h"
-#include "llex.h"
 #include "lmem.h"
 #include "lobject.h"
 #include "lstate.h"
@@ -27,13 +26,6 @@
 #include "ltm.h"
 
 
-/*
-** Number of fixed (luaC_fix) objects in a Lua state: metafield names,
-** plus reserved words, plus "_ENV", plus the memory-error message.
-*/
-#define NFIXED		(TM_N + NUM_RESERVED + 2)
-
-
 /*
 ** Maximum number of elements to sweep in each single step.
 ** (Large enough to dissipate fixed overheads but small enough
@@ -42,6 +34,12 @@
 #define GCSWEEPMAX	20
 
 
+/*
+** Cost (in work units) of running one finalizer.
+*/
+#define CWUFIN	10
+
+
 /* mask with all color bits */
 #define maskcolors	(bitmask(BLACKBIT) | WHITEBITS)
 
@@ -95,7 +93,7 @@
 
 
 static void reallymarkobject (global_State *g, GCObject *o);
-static l_obj atomic (lua_State *L);
+static void atomic (lua_State *L);
 static void entersweep (lua_State *L);
 
 
@@ -112,6 +110,66 @@ static void entersweep (lua_State *L);
 #define gnodelast(h)	gnode(h, cast_sizet(sizenode(h)))
 
 
+static size_t objsize (GCObject *o) {
+  switch (o->tt) {
+    case LUA_VTABLE: {
+      /* Fow now, table size does not consider 'haslastfree' */
+      Table *t = gco2t(o);
+      size_t sz = sizeof(Table)
+                + luaH_realasize(t) * (sizeof(Value) + 1);
+      if (!isdummy(t))
+        sz += sizenode(t) * sizeof(Node);
+      return sz;
+    }
+    case LUA_VLCL: {
+      LClosure *cl = gco2lcl(o);
+      return sizeLclosure(cl->nupvalues);
+    }
+    case LUA_VCCL: {
+      CClosure *cl = gco2ccl(o);
+      return sizeCclosure(cl->nupvalues);
+      break;
+    }
+    case LUA_VUSERDATA: {
+      Udata *u = gco2u(o);
+      return sizeudata(u->nuvalue, u->len);
+    }
+    case LUA_VPROTO: {
+      Proto *p = gco2p(o);
+      size_t sz = sizeof(Proto)
+                + cast_uint(p->sizep) * sizeof(Proto*)
+                + cast_uint(p->sizek) * sizeof(TValue)
+                + cast_uint(p->sizelocvars) * sizeof(LocVar)
+                + cast_uint(p->sizeupvalues) * sizeof(Upvaldesc);
+      if (!(p->flag & PF_FIXED)) {
+        sz +=  cast_uint(p->sizecode) * sizeof(Instruction)
+            +  cast_uint(p->sizelineinfo) * sizeof(lu_byte)
+            + cast_uint(p->sizeabslineinfo) * sizeof(AbsLineInfo);
+      }
+      return sz;
+    }
+    case LUA_VTHREAD: {
+      lua_State *L1 = gco2th(o);
+      size_t sz = sizeof(lua_State) + LUA_EXTRASPACE
+                + cast_uint(L1->nci) * sizeof(CallInfo);
+      if (L1->stack.p != NULL)
+        sz += cast_uint(stacksize(L1) + EXTRA_STACK) * sizeof(StackValue);
+      return sz;
+    }
+    case LUA_VSHRSTR: {
+      TString *ts = gco2ts(o);
+      return sizestrshr(cast_uint(ts->shrlen));
+    }
+    case LUA_VLNGSTR: {
+      TString *ts = gco2ts(o);
+      return luaS_sizelngstr(ts->u.lnglen, ts->shrlen);
+    }
+    case LUA_VUPVAL: return sizeof(UpVal);
+    default: lua_assert(0); return 0;
+  }
+}
+
+
 static GCObject **getgclist (GCObject *o) {
   switch (o->tt) {
     case LUA_VTABLE: return &gco2t(o)->gclist;
@@ -250,7 +308,6 @@ GCObject *luaC_newobjdt (lua_State *L, lu_byte tt, size_t sz, size_t offset) {
   global_State *g = G(L);
   char *p = cast_charp(luaM_newobject(L, novariant(tt), sz));
   GCObject *o = cast(GCObject *, p + offset);
-  g->GCdebt--;
   o->marked = luaC_white(g);
   o->tt = tt;
   o->next = g->allgc;
@@ -290,7 +347,7 @@ GCObject *luaC_newobj (lua_State *L, lu_byte tt, size_t sz) {
 ** (only closures can), and a userdata's metatable must be a table.
 */
 static void reallymarkobject (global_State *g, GCObject *o) {
-  g->GCmarked++;
+  g->GCmarked += cast(l_mem, objsize(o));
   switch (o->tt) {
     case LUA_VSHRSTR:
     case LUA_VLNGSTR: {
@@ -338,14 +395,10 @@ static void markmt (global_State *g) {
 /*
 ** mark all objects in list of being-finalized
 */
-static l_obj markbeingfnz (global_State *g) {
+static void markbeingfnz (global_State *g) {
   GCObject *o;
-  l_obj count = 0;
-  for (o = g->tobefnz; o != NULL; o = o->next) {
-    count++;
+  for (o = g->tobefnz; o != NULL; o = o->next)
     markobject(g, o);
-  }
-  return count;
 }
 
 
@@ -360,8 +413,7 @@ static l_obj markbeingfnz (global_State *g) {
 ** upvalues, as they have nothing to be checked. (If the thread gets an
 ** upvalue later, it will be linked in the list again.)
 */
-static l_obj remarkupvals (global_State *g) {
-  l_obj work = 0;
+static void remarkupvals (global_State *g) {
   lua_State *thread;
   lua_State **p = &g->twups;
   while ((thread = *p) != NULL) {
@@ -380,9 +432,7 @@ static l_obj remarkupvals (global_State *g) {
         }
       }
     }
-    work++;
   }
-  return work;
 }
 
 
@@ -401,7 +451,7 @@ static void cleargraylists (global_State *g) {
 */
 static void restartcollection (global_State *g) {
   cleargraylists(g);
-  g->GCmarked = NFIXED;
+  g->GCmarked = 0;
   markobject(g, g->mainthread);
   markvalue(g, &g->l_registry);
   markmt(g);
@@ -546,7 +596,7 @@ static void traversestrongtable (global_State *g, Table *h) {
 }
 
 
-static void traversetable (global_State *g, Table *h) {
+static l_mem traversetable (global_State *g, Table *h) {
   const char *weakkey, *weakvalue;
   const TValue *mode = gfasttm(g, h->metatable, TM_MODE);
   TString *smode;
@@ -565,15 +615,17 @@ static void traversetable (global_State *g, Table *h) {
   }
   else  /* not weak */
     traversestrongtable(g, h);
+  return 1 + sizenode(h) + h->alimit;
 }
 
 
-static void traverseudata (global_State *g, Udata *u) {
+static l_mem traverseudata (global_State *g, Udata *u) {
   int i;
   markobjectN(g, u->metatable);  /* mark its metatable */
   for (i = 0; i < u->nuvalue; i++)
     markvalue(g, &u->uv[i].uv);
   genlink(g, obj2gco(u));
+  return 1 + u->nuvalue;
 }
 
 
@@ -582,7 +634,7 @@ static void traverseudata (global_State *g, Udata *u) {
 ** arrays can be larger than needed; the extra slots are filled with
 ** NULL, so the use of 'markobjectN')
 */
-static void traverseproto (global_State *g, Proto *f) {
+static l_mem traverseproto (global_State *g, Proto *f) {
   int i;
   markobjectN(g, f->source);
   for (i = 0; i < f->sizek; i++)  /* mark literals */
@@ -593,26 +645,29 @@ static void traverseproto (global_State *g, Proto *f) {
     markobjectN(g, f->p[i]);
   for (i = 0; i < f->sizelocvars; i++)  /* mark local-variable names */
     markobjectN(g, f->locvars[i].varname);
+  return 1 + f->sizek + f->sizeupvalues + f->sizep + f->sizelocvars;
 }
 
 
-static void traverseCclosure (global_State *g, CClosure *cl) {
+static l_mem traverseCclosure (global_State *g, CClosure *cl) {
   int i;
   for (i = 0; i < cl->nupvalues; i++)  /* mark its upvalues */
     markvalue(g, &cl->upvalue[i]);
+  return 1 + cl->nupvalues;
 }
 
 /*
 ** Traverse a Lua closure, marking its prototype and its upvalues.
 ** (Both can be NULL while closure is being created.)
 */
-static void traverseLclosure (global_State *g, LClosure *cl) {
+static l_mem traverseLclosure (global_State *g, LClosure *cl) {
   int i;
   markobjectN(g, cl->p);  /* mark its prototype */
   for (i = 0; i < cl->nupvalues; i++) {  /* visit its upvalues */
     UpVal *uv = cl->upvals[i];
     markobjectN(g, uv);  /* mark upvalue */
   }
+  return 1 + cl->nupvalues;
 }
 
 
@@ -628,13 +683,13 @@ static void traverseLclosure (global_State *g, LClosure *cl) {
 ** (which can only happen in generational mode) or if the traverse is in
 ** the propagate phase (which can only happen in incremental mode).
 */
-static void traversethread (global_State *g, lua_State *th) {
+static l_mem traversethread (global_State *g, lua_State *th) {
   UpVal *uv;
   StkId o = th->stack.p;
   if (isold(th) || g->gcstate == GCSpropagate)
     linkgclist(th, g->grayagain);  /* insert into 'grayagain' list */
   if (o == NULL)
-    return;  /* stack not completely built yet */
+    return 0;  /* stack not completely built yet */
   lua_assert(g->gcstate == GCSatomic ||
              th->openupval == NULL || isintwups(th));
   for (; o < th->top.p; o++)  /* mark live elements in the stack */
@@ -652,35 +707,33 @@ static void traversethread (global_State *g, lua_State *th) {
       g->twups = th;
     }
   }
+  return 1 + (th->top.p - th->stack.p);
 }
 
 
 /*
-** traverse one gray object, turning it to black.
+** traverse one gray object, turning it to black. Return an estimate
+** of the number of slots traversed.
 */
-static void propagatemark (global_State *g) {
+static l_mem propagatemark (global_State *g) {
   GCObject *o = g->gray;
   nw2black(o);
   g->gray = *getgclist(o);  /* remove from 'gray' list */
   switch (o->tt) {
-    case LUA_VTABLE: traversetable(g, gco2t(o)); break;
-    case LUA_VUSERDATA: traverseudata(g, gco2u(o)); break;
-    case LUA_VLCL: traverseLclosure(g, gco2lcl(o)); break;
-    case LUA_VCCL: traverseCclosure(g, gco2ccl(o)); break;
-    case LUA_VPROTO: traverseproto(g, gco2p(o)); break;
-    case LUA_VTHREAD: traversethread(g, gco2th(o)); break;
-    default: lua_assert(0);
+    case LUA_VTABLE: return traversetable(g, gco2t(o));
+    case LUA_VUSERDATA: return traverseudata(g, gco2u(o));
+    case LUA_VLCL: return traverseLclosure(g, gco2lcl(o));
+    case LUA_VCCL: return traverseCclosure(g, gco2ccl(o));
+    case LUA_VPROTO: return traverseproto(g, gco2p(o));
+    case LUA_VTHREAD: return traversethread(g, gco2th(o));
+    default: lua_assert(0); return 0;
   }
 }
 
 
-static l_obj propagateall (global_State *g) {
-  l_obj work = 0;
-  while (g->gray) {
+static void propagateall (global_State *g) {
+  while (g->gray)
     propagatemark(g);
-    work++;
-  }
-  return work;
 }
 
 
@@ -690,9 +743,8 @@ static l_obj propagateall (global_State *g) {
 ** inverts the direction of the traversals, trying to speed up
 ** convergence on chains in the same table.
 */
-static l_obj convergeephemerons (global_State *g) {
+static void convergeephemerons (global_State *g) {
   int changed;
-  l_obj work = 0;
   int dir = 0;
   do {
     GCObject *w;
@@ -707,11 +759,9 @@ static l_obj convergeephemerons (global_State *g) {
         propagateall(g);  /* propagate changes */
         changed = 1;  /* will have to revisit all ephemeron tables */
       }
-      work++;
     }
     dir = !dir;  /* invert direction next time */
   } while (changed);  /* repeat until no more changes */
-  return work;
 }
 
 /* }====================================================== */
@@ -727,8 +777,7 @@ static l_obj convergeephemerons (global_State *g) {
 /*
 ** clear entries with unmarked keys from all weaktables in list 'l'
 */
-static l_obj clearbykeys (global_State *g, GCObject *l) {
-  l_obj work = 0;
+static void clearbykeys (global_State *g, GCObject *l) {
   for (; l; l = gco2t(l)->gclist) {
     Table *h = gco2t(l);
     Node *limit = gnodelast(h);
@@ -739,9 +788,7 @@ static l_obj clearbykeys (global_State *g, GCObject *l) {
       if (isempty(gval(n)))  /* is entry empty? */
         clearkey(n);  /* clear its key */
     }
-    work++;
   }
-  return work;
 }
 
 
@@ -749,8 +796,7 @@ static l_obj clearbykeys (global_State *g, GCObject *l) {
 ** clear entries with unmarked values from all weaktables in list 'l' up
 ** to element 'f'
 */
-static l_obj clearbyvalues (global_State *g, GCObject *l, GCObject *f) {
-  l_obj work = 0;
+static void clearbyvalues (global_State *g, GCObject *l, GCObject *f) {
   for (; l != f; l = gco2t(l)->gclist) {
     Table *h = gco2t(l);
     Node *n, *limit = gnodelast(h);
@@ -767,9 +813,7 @@ static l_obj clearbyvalues (global_State *g, GCObject *l, GCObject *f) {
       if (isempty(gval(n)))  /* is entry empty? */
         clearkey(n);  /* clear its key */
     }
-    work++;
   }
-  return work;
 }
 
 
@@ -781,7 +825,6 @@ static void freeupval (lua_State *L, UpVal *uv) {
 
 
 static void freeobj (lua_State *L, GCObject *o) {
-  G(L)->GCtotalobjs--;
   switch (o->tt) {
     case LUA_VPROTO:
       luaF_freeproto(L, gco2p(o));
@@ -835,12 +878,11 @@ static void freeobj (lua_State *L, GCObject *o) {
 ** for next collection cycle. Return where to continue the traversal or
 ** NULL if list is finished.
 */
-static GCObject **sweeplist (lua_State *L, GCObject **p, l_obj countin) {
+static GCObject **sweeplist (lua_State *L, GCObject **p, l_mem countin) {
   global_State *g = G(L);
   int ow = otherwhite(g);
-  l_obj i;
   int white = luaC_white(g);  /* current white */
-  for (i = 0; *p != NULL && i < countin; i++) {
+  while (*p != NULL && countin-- > 0) {
     GCObject *curr = *p;
     int marked = curr->marked;
     if (isdeadm(ow, marked)) {  /* is 'curr' dead? */
@@ -1052,8 +1094,8 @@ void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) {
 ** approximately (marked * pause / 100).
 */
 static void setpause (global_State *g) {
-  l_obj threshold = applygcparam(g, PAUSE, g->GCmarked);
-  l_obj debt = threshold - gettotalobjs(g);
+  l_mem threshold = applygcparam(g, PAUSE, g->GCmarked);
+  l_mem debt = threshold - gettotalbytes(g);
   if (debt < 0) debt = 0;
   luaE_setdebt(g, debt);
 }
@@ -1103,7 +1145,7 @@ static void sweep2old (lua_State *L, GCObject **p) {
 */
 static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p,
                             GCObject *limit, GCObject **pfirstold1,
-                            l_obj *paddedold) {
+                            l_mem *paddedold) {
   static const lu_byte nextage[] = {
     G_SURVIVAL,  /* from G_NEW */
     G_OLD1,      /* from G_SURVIVAL */
@@ -1113,7 +1155,7 @@ static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p,
     G_TOUCHED1,  /* from G_TOUCHED1 (do not change) */
     G_TOUCHED2   /* from G_TOUCHED2 (do not change) */
   };
-  l_obj addedold = 0;
+  l_mem addedold = 0;
   int white = luaC_white(g);
   GCObject *curr;
   while ((curr = *p) != limit) {
@@ -1132,7 +1174,7 @@ static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p,
         lua_assert(age != G_OLD1);  /* advanced in 'markold' */
         setage(curr, nextage[age]);
         if (getage(curr) == G_OLD1) {
-          addedold++;  /* one more object becoming old */
+          addedold += cast(l_mem, objsize(curr));  /* bytes becoming old */
           if (*pfirstold1 == NULL)
             *pfirstold1 = curr;  /* first OLD1 object in the list */
         }
@@ -1257,9 +1299,9 @@ static void minor2inc (lua_State *L, global_State *g, lu_byte kind) {
 ** than 'minormajor'% of the number of lived objects after the last
 ** major collection. (That percentage is computed in 'limit'.)
 */
-static int checkminormajor (global_State *g, l_obj addedold1) {
-  l_obj step = applygcparam(g, MINORMUL, g->GCmajorminor);
-  l_obj limit = applygcparam(g, MINORMAJOR, g->GCmajorminor);
+static int checkminormajor (global_State *g, l_mem addedold1) {
+  l_mem step = applygcparam(g, MINORMUL, g->GCmajorminor);
+  l_mem limit = applygcparam(g, MINORMAJOR, g->GCmajorminor);
   return (addedold1 >= (step >> 1) || g->GCmarked >= limit);
 }
 
@@ -1269,8 +1311,8 @@ static int checkminormajor (global_State *g, l_obj addedold1) {
 ** sweep all lists and advance pointers. Finally, finish the collection.
 */
 static void youngcollection (lua_State *L, global_State *g) {
-  l_obj addedold1 = 0;
-  l_obj marked = g->GCmarked;  /* preserve 'g->GCmarked' */
+  l_mem addedold1 = 0;
+  l_mem marked = g->GCmarked;  /* preserve 'g->GCmarked' */
   GCObject **psurvival;  /* to point to first non-dead survival object */
   GCObject *dummy;  /* dummy out parameter to 'sweepgen' */
   lua_assert(g->gcstate == GCSpropagate);
@@ -1346,7 +1388,9 @@ static void atomic2gen (lua_State *L, global_State *g) {
 
 /*
 ** Set debt for the next minor collection, which will happen when
-** total number of objects grows 'genminormul'%.
+** total number of bytes grows 'genminormul'% in relation to
+** the base, GCmajorminor, which is the number of bytes being used
+** after the last major collection.
 */
 static void setminordebt (global_State *g) {
   luaE_setdebt(g, applygcparam(g, MINORMUL, g->GCmajorminor));
@@ -1404,18 +1448,18 @@ static void fullgen (lua_State *L, global_State *g) {
 */
 static int checkmajorminor (lua_State *L, global_State *g) {
   if (g->gckind == KGC_GENMAJOR) {  /* generational mode? */
-    l_obj numobjs = gettotalobjs(g);
-    l_obj addedobjs = numobjs - g->GCmajorminor;
-    l_obj limit = applygcparam(g, MAJORMINOR, addedobjs);
-    l_obj tobecollected = numobjs - g->GCmarked;
+    l_mem numbytes = gettotalbytes(g);
+    l_mem addedobjs = numbytes - g->GCmajorminor;
+    l_mem limit = applygcparam(g, MAJORMINOR, addedobjs);
+    l_mem tobecollected = numbytes - g->GCmarked;
     if (tobecollected > limit) {
       atomic2gen(L, g);  /* return to generational mode */
       setminordebt(g);
-      return 0;  /* exit incremental collection */
+      return 1;  /* exit incremental collection */
     }
   }
   g->GCmajorminor = g->GCmarked;  /* prepare for next collection */
-  return 1;  /* stay doing incremental collections */
+  return 0;  /* stay doing incremental collections */
 }
 
 /* }====================================================== */
@@ -1474,8 +1518,7 @@ void luaC_freeallobjects (lua_State *L) {
 }
 
 
-static l_obj atomic (lua_State *L) {
-  l_obj work = 0;
+static void atomic (lua_State *L) {
   global_State *g = G(L);
   GCObject *origweak, *origall;
   GCObject *grayagain = g->grayagain;  /* save original list */
@@ -1487,33 +1530,32 @@ static l_obj atomic (lua_State *L) {
   /* registry and global metatables may be changed by API */
   markvalue(g, &g->l_registry);
   markmt(g);  /* mark global metatables */
-  work += propagateall(g);  /* empties 'gray' list */
+  propagateall(g);  /* empties 'gray' list */
   /* remark occasional upvalues of (maybe) dead threads */
-  work += remarkupvals(g);
-  work += propagateall(g);  /* propagate changes */
+  remarkupvals(g);
+  propagateall(g);  /* propagate changes */
   g->gray = grayagain;
-  work += propagateall(g);  /* traverse 'grayagain' list */
-  work += convergeephemerons(g);
+  propagateall(g);  /* traverse 'grayagain' list */
+  convergeephemerons(g);
   /* at this point, all strongly accessible objects are marked. */
   /* Clear values from weak tables, before checking finalizers */
-  work += clearbyvalues(g, g->weak, NULL);
-  work += clearbyvalues(g, g->allweak, NULL);
+  clearbyvalues(g, g->weak, NULL);
+  clearbyvalues(g, g->allweak, NULL);
   origweak = g->weak; origall = g->allweak;
   separatetobefnz(g, 0);  /* separate objects to be finalized */
-  work += markbeingfnz(g);  /* mark objects that will be finalized */
-  work += propagateall(g);  /* remark, to propagate 'resurrection' */
-  work += convergeephemerons(g);
+  markbeingfnz(g);  /* mark objects that will be finalized */
+  propagateall(g);  /* remark, to propagate 'resurrection' */
+  convergeephemerons(g);
   /* at this point, all resurrected objects are marked. */
   /* remove dead objects from weak tables */
-  work += clearbykeys(g, g->ephemeron);  /* clear keys from all ephemeron */
-  work += clearbykeys(g, g->allweak);  /* clear keys from all 'allweak' */
+  clearbykeys(g, g->ephemeron);  /* clear keys from all ephemeron */
+  clearbykeys(g, g->allweak);  /* clear keys from all 'allweak' */
   /* clear values from resurrected weak tables */
-  work += clearbyvalues(g, g->weak, origweak);
-  work += clearbyvalues(g, g->allweak, origall);
+  clearbyvalues(g, g->weak, origweak);
+  clearbyvalues(g, g->allweak, origall);
   luaS_clearcache(g);
   g->currentwhite = cast_byte(otherwhite(g));  /* flip current white */
   lua_assert(g->gray == NULL);
-  return work;
 }
 
 
@@ -1524,7 +1566,7 @@ static l_obj atomic (lua_State *L) {
 static void sweepstep (lua_State *L, global_State *g,
                        lu_byte nextstate, GCObject **nextlist, int fast) {
   if (g->sweepgc)
-    g->sweepgc = sweeplist(L, g->sweepgc, fast ? MAX_LOBJ : GCSWEEPMAX);
+    g->sweepgc = sweeplist(L, g->sweepgc, fast ? MAX_LMEM : GCSWEEPMAX);
   else {  /* enter next state */
     g->gcstate = nextstate;
     g->sweepgc = nextlist;
@@ -1544,72 +1586,80 @@ static void sweepstep (lua_State *L, global_State *g,
 ** That avoids traversing twice some objects, such as threads and
 ** weak tables.
 */
-static l_obj singlestep (lua_State *L, int fast) {
+
+#define step2pause	-3  /* finished collection; entered pause state */
+#define atomicstep	-2  /* atomic step */
+#define step2minor	-1  /* moved to minor collections */
+
+
+static l_mem singlestep (lua_State *L, int fast) {
   global_State *g = G(L);
-  l_obj work;
+  l_mem stepresult;
   lua_assert(!g->gcstopem);  /* collector is not reentrant */
   g->gcstopem = 1;  /* no emergency collections while collecting */
   switch (g->gcstate) {
     case GCSpause: {
       restartcollection(g);
       g->gcstate = GCSpropagate;
-      work = 1;
+      stepresult = 1;
       break;
     }
     case GCSpropagate: {
       if (fast || g->gray == NULL) {
         g->gcstate = GCSenteratomic;  /* finish propagate phase */
-        work = 0;
-      }
-      else {
-        propagatemark(g);  /* traverse one gray object */
-        work = 1;
+        stepresult = 1;
       }
+      else
+        stepresult = propagatemark(g);  /* traverse one gray object */
       break;
     }
     case GCSenteratomic: {
-      work = atomic(L);
+      atomic(L);
       if (checkmajorminor(L, g))
+        stepresult = step2minor;
+      else {
         entersweep(L);
+        stepresult = atomicstep;
+      }
       break;
     }
     case GCSswpallgc: {  /* sweep "regular" objects */
       sweepstep(L, g, GCSswpfinobj, &g->finobj, fast);
-      work = GCSWEEPMAX;
+      stepresult = GCSWEEPMAX;
       break;
     }
     case GCSswpfinobj: {  /* sweep objects with finalizers */
       sweepstep(L, g, GCSswptobefnz, &g->tobefnz, fast);
-      work = GCSWEEPMAX;
+      stepresult = GCSWEEPMAX;
       break;
     }
     case GCSswptobefnz: {  /* sweep objects to be finalized */
       sweepstep(L, g, GCSswpend, NULL, fast);
-      work = GCSWEEPMAX;
+      stepresult = GCSWEEPMAX;
       break;
     }
     case GCSswpend: {  /* finish sweeps */
       checkSizes(L, g);
       g->gcstate = GCScallfin;
-      work = 0;
+      stepresult = GCSWEEPMAX;
       break;
     }
     case GCScallfin: {  /* call finalizers */
       if (g->tobefnz && !g->gcemergency) {
         g->gcstopem = 0;  /* ok collections during finalizers */
         GCTM(L);  /* call one finalizer */
-        work = 1;
+        stepresult = CWUFIN;
       }
       else {  /* emergency mode or no more finalizers */
         g->gcstate = GCSpause;  /* finish collection */
-        work = 0;
+        stepresult = step2pause;
       }
       break;
     }
     default: lua_assert(0); return 0;
   }
   g->gcstopem = 0;
-  return work;
+  return stepresult;
 }
 
 
@@ -1635,25 +1685,26 @@ void luaC_runtilstate (lua_State *L, int state, int fast) {
 ** controls when next step will be performed.
 */
 static void incstep (lua_State *L, global_State *g) {
-  l_obj stepsize = applygcparam(g, STEPSIZE, 100);
-  l_obj work2do = applygcparam(g, STEPMUL, stepsize);
-  int fast = 0;
-  if (work2do == 0) {  /* special case: do a full collection */
-    work2do = MAX_LOBJ;  /* do unlimited work */
-    fast = 1;
-  }
-  do {  /* repeat until pause or enough work */
-    l_obj work = singlestep(L, fast);  /* perform one single step */
-    if (g->gckind == KGC_GENMINOR)  /* returned to minor collections? */
+  l_mem stepsize = applygcparam(g, STEPSIZE, 100);
+  l_mem work2do = applygcparam(g, STEPMUL, stepsize);
+  l_mem stres;
+  int fast = (work2do == 0);  /* special case: do a full collection */
+  do {  /* repeat until enough work */
+    stres = singlestep(L, fast);  /* perform one single step */
+    if (stres == step2minor)  /* returned to minor collections? */
       return;  /* nothing else to be done here */
-    work2do -= work;
-  } while (work2do > 0 && g->gcstate != GCSpause);
+    else if (stres == step2pause || (stres == atomicstep && !fast))
+      break;  /* end of cycle or atomic */
+    else
+      work2do -= stres;
+  } while (fast || work2do > 0);
   if (g->gcstate == GCSpause)
     setpause(g);  /* pause until next cycle */
   else
     luaE_setdebt(g, stepsize);
 }
 
+
 /*
 ** Performs a basic GC step if collector is running. (If collector is
 ** not running, set a reasonable debt to avoid it being called at
@@ -1663,17 +1714,23 @@ void luaC_step (lua_State *L) {
   global_State *g = G(L);
   lua_assert(!g->gcemergency);
   if (!gcrunning(g))  /* not running? */
-    luaE_setdebt(g, 2000);
+    luaE_setdebt(g, 20000);
   else {
+// printf("mem: %ld  kind: %s  ", gettotalbytes(g),
+//   g->gckind == KGC_INC ? "inc" : g->gckind == KGC_GENMAJOR ? "genmajor" :
+//     "genminor");
     switch (g->gckind) {
       case KGC_INC: case KGC_GENMAJOR:
+// printf("(%d -> ", g->gcstate);
         incstep(L, g);
+// printf("%d) ", g->gcstate);
         break;
       case KGC_GENMINOR:
         youngcollection(L, g);
         setminordebt(g);
         break;
     }
+// printf("-> mem: %ld  debt: %ld\n", gettotalbytes(g), g->GCdebt);
   }
 }
 
@@ -1692,7 +1749,7 @@ static void fullinc (lua_State *L, global_State *g) {
   luaC_runtilstate(L, GCSpause, 1);
   luaC_runtilstate(L, GCScallfin, 1);  /* run up to finalizers */
   /* 'marked' must be correct after a full GC cycle */
-  lua_assert(g->GCmarked == gettotalobjs(g));
+  /* lua_assert(g->GCmarked == gettotalobjs(g)); ??? */
   luaC_runtilstate(L, GCSpause, 1);  /* finish collection */
   setpause(g);
 }

+ 19 - 15
lgc.h

@@ -23,8 +23,9 @@
 ** never point to a white one. Moreover, any gray object must be in a
 ** "gray list" (gray, grayagain, weak, allweak, ephemeron) so that it
 ** can be visited again before finishing the collection cycle. (Open
-** upvalues are an exception to this rule.)  These lists have no meaning
-** when the invariant is not being enforced (e.g., sweep phase).
+** upvalues are an exception to this rule, as they are attached to
+** a corresponding thread.)  These lists have no meaning when the
+** invariant is not being enforced (e.g., sweep phase).
 */
 
 
@@ -48,10 +49,10 @@
 
 /*
 ** macro to tell when main invariant (white objects cannot point to black
-** ones) must be kept. During a collection, the sweep
-** phase may break the invariant, as objects turned white may point to
-** still-black objects. The invariant is restored when sweep ends and
-** all objects are white again.
+** ones) must be kept. During a collection, the sweep phase may break
+** the invariant, as objects turned white may point to still-black
+** objects. The invariant is restored when sweep ends and all objects
+** are white again.
 */
 
 #define keepinvariant(g)	((g)->gcstate <= GCSatomic)
@@ -163,34 +164,37 @@
 
 /*
 ** Minor collections will shift to major ones after LUAI_MINORMAJOR%
-** objects become old.
+** bytes become old.
 */
 #define LUAI_MINORMAJOR         100
 
 /*
 ** Major collections will shift to minor ones after a collection
-** collects at least LUAI_MAJORMINOR% of the new objects.
+** collects at least LUAI_MAJORMINOR% of the new bytes.
 */
 #define LUAI_MAJORMINOR         50
 
 /*
 ** A young (minor) collection will run after creating LUAI_GENMINORMUL%
-** new objects.
+** new bytes.
 */
 #define LUAI_GENMINORMUL         25
 
 
 /* incremental */
 
-/* Number of objects must be LUAI_GCPAUSE% before starting new cycle */
+/* Number of bytes must be LUAI_GCPAUSE% before starting new cycle */
 #define LUAI_GCPAUSE    200
 
-/* Step multiplier. (Roughly, the collector handles LUAI_GCMUL% objects
-   for each new allocated object.) */
-#define LUAI_GCMUL      200
+/*
+** Step multiplier: The collector handles LUAI_GCMUL% work units for
+** each new allocated byte. (Each "work unit" corresponds roughly to
+** sweeping or marking one object.)
+*/
+#define LUAI_GCMUL      20  /* ??? */
 
-/* How many objects to allocate before next GC step */
-#define LUAI_GCSTEPSIZE	250
+/* How many bytes to allocate before next GC step */
+#define LUAI_GCSTEPSIZE	(250 * sizeof(void*))
 
 
 #define setgcparam(g,p,v)  (g->gcparams[LUA_GCP##p] = luaO_codeparam(v))

+ 9 - 10
llimits.h

@@ -16,25 +16,24 @@
 
 
 /*
-** 'lu_mem' is an unsigned integer big enough to count the total memory
-** used by Lua (in bytes). 'l_obj' is a signed integer big enough to
-** count the total number of objects used by Lua. (It is signed due
-** to the use of debt in several computations.)  Usually, 'size_t' and
-** 'ptrdiff_t' should work, but we use 'long' for 16-bit machines.
+** 'l_mem' is a signed integer big enough to count the total memory
+** used by Lua.  (It is signed due to the use of debt in several
+** computations.)  Usually, 'ptrdiff_t' should work, but we use 'long'
+** for 16-bit machines.
 */
 #if defined(LUAI_MEM)		/* { external definitions? */
+typedef LUAI_MEM l_mem;
 typedef LUAI_UMEM lu_mem;
-typedef LUAI_MEM l_obj;
 #elif LUAI_IS32INT	/* }{ */
+typedef ptrdiff_t l_mem;
 typedef size_t lu_mem;
-typedef ptrdiff_t l_obj;
 #else  /* 16-bit ints */	/* }{ */
+typedef long l_mem;
 typedef unsigned long lu_mem;
-typedef long l_obj;
 #endif				/* } */
 
-#define MAX_LOBJ  \
-	cast(l_obj, (cast(lu_mem, 1) << (sizeof(l_obj) * CHAR_BIT - 1)) - 1)
+#define MAX_LMEM  \
+	cast(l_mem, (cast(lu_mem, 1) << (sizeof(l_mem) * 8 - 1)) - 1)
 
 
 /* chars used as small naturals (so that 'char' is reserved for characters) */

+ 4 - 4
lmem.c

@@ -151,7 +151,7 @@ void luaM_free_ (lua_State *L, void *block, size_t osize) {
   global_State *g = G(L);
   lua_assert((osize == 0) == (block == NULL));
   callfrealloc(g, block, osize, 0);
-  g->GCtotalbytes -= osize;
+  g->GCdebt += cast(l_mem, osize);
 }
 
 
@@ -181,10 +181,10 @@ void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) {
   if (l_unlikely(newblock == NULL && nsize > 0)) {
     newblock = tryagain(L, block, osize, nsize);
     if (newblock == NULL)  /* still no memory? */
-      return NULL;  /* do not update 'GCtotalbytes' */
+      return NULL;  /* do not update 'GCdebt' */
   }
   lua_assert((nsize == 0) == (newblock == NULL));
-  g->GCtotalbytes += nsize - osize;
+  g->GCdebt -= cast(l_mem, nsize) - cast(l_mem, osize);
   return newblock;
 }
 
@@ -209,7 +209,7 @@ void *luaM_malloc_ (lua_State *L, size_t size, int tag) {
       if (newblock == NULL)
         luaM_error(L);
     }
-    g->GCtotalbytes += size;
+    g->GCdebt -= cast(l_mem, size);
     return newblock;
   }
 }

+ 6 - 6
lobject.c

@@ -85,7 +85,7 @@ lu_byte luaO_codeparam (unsigned int p) {
 ** more significant bits, as long as the multiplication does not
 ** overflow, so we check which order is best.
 */
-l_obj luaO_applyparam (lu_byte p, l_obj x) {
+l_mem luaO_applyparam (lu_byte p, l_mem x) {
   unsigned int m = p & 0xF;  /* mantissa */
   int e = (p >> 4);  /* exponent */
   if (e > 0) {  /* normalized? */
@@ -94,19 +94,19 @@ l_obj luaO_applyparam (lu_byte p, l_obj x) {
   }
   e -= 7;  /* correct excess-7 */
   if (e >= 0) {
-    if (x < (MAX_LOBJ / 0x1F) >> e)  /* no overflow? */
+    if (x < (MAX_LMEM / 0x1F) >> e)  /* no overflow? */
       return (x * m) << e;  /* order doesn't matter here */
     else  /* real overflow */
-      return MAX_LOBJ;
+      return MAX_LMEM;
   }
   else {  /* negative exponent */
     e = -e;
-    if (x < MAX_LOBJ / 0x1F)  /* multiplication cannot overflow? */
+    if (x < MAX_LMEM / 0x1F)  /* multiplication cannot overflow? */
       return (x * m) >> e;  /* multiplying first gives more precision */
-    else if ((x >> e) <  MAX_LOBJ / 0x1F)  /* cannot overflow after shift? */
+    else if ((x >> e) <  MAX_LMEM / 0x1F)  /* cannot overflow after shift? */
       return (x >> e) * m;
     else  /* real overflow */
-      return MAX_LOBJ;
+      return MAX_LMEM;
   }
 }
 

+ 1 - 1
lobject.h

@@ -838,7 +838,7 @@ typedef struct Table {
 LUAI_FUNC int luaO_utf8esc (char *buff, unsigned long x);
 LUAI_FUNC lu_byte luaO_ceillog2 (unsigned int x);
 LUAI_FUNC lu_byte luaO_codeparam (unsigned int p);
-LUAI_FUNC l_obj luaO_applyparam (lu_byte p, l_obj x);
+LUAI_FUNC l_mem luaO_applyparam (lu_byte p, l_mem x);
 
 LUAI_FUNC int luaO_rawarith (lua_State *L, int op, const TValue *p1,
                              const TValue *p2, TValue *res);

+ 6 - 8
lstate.c

@@ -77,12 +77,12 @@ typedef struct LG {
 ** objects (GCtotalobjs - GCdebt) invariant and avoiding overflows in
 ** 'GCtotalobjs'.
 */
-void luaE_setdebt (global_State *g, l_obj debt) {
-  l_obj tb = gettotalobjs(g);
+void luaE_setdebt (global_State *g, l_mem debt) {
+  l_mem tb = gettotalbytes(g);
   lua_assert(tb > 0);
-  if (debt > MAX_LOBJ - tb)
-    debt = MAX_LOBJ - tb;  /* will make GCtotalobjs == MAX_LOBJ */
-  g->GCtotalobjs = tb + debt;
+  if (debt > MAX_LMEM - tb)
+    debt = MAX_LMEM - tb;  /* will make GCtotalbytes == MAX_LMEM */
+  g->GCtotalbytes = tb + debt;
   g->GCdebt = debt;
 }
 
@@ -269,8 +269,7 @@ static void close_state (lua_State *L) {
   }
   luaM_freearray(L, G(L)->strt.hash, cast_sizet(G(L)->strt.size));
   freestack(L);
-  lua_assert(g->GCtotalbytes == sizeof(LG));
-  lua_assert(gettotalobjs(g) == 1);
+  lua_assert(gettotalbytes(g) == sizeof(LG));
   (*g->frealloc)(g->ud, fromstate(L), sizeof(LG), 0);  /* free main block */
 }
 
@@ -379,7 +378,6 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud, unsigned seed) {
   g->weak = g->ephemeron = g->allweak = NULL;
   g->twups = NULL;
   g->GCtotalbytes = sizeof(LG);
-  g->GCtotalobjs = 1;
   g->GCmarked = 0;
   g->GCdebt = 0;
   setivalue(&g->nilvalue, 0);  /* to signal that state is not yet built */

+ 7 - 8
lstate.h

@@ -274,11 +274,10 @@ struct CallInfo {
 typedef struct global_State {
   lua_Alloc frealloc;  /* function to reallocate memory */
   void *ud;         /* auxiliary data to 'frealloc' */
-  lu_mem GCtotalbytes;  /* number of bytes currently allocated */
-  l_obj GCtotalobjs;  /* total number of objects allocated + GCdebt */
-  l_obj GCdebt;  /* objects counted but not yet allocated */
-  l_obj GCmarked;  /* number of objects marked in a GC cycle */
-  l_obj GCmajorminor;  /* auxiliary counter to control major-minor shifts */
+  l_mem GCtotalbytes;  /* number of bytes currently allocated + debt */
+  l_mem GCdebt;  /* bytes counted but not yet allocated */
+  l_mem GCmarked;  /* number of objects marked in a GC cycle */
+  l_mem GCmajorminor;  /* auxiliary counter to control major-minor shifts */
   stringtable strt;  /* hash table for strings */
   TValue l_registry;
   TValue nilvalue;  /* a nil value */
@@ -411,11 +410,11 @@ union GCUnion {
 #define obj2gco(v)	check_exp((v)->tt >= LUA_TSTRING, &(cast_u(v)->gc))
 
 
-/* actual number of total objects allocated */
-#define gettotalobjs(g)	((g)->GCtotalobjs - (g)->GCdebt)
+/* actual number of total memory allocated */
+#define gettotalbytes(g)	((g)->GCtotalbytes - (g)->GCdebt)
 
 
-LUAI_FUNC void luaE_setdebt (global_State *g, l_obj debt);
+LUAI_FUNC void luaE_setdebt (global_State *g, l_mem debt);
 LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1);
 LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L);
 LUAI_FUNC void luaE_shrinkCI (lua_State *L);

+ 9 - 9
ltests.c

@@ -537,7 +537,7 @@ static void checkobject (global_State *g, GCObject *o, int maybedead,
 }
 
 
-static l_obj checkgraylist (global_State *g, GCObject *o) {
+static l_mem checkgraylist (global_State *g, GCObject *o) {
   int total = 0;  /* count number of elements in the list */
   cast_void(g);  /* better to keep it if we need to print an object */
   while (o) {
@@ -566,8 +566,8 @@ static l_obj checkgraylist (global_State *g, GCObject *o) {
 /*
 ** Check objects in gray lists.
 */
-static l_obj checkgrays (global_State *g) {
-  l_obj total = 0;  /* count number of elements in all lists */
+static l_mem checkgrays (global_State *g) {
+  l_mem total = 0;  /* count number of elements in all lists */
   if (!keepinvariant(g)) return total;
   total += checkgraylist(g, g->gray);
   total += checkgraylist(g, g->grayagain);
@@ -583,7 +583,7 @@ static l_obj checkgrays (global_State *g) {
 ** 'count' and check its TESTBIT. (It must have been previously set by
 ** 'checkgraylist'.)
 */
-static void incifingray (global_State *g, GCObject *o, l_obj *count) {
+static void incifingray (global_State *g, GCObject *o, l_mem *count) {
   if (!keepinvariant(g))
     return;  /* gray lists not being kept in these phases */
   if (o->tt == LUA_VUPVAL) {
@@ -600,10 +600,10 @@ static void incifingray (global_State *g, GCObject *o, l_obj *count) {
 }
 
 
-static l_obj checklist (global_State *g, int maybedead, int tof,
+static l_mem checklist (global_State *g, int maybedead, int tof,
   GCObject *newl, GCObject *survival, GCObject *old, GCObject *reallyold) {
   GCObject *o;
-  l_obj total = 0;  /* number of object that should be in  gray lists */
+  l_mem total = 0;  /* number of object that should be in  gray lists */
   for (o = newl; o != survival; o = o->next) {
     checkobject(g, o, maybedead, G_NEW);
     incifingray(g, o, &total);
@@ -632,8 +632,8 @@ int lua_checkmemory (lua_State *L) {
   global_State *g = G(L);
   GCObject *o;
   int maybedead;
-  l_obj totalin;  /* total of objects that are in gray lists */
-  l_obj totalshould;  /* total of objects that should be in gray lists */
+  l_mem totalin;  /* total of objects that are in gray lists */
+  l_mem totalshould;  /* total of objects that should be in gray lists */
   if (keepinvariant(g)) {
     assert(!iswhite(g->mainthread));
     assert(!iswhite(gcvalue(&g->l_registry)));
@@ -1040,7 +1040,7 @@ static int table_query (lua_State *L) {
 
 static int query_GCparams (lua_State *L) {
   global_State *g = G(L);
-  lua_pushinteger(L, cast(lua_Integer, gettotalobjs(g)));
+  lua_pushinteger(L, cast(lua_Integer, gettotalbytes(g)));
   lua_pushinteger(L, cast(lua_Integer, g->GCdebt));
   lua_pushinteger(L, cast(lua_Integer, applygcparam(g, MINORMUL, 100)));
   lua_pushinteger(L, cast(lua_Integer, applygcparam(g, MAJORMINOR, 100)));