Browse Source

GC back to controling pace counting bytes

Memory is the resource we want to save. Still to be reviewed again.
Roberto Ierusalimschy 10 months ago
parent
commit
ddfa1fbccf
11 changed files with 247 additions and 202 deletions
  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)
 #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.
 ** Convert an acceptable index to a pointer to its respective value.
 ** Non-valid indices return the special nil value 'G(L)->nilvalue'.
 ** 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);
   ts = (len == 0) ? luaS_new(L, "") : luaS_newlstr(L, s, len);
   setsvalue2s(L, L->top.p, ts);
   setsvalue2s(L, L->top.p, ts);
   api_incr_top(L);
   api_incr_top(L);
-  advancegc(L, len);
   luaC_checkGC(L);
   luaC_checkGC(L);
   lua_unlock(L);
   lua_unlock(L);
   return getstr(ts);
   return getstr(ts);
@@ -557,7 +546,6 @@ LUA_API const char *lua_pushextlstring (lua_State *L,
   setsvalue2s(L, L->top.p, ts);
   setsvalue2s(L, L->top.p, ts);
   api_incr_top(L);
   api_incr_top(L);
   if (falloc != NULL)  /* non-static string? */
   if (falloc != NULL)  /* non-static string? */
-    advancegc(L, len);  /* count its memory */
   luaC_checkGC(L);
   luaC_checkGC(L);
   lua_unlock(L);
   lua_unlock(L);
   return getstr(ts);
   return getstr(ts);
@@ -1190,16 +1178,16 @@ LUA_API int lua_gc (lua_State *L, int what, ...) {
     }
     }
     case LUA_GCCOUNT: {
     case LUA_GCCOUNT: {
       /* GC values are expressed in Kbytes: #bytes/2^10 */
       /* GC values are expressed in Kbytes: #bytes/2^10 */
-      res = cast_int(g->GCtotalbytes >> 10);
+      res = cast_int(gettotalbytes(g) >> 10);
       break;
       break;
     }
     }
     case LUA_GCCOUNTB: {
     case LUA_GCCOUNTB: {
-      res = cast_int(g->GCtotalbytes & 0x3ff);
+      res = cast_int(gettotalbytes(g) & 0x3ff);
       break;
       break;
     }
     }
     case LUA_GCSTEP: {
     case LUA_GCSTEP: {
       lu_byte oldstp = g->gcstp;
       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 */
       int work = 0;  /* true if GC did some work */
       g->gcstp = 0;  /* allow GC to run (other bits must be zero here) */
       g->gcstp = 0;  /* allow GC to run (other bits must be zero here) */
       if (n <= 0)
       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));
   u = luaS_newudata(L, size, cast(unsigned short, nuvalue));
   setuvalue(L, s2v(L->top.p), u);
   setuvalue(L, s2v(L->top.p), u);
   api_incr_top(L);
   api_incr_top(L);
-  advancegc(L, size);
   luaC_checkGC(L);
   luaC_checkGC(L);
   lua_unlock(L);
   lua_unlock(L);
   return getudatamem(u);
   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;
     box->bsize = 0;  box->box = NULL;
     lua_pushextlstring(L, s, len, allocf, ud);
     lua_pushextlstring(L, s, len, allocf, ud);
     lua_closeslot(L, -2);  /* close the box */
     lua_closeslot(L, -2);  /* close the box */
+    lua_gc(L, LUA_GCSTEP, len);
   }
   }
   lua_remove(L, -2);  /* remove box or placeholder from the stack */
   lua_remove(L, -2);  /* remove box or placeholder from the stack */
 }
 }

+ 182 - 125
lgc.c

@@ -18,7 +18,6 @@
 #include "ldo.h"
 #include "ldo.h"
 #include "lfunc.h"
 #include "lfunc.h"
 #include "lgc.h"
 #include "lgc.h"
-#include "llex.h"
 #include "lmem.h"
 #include "lmem.h"
 #include "lobject.h"
 #include "lobject.h"
 #include "lstate.h"
 #include "lstate.h"
@@ -27,13 +26,6 @@
 #include "ltm.h"
 #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.
 ** Maximum number of elements to sweep in each single step.
 ** (Large enough to dissipate fixed overheads but small enough
 ** (Large enough to dissipate fixed overheads but small enough
@@ -42,6 +34,12 @@
 #define GCSWEEPMAX	20
 #define GCSWEEPMAX	20
 
 
 
 
+/*
+** Cost (in work units) of running one finalizer.
+*/
+#define CWUFIN	10
+
+
 /* mask with all color bits */
 /* mask with all color bits */
 #define maskcolors	(bitmask(BLACKBIT) | WHITEBITS)
 #define maskcolors	(bitmask(BLACKBIT) | WHITEBITS)
 
 
@@ -95,7 +93,7 @@
 
 
 
 
 static void reallymarkobject (global_State *g, GCObject *o);
 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);
 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)))
 #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) {
 static GCObject **getgclist (GCObject *o) {
   switch (o->tt) {
   switch (o->tt) {
     case LUA_VTABLE: return &gco2t(o)->gclist;
     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);
   global_State *g = G(L);
   char *p = cast_charp(luaM_newobject(L, novariant(tt), sz));
   char *p = cast_charp(luaM_newobject(L, novariant(tt), sz));
   GCObject *o = cast(GCObject *, p + offset);
   GCObject *o = cast(GCObject *, p + offset);
-  g->GCdebt--;
   o->marked = luaC_white(g);
   o->marked = luaC_white(g);
   o->tt = tt;
   o->tt = tt;
   o->next = g->allgc;
   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.
 ** (only closures can), and a userdata's metatable must be a table.
 */
 */
 static void reallymarkobject (global_State *g, GCObject *o) {
 static void reallymarkobject (global_State *g, GCObject *o) {
-  g->GCmarked++;
+  g->GCmarked += cast(l_mem, objsize(o));
   switch (o->tt) {
   switch (o->tt) {
     case LUA_VSHRSTR:
     case LUA_VSHRSTR:
     case LUA_VLNGSTR: {
     case LUA_VLNGSTR: {
@@ -338,14 +395,10 @@ static void markmt (global_State *g) {
 /*
 /*
 ** mark all objects in list of being-finalized
 ** mark all objects in list of being-finalized
 */
 */
-static l_obj markbeingfnz (global_State *g) {
+static void markbeingfnz (global_State *g) {
   GCObject *o;
   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);
     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
 ** upvalues, as they have nothing to be checked. (If the thread gets an
 ** upvalue later, it will be linked in the list again.)
 ** 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 *thread;
   lua_State **p = &g->twups;
   lua_State **p = &g->twups;
   while ((thread = *p) != NULL) {
   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) {
 static void restartcollection (global_State *g) {
   cleargraylists(g);
   cleargraylists(g);
-  g->GCmarked = NFIXED;
+  g->GCmarked = 0;
   markobject(g, g->mainthread);
   markobject(g, g->mainthread);
   markvalue(g, &g->l_registry);
   markvalue(g, &g->l_registry);
   markmt(g);
   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 char *weakkey, *weakvalue;
   const TValue *mode = gfasttm(g, h->metatable, TM_MODE);
   const TValue *mode = gfasttm(g, h->metatable, TM_MODE);
   TString *smode;
   TString *smode;
@@ -565,15 +615,17 @@ static void traversetable (global_State *g, Table *h) {
   }
   }
   else  /* not weak */
   else  /* not weak */
     traversestrongtable(g, h);
     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;
   int i;
   markobjectN(g, u->metatable);  /* mark its metatable */
   markobjectN(g, u->metatable);  /* mark its metatable */
   for (i = 0; i < u->nuvalue; i++)
   for (i = 0; i < u->nuvalue; i++)
     markvalue(g, &u->uv[i].uv);
     markvalue(g, &u->uv[i].uv);
   genlink(g, obj2gco(u));
   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
 ** arrays can be larger than needed; the extra slots are filled with
 ** NULL, so the use of 'markobjectN')
 ** 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;
   int i;
   markobjectN(g, f->source);
   markobjectN(g, f->source);
   for (i = 0; i < f->sizek; i++)  /* mark literals */
   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]);
     markobjectN(g, f->p[i]);
   for (i = 0; i < f->sizelocvars; i++)  /* mark local-variable names */
   for (i = 0; i < f->sizelocvars; i++)  /* mark local-variable names */
     markobjectN(g, f->locvars[i].varname);
     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;
   int i;
   for (i = 0; i < cl->nupvalues; i++)  /* mark its upvalues */
   for (i = 0; i < cl->nupvalues; i++)  /* mark its upvalues */
     markvalue(g, &cl->upvalue[i]);
     markvalue(g, &cl->upvalue[i]);
+  return 1 + cl->nupvalues;
 }
 }
 
 
 /*
 /*
 ** Traverse a Lua closure, marking its prototype and its upvalues.
 ** Traverse a Lua closure, marking its prototype and its upvalues.
 ** (Both can be NULL while closure is being created.)
 ** (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;
   int i;
   markobjectN(g, cl->p);  /* mark its prototype */
   markobjectN(g, cl->p);  /* mark its prototype */
   for (i = 0; i < cl->nupvalues; i++) {  /* visit its upvalues */
   for (i = 0; i < cl->nupvalues; i++) {  /* visit its upvalues */
     UpVal *uv = cl->upvals[i];
     UpVal *uv = cl->upvals[i];
     markobjectN(g, uv);  /* mark upvalue */
     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
 ** (which can only happen in generational mode) or if the traverse is in
 ** the propagate phase (which can only happen in incremental mode).
 ** 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;
   UpVal *uv;
   StkId o = th->stack.p;
   StkId o = th->stack.p;
   if (isold(th) || g->gcstate == GCSpropagate)
   if (isold(th) || g->gcstate == GCSpropagate)
     linkgclist(th, g->grayagain);  /* insert into 'grayagain' list */
     linkgclist(th, g->grayagain);  /* insert into 'grayagain' list */
   if (o == NULL)
   if (o == NULL)
-    return;  /* stack not completely built yet */
+    return 0;  /* stack not completely built yet */
   lua_assert(g->gcstate == GCSatomic ||
   lua_assert(g->gcstate == GCSatomic ||
              th->openupval == NULL || isintwups(th));
              th->openupval == NULL || isintwups(th));
   for (; o < th->top.p; o++)  /* mark live elements in the stack */
   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;
       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;
   GCObject *o = g->gray;
   nw2black(o);
   nw2black(o);
   g->gray = *getgclist(o);  /* remove from 'gray' list */
   g->gray = *getgclist(o);  /* remove from 'gray' list */
   switch (o->tt) {
   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);
     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
 ** inverts the direction of the traversals, trying to speed up
 ** convergence on chains in the same table.
 ** convergence on chains in the same table.
 */
 */
-static l_obj convergeephemerons (global_State *g) {
+static void convergeephemerons (global_State *g) {
   int changed;
   int changed;
-  l_obj work = 0;
   int dir = 0;
   int dir = 0;
   do {
   do {
     GCObject *w;
     GCObject *w;
@@ -707,11 +759,9 @@ static l_obj convergeephemerons (global_State *g) {
         propagateall(g);  /* propagate changes */
         propagateall(g);  /* propagate changes */
         changed = 1;  /* will have to revisit all ephemeron tables */
         changed = 1;  /* will have to revisit all ephemeron tables */
       }
       }
-      work++;
     }
     }
     dir = !dir;  /* invert direction next time */
     dir = !dir;  /* invert direction next time */
   } while (changed);  /* repeat until no more changes */
   } 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'
 ** 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) {
   for (; l; l = gco2t(l)->gclist) {
     Table *h = gco2t(l);
     Table *h = gco2t(l);
     Node *limit = gnodelast(h);
     Node *limit = gnodelast(h);
@@ -739,9 +788,7 @@ static l_obj clearbykeys (global_State *g, GCObject *l) {
       if (isempty(gval(n)))  /* is entry empty? */
       if (isempty(gval(n)))  /* is entry empty? */
         clearkey(n);  /* clear its key */
         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
 ** clear entries with unmarked values from all weaktables in list 'l' up
 ** to element 'f'
 ** 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) {
   for (; l != f; l = gco2t(l)->gclist) {
     Table *h = gco2t(l);
     Table *h = gco2t(l);
     Node *n, *limit = gnodelast(h);
     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? */
       if (isempty(gval(n)))  /* is entry empty? */
         clearkey(n);  /* clear its key */
         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) {
 static void freeobj (lua_State *L, GCObject *o) {
-  G(L)->GCtotalobjs--;
   switch (o->tt) {
   switch (o->tt) {
     case LUA_VPROTO:
     case LUA_VPROTO:
       luaF_freeproto(L, gco2p(o));
       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
 ** for next collection cycle. Return where to continue the traversal or
 ** NULL if list is finished.
 ** 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);
   global_State *g = G(L);
   int ow = otherwhite(g);
   int ow = otherwhite(g);
-  l_obj i;
   int white = luaC_white(g);  /* current white */
   int white = luaC_white(g);  /* current white */
-  for (i = 0; *p != NULL && i < countin; i++) {
+  while (*p != NULL && countin-- > 0) {
     GCObject *curr = *p;
     GCObject *curr = *p;
     int marked = curr->marked;
     int marked = curr->marked;
     if (isdeadm(ow, marked)) {  /* is 'curr' dead? */
     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).
 ** approximately (marked * pause / 100).
 */
 */
 static void setpause (global_State *g) {
 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;
   if (debt < 0) debt = 0;
   luaE_setdebt(g, debt);
   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,
 static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p,
                             GCObject *limit, GCObject **pfirstold1,
                             GCObject *limit, GCObject **pfirstold1,
-                            l_obj *paddedold) {
+                            l_mem *paddedold) {
   static const lu_byte nextage[] = {
   static const lu_byte nextage[] = {
     G_SURVIVAL,  /* from G_NEW */
     G_SURVIVAL,  /* from G_NEW */
     G_OLD1,      /* from G_SURVIVAL */
     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_TOUCHED1,  /* from G_TOUCHED1 (do not change) */
     G_TOUCHED2   /* from G_TOUCHED2 (do not change) */
     G_TOUCHED2   /* from G_TOUCHED2 (do not change) */
   };
   };
-  l_obj addedold = 0;
+  l_mem addedold = 0;
   int white = luaC_white(g);
   int white = luaC_white(g);
   GCObject *curr;
   GCObject *curr;
   while ((curr = *p) != limit) {
   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' */
         lua_assert(age != G_OLD1);  /* advanced in 'markold' */
         setage(curr, nextage[age]);
         setage(curr, nextage[age]);
         if (getage(curr) == G_OLD1) {
         if (getage(curr) == G_OLD1) {
-          addedold++;  /* one more object becoming old */
+          addedold += cast(l_mem, objsize(curr));  /* bytes becoming old */
           if (*pfirstold1 == NULL)
           if (*pfirstold1 == NULL)
             *pfirstold1 = curr;  /* first OLD1 object in the list */
             *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
 ** than 'minormajor'% of the number of lived objects after the last
 ** major collection. (That percentage is computed in 'limit'.)
 ** 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);
   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.
 ** sweep all lists and advance pointers. Finally, finish the collection.
 */
 */
 static void youngcollection (lua_State *L, global_State *g) {
 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 **psurvival;  /* to point to first non-dead survival object */
   GCObject *dummy;  /* dummy out parameter to 'sweepgen' */
   GCObject *dummy;  /* dummy out parameter to 'sweepgen' */
   lua_assert(g->gcstate == GCSpropagate);
   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
 ** 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) {
 static void setminordebt (global_State *g) {
   luaE_setdebt(g, applygcparam(g, MINORMUL, g->GCmajorminor));
   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) {
 static int checkmajorminor (lua_State *L, global_State *g) {
   if (g->gckind == KGC_GENMAJOR) {  /* generational mode? */
   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) {
     if (tobecollected > limit) {
       atomic2gen(L, g);  /* return to generational mode */
       atomic2gen(L, g);  /* return to generational mode */
       setminordebt(g);
       setminordebt(g);
-      return 0;  /* exit incremental collection */
+      return 1;  /* exit incremental collection */
     }
     }
   }
   }
   g->GCmajorminor = g->GCmarked;  /* prepare for next 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);
   global_State *g = G(L);
   GCObject *origweak, *origall;
   GCObject *origweak, *origall;
   GCObject *grayagain = g->grayagain;  /* save original list */
   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 */
   /* registry and global metatables may be changed by API */
   markvalue(g, &g->l_registry);
   markvalue(g, &g->l_registry);
   markmt(g);  /* mark global metatables */
   markmt(g);  /* mark global metatables */
-  work += propagateall(g);  /* empties 'gray' list */
+  propagateall(g);  /* empties 'gray' list */
   /* remark occasional upvalues of (maybe) dead threads */
   /* remark occasional upvalues of (maybe) dead threads */
-  work += remarkupvals(g);
-  work += propagateall(g);  /* propagate changes */
+  remarkupvals(g);
+  propagateall(g);  /* propagate changes */
   g->gray = grayagain;
   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. */
   /* at this point, all strongly accessible objects are marked. */
   /* Clear values from weak tables, before checking finalizers */
   /* 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;
   origweak = g->weak; origall = g->allweak;
   separatetobefnz(g, 0);  /* separate objects to be finalized */
   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. */
   /* at this point, all resurrected objects are marked. */
   /* remove dead objects from weak tables */
   /* 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 */
   /* 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);
   luaS_clearcache(g);
   g->currentwhite = cast_byte(otherwhite(g));  /* flip current white */
   g->currentwhite = cast_byte(otherwhite(g));  /* flip current white */
   lua_assert(g->gray == NULL);
   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,
 static void sweepstep (lua_State *L, global_State *g,
                        lu_byte nextstate, GCObject **nextlist, int fast) {
                        lu_byte nextstate, GCObject **nextlist, int fast) {
   if (g->sweepgc)
   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 */
   else {  /* enter next state */
     g->gcstate = nextstate;
     g->gcstate = nextstate;
     g->sweepgc = nextlist;
     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
 ** That avoids traversing twice some objects, such as threads and
 ** weak tables.
 ** 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);
   global_State *g = G(L);
-  l_obj work;
+  l_mem stepresult;
   lua_assert(!g->gcstopem);  /* collector is not reentrant */
   lua_assert(!g->gcstopem);  /* collector is not reentrant */
   g->gcstopem = 1;  /* no emergency collections while collecting */
   g->gcstopem = 1;  /* no emergency collections while collecting */
   switch (g->gcstate) {
   switch (g->gcstate) {
     case GCSpause: {
     case GCSpause: {
       restartcollection(g);
       restartcollection(g);
       g->gcstate = GCSpropagate;
       g->gcstate = GCSpropagate;
-      work = 1;
+      stepresult = 1;
       break;
       break;
     }
     }
     case GCSpropagate: {
     case GCSpropagate: {
       if (fast || g->gray == NULL) {
       if (fast || g->gray == NULL) {
         g->gcstate = GCSenteratomic;  /* finish propagate phase */
         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;
       break;
     }
     }
     case GCSenteratomic: {
     case GCSenteratomic: {
-      work = atomic(L);
+      atomic(L);
       if (checkmajorminor(L, g))
       if (checkmajorminor(L, g))
+        stepresult = step2minor;
+      else {
         entersweep(L);
         entersweep(L);
+        stepresult = atomicstep;
+      }
       break;
       break;
     }
     }
     case GCSswpallgc: {  /* sweep "regular" objects */
     case GCSswpallgc: {  /* sweep "regular" objects */
       sweepstep(L, g, GCSswpfinobj, &g->finobj, fast);
       sweepstep(L, g, GCSswpfinobj, &g->finobj, fast);
-      work = GCSWEEPMAX;
+      stepresult = GCSWEEPMAX;
       break;
       break;
     }
     }
     case GCSswpfinobj: {  /* sweep objects with finalizers */
     case GCSswpfinobj: {  /* sweep objects with finalizers */
       sweepstep(L, g, GCSswptobefnz, &g->tobefnz, fast);
       sweepstep(L, g, GCSswptobefnz, &g->tobefnz, fast);
-      work = GCSWEEPMAX;
+      stepresult = GCSWEEPMAX;
       break;
       break;
     }
     }
     case GCSswptobefnz: {  /* sweep objects to be finalized */
     case GCSswptobefnz: {  /* sweep objects to be finalized */
       sweepstep(L, g, GCSswpend, NULL, fast);
       sweepstep(L, g, GCSswpend, NULL, fast);
-      work = GCSWEEPMAX;
+      stepresult = GCSWEEPMAX;
       break;
       break;
     }
     }
     case GCSswpend: {  /* finish sweeps */
     case GCSswpend: {  /* finish sweeps */
       checkSizes(L, g);
       checkSizes(L, g);
       g->gcstate = GCScallfin;
       g->gcstate = GCScallfin;
-      work = 0;
+      stepresult = GCSWEEPMAX;
       break;
       break;
     }
     }
     case GCScallfin: {  /* call finalizers */
     case GCScallfin: {  /* call finalizers */
       if (g->tobefnz && !g->gcemergency) {
       if (g->tobefnz && !g->gcemergency) {
         g->gcstopem = 0;  /* ok collections during finalizers */
         g->gcstopem = 0;  /* ok collections during finalizers */
         GCTM(L);  /* call one finalizer */
         GCTM(L);  /* call one finalizer */
-        work = 1;
+        stepresult = CWUFIN;
       }
       }
       else {  /* emergency mode or no more finalizers */
       else {  /* emergency mode or no more finalizers */
         g->gcstate = GCSpause;  /* finish collection */
         g->gcstate = GCSpause;  /* finish collection */
-        work = 0;
+        stepresult = step2pause;
       }
       }
       break;
       break;
     }
     }
     default: lua_assert(0); return 0;
     default: lua_assert(0); return 0;
   }
   }
   g->gcstopem = 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.
 ** controls when next step will be performed.
 */
 */
 static void incstep (lua_State *L, global_State *g) {
 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 */
       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)
   if (g->gcstate == GCSpause)
     setpause(g);  /* pause until next cycle */
     setpause(g);  /* pause until next cycle */
   else
   else
     luaE_setdebt(g, stepsize);
     luaE_setdebt(g, stepsize);
 }
 }
 
 
+
 /*
 /*
 ** Performs a basic GC step if collector is running. (If collector is
 ** Performs a basic GC step if collector is running. (If collector is
 ** not running, set a reasonable debt to avoid it being called at
 ** 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);
   global_State *g = G(L);
   lua_assert(!g->gcemergency);
   lua_assert(!g->gcemergency);
   if (!gcrunning(g))  /* not running? */
   if (!gcrunning(g))  /* not running? */
-    luaE_setdebt(g, 2000);
+    luaE_setdebt(g, 20000);
   else {
   else {
+// printf("mem: %ld  kind: %s  ", gettotalbytes(g),
+//   g->gckind == KGC_INC ? "inc" : g->gckind == KGC_GENMAJOR ? "genmajor" :
+//     "genminor");
     switch (g->gckind) {
     switch (g->gckind) {
       case KGC_INC: case KGC_GENMAJOR:
       case KGC_INC: case KGC_GENMAJOR:
+// printf("(%d -> ", g->gcstate);
         incstep(L, g);
         incstep(L, g);
+// printf("%d) ", g->gcstate);
         break;
         break;
       case KGC_GENMINOR:
       case KGC_GENMINOR:
         youngcollection(L, g);
         youngcollection(L, g);
         setminordebt(g);
         setminordebt(g);
         break;
         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, GCSpause, 1);
   luaC_runtilstate(L, GCScallfin, 1);  /* run up to finalizers */
   luaC_runtilstate(L, GCScallfin, 1);  /* run up to finalizers */
   /* 'marked' must be correct after a full GC cycle */
   /* '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 */
   luaC_runtilstate(L, GCSpause, 1);  /* finish collection */
   setpause(g);
   setpause(g);
 }
 }

+ 19 - 15
lgc.h

@@ -23,8 +23,9 @@
 ** never point to a white one. Moreover, any gray object must be in a
 ** never point to a white one. Moreover, any gray object must be in a
 ** "gray list" (gray, grayagain, weak, allweak, ephemeron) so that it
 ** "gray list" (gray, grayagain, weak, allweak, ephemeron) so that it
 ** can be visited again before finishing the collection cycle. (Open
 ** 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
 ** 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)
 #define keepinvariant(g)	((g)->gcstate <= GCSatomic)
@@ -163,34 +164,37 @@
 
 
 /*
 /*
 ** Minor collections will shift to major ones after LUAI_MINORMAJOR%
 ** Minor collections will shift to major ones after LUAI_MINORMAJOR%
-** objects become old.
+** bytes become old.
 */
 */
 #define LUAI_MINORMAJOR         100
 #define LUAI_MINORMAJOR         100
 
 
 /*
 /*
 ** Major collections will shift to minor ones after a collection
 ** 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
 #define LUAI_MAJORMINOR         50
 
 
 /*
 /*
 ** A young (minor) collection will run after creating LUAI_GENMINORMUL%
 ** A young (minor) collection will run after creating LUAI_GENMINORMUL%
-** new objects.
+** new bytes.
 */
 */
 #define LUAI_GENMINORMUL         25
 #define LUAI_GENMINORMUL         25
 
 
 
 
 /* incremental */
 /* 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
 #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))
 #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? */
 #if defined(LUAI_MEM)		/* { external definitions? */
+typedef LUAI_MEM l_mem;
 typedef LUAI_UMEM lu_mem;
 typedef LUAI_UMEM lu_mem;
-typedef LUAI_MEM l_obj;
 #elif LUAI_IS32INT	/* }{ */
 #elif LUAI_IS32INT	/* }{ */
+typedef ptrdiff_t l_mem;
 typedef size_t lu_mem;
 typedef size_t lu_mem;
-typedef ptrdiff_t l_obj;
 #else  /* 16-bit ints */	/* }{ */
 #else  /* 16-bit ints */	/* }{ */
+typedef long l_mem;
 typedef unsigned long lu_mem;
 typedef unsigned long lu_mem;
-typedef long l_obj;
 #endif				/* } */
 #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) */
 /* 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);
   global_State *g = G(L);
   lua_assert((osize == 0) == (block == NULL));
   lua_assert((osize == 0) == (block == NULL));
   callfrealloc(g, block, osize, 0);
   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)) {
   if (l_unlikely(newblock == NULL && nsize > 0)) {
     newblock = tryagain(L, block, osize, nsize);
     newblock = tryagain(L, block, osize, nsize);
     if (newblock == NULL)  /* still no memory? */
     if (newblock == NULL)  /* still no memory? */
-      return NULL;  /* do not update 'GCtotalbytes' */
+      return NULL;  /* do not update 'GCdebt' */
   }
   }
   lua_assert((nsize == 0) == (newblock == NULL));
   lua_assert((nsize == 0) == (newblock == NULL));
-  g->GCtotalbytes += nsize - osize;
+  g->GCdebt -= cast(l_mem, nsize) - cast(l_mem, osize);
   return newblock;
   return newblock;
 }
 }
 
 
@@ -209,7 +209,7 @@ void *luaM_malloc_ (lua_State *L, size_t size, int tag) {
       if (newblock == NULL)
       if (newblock == NULL)
         luaM_error(L);
         luaM_error(L);
     }
     }
-    g->GCtotalbytes += size;
+    g->GCdebt -= cast(l_mem, size);
     return newblock;
     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
 ** more significant bits, as long as the multiplication does not
 ** overflow, so we check which order is best.
 ** 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 */
   unsigned int m = p & 0xF;  /* mantissa */
   int e = (p >> 4);  /* exponent */
   int e = (p >> 4);  /* exponent */
   if (e > 0) {  /* normalized? */
   if (e > 0) {  /* normalized? */
@@ -94,19 +94,19 @@ l_obj luaO_applyparam (lu_byte p, l_obj x) {
   }
   }
   e -= 7;  /* correct excess-7 */
   e -= 7;  /* correct excess-7 */
   if (e >= 0) {
   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 */
       return (x * m) << e;  /* order doesn't matter here */
     else  /* real overflow */
     else  /* real overflow */
-      return MAX_LOBJ;
+      return MAX_LMEM;
   }
   }
   else {  /* negative exponent */
   else {  /* negative exponent */
     e = -e;
     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 */
       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;
       return (x >> e) * m;
     else  /* real overflow */
     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 int luaO_utf8esc (char *buff, unsigned long x);
 LUAI_FUNC lu_byte luaO_ceillog2 (unsigned int x);
 LUAI_FUNC lu_byte luaO_ceillog2 (unsigned int x);
 LUAI_FUNC lu_byte luaO_codeparam (unsigned int p);
 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,
 LUAI_FUNC int luaO_rawarith (lua_State *L, int op, const TValue *p1,
                              const TValue *p2, TValue *res);
                              const TValue *p2, TValue *res);

+ 6 - 8
lstate.c

@@ -77,12 +77,12 @@ typedef struct LG {
 ** objects (GCtotalobjs - GCdebt) invariant and avoiding overflows in
 ** objects (GCtotalobjs - GCdebt) invariant and avoiding overflows in
 ** 'GCtotalobjs'.
 ** '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);
   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;
   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));
   luaM_freearray(L, G(L)->strt.hash, cast_sizet(G(L)->strt.size));
   freestack(L);
   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 */
   (*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->weak = g->ephemeron = g->allweak = NULL;
   g->twups = NULL;
   g->twups = NULL;
   g->GCtotalbytes = sizeof(LG);
   g->GCtotalbytes = sizeof(LG);
-  g->GCtotalobjs = 1;
   g->GCmarked = 0;
   g->GCmarked = 0;
   g->GCdebt = 0;
   g->GCdebt = 0;
   setivalue(&g->nilvalue, 0);  /* to signal that state is not yet built */
   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 {
 typedef struct global_State {
   lua_Alloc frealloc;  /* function to reallocate memory */
   lua_Alloc frealloc;  /* function to reallocate memory */
   void *ud;         /* auxiliary data to 'frealloc' */
   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 */
   stringtable strt;  /* hash table for strings */
   TValue l_registry;
   TValue l_registry;
   TValue nilvalue;  /* a nil value */
   TValue nilvalue;  /* a nil value */
@@ -411,11 +410,11 @@ union GCUnion {
 #define obj2gco(v)	check_exp((v)->tt >= LUA_TSTRING, &(cast_u(v)->gc))
 #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 void luaE_freethread (lua_State *L, lua_State *L1);
 LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L);
 LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L);
 LUAI_FUNC void luaE_shrinkCI (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 */
   int total = 0;  /* count number of elements in the list */
   cast_void(g);  /* better to keep it if we need to print an object */
   cast_void(g);  /* better to keep it if we need to print an object */
   while (o) {
   while (o) {
@@ -566,8 +566,8 @@ static l_obj checkgraylist (global_State *g, GCObject *o) {
 /*
 /*
 ** Check objects in gray lists.
 ** 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;
   if (!keepinvariant(g)) return total;
   total += checkgraylist(g, g->gray);
   total += checkgraylist(g, g->gray);
   total += checkgraylist(g, g->grayagain);
   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
 ** 'count' and check its TESTBIT. (It must have been previously set by
 ** 'checkgraylist'.)
 ** '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))
   if (!keepinvariant(g))
     return;  /* gray lists not being kept in these phases */
     return;  /* gray lists not being kept in these phases */
   if (o->tt == LUA_VUPVAL) {
   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 *newl, GCObject *survival, GCObject *old, GCObject *reallyold) {
   GCObject *o;
   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) {
   for (o = newl; o != survival; o = o->next) {
     checkobject(g, o, maybedead, G_NEW);
     checkobject(g, o, maybedead, G_NEW);
     incifingray(g, o, &total);
     incifingray(g, o, &total);
@@ -632,8 +632,8 @@ int lua_checkmemory (lua_State *L) {
   global_State *g = G(L);
   global_State *g = G(L);
   GCObject *o;
   GCObject *o;
   int maybedead;
   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)) {
   if (keepinvariant(g)) {
     assert(!iswhite(g->mainthread));
     assert(!iswhite(g->mainthread));
     assert(!iswhite(gcvalue(&g->l_registry)));
     assert(!iswhite(gcvalue(&g->l_registry)));
@@ -1040,7 +1040,7 @@ static int table_query (lua_State *L) {
 
 
 static int query_GCparams (lua_State *L) {
 static int query_GCparams (lua_State *L) {
   global_State *g = G(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, g->GCdebt));
   lua_pushinteger(L, cast(lua_Integer, applygcparam(g, MINORMUL, 100)));
   lua_pushinteger(L, cast(lua_Integer, applygcparam(g, MINORMUL, 100)));
   lua_pushinteger(L, cast(lua_Integer, applygcparam(g, MAJORMINOR, 100)));
   lua_pushinteger(L, cast(lua_Integer, applygcparam(g, MAJORMINOR, 100)));