Переглянути джерело

upvalues collected by reference count

Roberto Ierusalimschy 12 роки тому
батько
коміт
af35c7f398
12 змінених файлів з 157 додано та 205 видалено
  1. 14 8
      lapi.c
  2. 5 10
      ldo.c
  3. 27 38
      lfunc.c
  4. 20 2
      lfunc.h
  5. 58 84
      lgc.c
  6. 10 4
      lgc.h
  7. 4 9
      lobject.h
  8. 2 2
      lstate.c
  9. 2 12
      lstate.h
  10. 9 31
      ltests.c
  11. 2 2
      ltm.c
  12. 4 3
      lvm.c

+ 14 - 8
lapi.c

@@ -1,5 +1,5 @@
 /*
 /*
-** $Id: lapi.c,v 2.186 2013/08/05 16:58:28 roberto Exp roberto $
+** $Id: lapi.c,v 2.187 2013/08/16 18:55:49 roberto Exp roberto $
 ** Lua API
 ** Lua API
 ** See Copyright Notice in lua.h
 ** See Copyright Notice in lua.h
 */
 */
@@ -1192,7 +1192,7 @@ LUA_API void *lua_newuserdata (lua_State *L, size_t size) {
 
 
 
 
 static const char *aux_upvalue (StkId fi, int n, TValue **val,
 static const char *aux_upvalue (StkId fi, int n, TValue **val,
-                                GCObject **owner) {
+                                GCObject **owner, UpVal **uv) {
   switch (ttype(fi)) {
   switch (ttype(fi)) {
     case LUA_TCCL: {  /* C closure */
     case LUA_TCCL: {  /* C closure */
       CClosure *f = clCvalue(fi);
       CClosure *f = clCvalue(fi);
@@ -1207,7 +1207,7 @@ static const char *aux_upvalue (StkId fi, int n, TValue **val,
       Proto *p = f->p;
       Proto *p = f->p;
       if (!(1 <= n && n <= p->sizeupvalues)) return NULL;
       if (!(1 <= n && n <= p->sizeupvalues)) return NULL;
       *val = f->upvals[n-1]->v;
       *val = f->upvals[n-1]->v;
-      if (owner) *owner = obj2gco(f->upvals[n - 1]);
+      if (uv) *uv = f->upvals[n - 1];
       name = p->upvalues[n-1].name;
       name = p->upvalues[n-1].name;
       return (name == NULL) ? "" : getstr(name);
       return (name == NULL) ? "" : getstr(name);
     }
     }
@@ -1220,7 +1220,7 @@ LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n) {
   const char *name;
   const char *name;
   TValue *val = NULL;  /* to avoid warnings */
   TValue *val = NULL;  /* to avoid warnings */
   lua_lock(L);
   lua_lock(L);
-  name = aux_upvalue(index2addr(L, funcindex), n, &val, NULL);
+  name = aux_upvalue(index2addr(L, funcindex), n, &val, NULL, NULL);
   if (name) {
   if (name) {
     setobj2s(L, L->top, val);
     setobj2s(L, L->top, val);
     api_incr_top(L);
     api_incr_top(L);
@@ -1233,16 +1233,18 @@ LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n) {
 LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n) {
 LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n) {
   const char *name;
   const char *name;
   TValue *val = NULL;  /* to avoid warnings */
   TValue *val = NULL;  /* to avoid warnings */
-  GCObject *owner = NULL;  /* to avoid warnings */
+  GCObject *owner = NULL;
+  UpVal *uv = NULL;
   StkId fi;
   StkId fi;
   lua_lock(L);
   lua_lock(L);
   fi = index2addr(L, funcindex);
   fi = index2addr(L, funcindex);
   api_checknelems(L, 1);
   api_checknelems(L, 1);
-  name = aux_upvalue(fi, n, &val, &owner);
+  name = aux_upvalue(fi, n, &val, &owner, &uv);
   if (name) {
   if (name) {
     L->top--;
     L->top--;
     setobj(L, val, L->top);
     setobj(L, val, L->top);
-    luaC_barrier(L, owner, L->top);
+    if (owner) { luaC_barrier(L, owner, L->top); }
+    else if (uv) { luaC_upvalbarrier(L, uv); }
   }
   }
   lua_unlock(L);
   lua_unlock(L);
   return name;
   return name;
@@ -1284,7 +1286,11 @@ LUA_API void lua_upvaluejoin (lua_State *L, int fidx1, int n1,
   LClosure *f1;
   LClosure *f1;
   UpVal **up1 = getupvalref(L, fidx1, n1, &f1);
   UpVal **up1 = getupvalref(L, fidx1, n1, &f1);
   UpVal **up2 = getupvalref(L, fidx2, n2, NULL);
   UpVal **up2 = getupvalref(L, fidx2, n2, NULL);
+  luaC_upvdeccount(L, *up1);
   *up1 = *up2;
   *up1 = *up2;
-  luaC_objbarrier(L, f1, *up2);
+  (*up1)->refcount++;
+  if (upisopen(*up1)) (*up1)->u.op.touched = 1;
+  luaC_upvalbarrier(L, *up1);
 }
 }
 
 
+

+ 5 - 10
ldo.c

@@ -1,5 +1,5 @@
 /*
 /*
-** $Id: ldo.c,v 2.108.1.2 2013/04/19 21:03:23 roberto Exp $
+** $Id: ldo.c,v 2.109 2013/04/19 21:05:04 roberto Exp roberto $
 ** Stack and Call structure of Lua
 ** Stack and Call structure of Lua
 ** See Copyright Notice in lua.h
 ** See Copyright Notice in lua.h
 */
 */
@@ -141,10 +141,10 @@ int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
 
 
 static void correctstack (lua_State *L, TValue *oldstack) {
 static void correctstack (lua_State *L, TValue *oldstack) {
   CallInfo *ci;
   CallInfo *ci;
-  GCObject *up;
+  UpVal *up;
   L->top = (L->top - oldstack) + L->stack;
   L->top = (L->top - oldstack) + L->stack;
-  for (up = L->openupval; up != NULL; up = up->gch.next)
-    gco2uv(up)->v = (gco2uv(up)->v - oldstack) + L->stack;
+  for (up = L->openupval; up != NULL; up = up->u.op.next)
+    up->v = (up->v - oldstack) + L->stack;
   for (ci = L->ci; ci != NULL; ci = ci->previous) {
   for (ci = L->ci; ci != NULL; ci = ci->previous) {
     ci->top = (ci->top - oldstack) + L->stack;
     ci->top = (ci->top - oldstack) + L->stack;
     ci->func = (ci->func - oldstack) + L->stack;
     ci->func = (ci->func - oldstack) + L->stack;
@@ -637,7 +637,6 @@ static void checkmode (lua_State *L, const char *mode, const char *x) {
 
 
 
 
 static void f_parser (lua_State *L, void *ud) {
 static void f_parser (lua_State *L, void *ud) {
-  int i;
   Closure *cl;
   Closure *cl;
   struct SParser *p = cast(struct SParser *, ud);
   struct SParser *p = cast(struct SParser *, ud);
   int c = zgetc(p->z);  /* read first character */
   int c = zgetc(p->z);  /* read first character */
@@ -650,11 +649,7 @@ static void f_parser (lua_State *L, void *ud) {
     cl = luaY_parser(L, p->z, &p->buff, &p->dyd, p->name, c);
     cl = luaY_parser(L, p->z, &p->buff, &p->dyd, p->name, c);
   }
   }
   lua_assert(cl->l.nupvalues == cl->l.p->sizeupvalues);
   lua_assert(cl->l.nupvalues == cl->l.p->sizeupvalues);
-  for (i = 0; i < cl->l.nupvalues; i++) {  /* initialize upvalues */
-    UpVal *up = luaF_newupval(L);
-    cl->l.upvals[i] = up;
-    luaC_objbarrier(L, cl, up);
-  }
+  luaF_initupvals(L, &cl->l);
 }
 }
 
 
 
 

+ 27 - 38
lfunc.c

@@ -1,5 +1,5 @@
 /*
 /*
-** $Id: lfunc.c,v 2.34 2013/08/23 13:34:54 roberto Exp roberto $
+** $Id: lfunc.c,v 2.35 2013/08/26 12:41:10 roberto Exp roberto $
 ** Auxiliary functions to manipulate prototypes and closures
 ** Auxiliary functions to manipulate prototypes and closures
 ** See Copyright Notice in lua.h
 ** See Copyright Notice in lua.h
 */
 */
@@ -38,32 +38,33 @@ Closure *luaF_newLclosure (lua_State *L, int n) {
 }
 }
 
 
 
 
-UpVal *luaF_newupval (lua_State *L) {
-  UpVal *uv = &luaC_newobj(L, LUA_TUPVAL, sizeof(UpVal),
-                              &G(L)->localupv, 0)->uv;
-  uv->v = &uv->value;
-  setnilvalue(uv->v);
-  return uv;
+void luaF_initupvals (lua_State *L, LClosure *cl) {
+  int i;
+  for (i = 0; i < cl->nupvalues; i++) {
+    UpVal *uv = luaM_new(L, UpVal);
+    uv->refcount = 1;
+    uv->v = &uv->u.value;  /* make it closed */
+    setnilvalue(uv->v);
+    cl->upvals[i] = uv;
+  }
 }
 }
 
 
 
 
 UpVal *luaF_findupval (lua_State *L, StkId level) {
 UpVal *luaF_findupval (lua_State *L, StkId level) {
-  global_State *g = G(L);
-  GCObject **pp = &L->openupval;
+  UpVal **pp = &L->openupval;
   UpVal *p;
   UpVal *p;
   UpVal *uv;
   UpVal *uv;
-  while (*pp != NULL && (p = gco2uv(*pp))->v >= level) {
-    GCObject *o = obj2gco(p);
-    lua_assert(p->v != &p->value);
-    if (p->v == level) {  /* found a corresponding upvalue? */
-      if (isdead(g, o))  /* is it dead? */
-        changewhite(o);  /* resurrect it */
-      return p;
-    }
-    pp = &p->next;
+  while (*pp != NULL && (p = *pp)->v >= level) {
+    lua_assert(upisopen(p));
+    if (p->v == level)  /* found a corresponding upvalue? */
+      return p;  /* return it */
+    pp = &p->u.op.next;
   }
   }
   /* not found: create a new one */
   /* not found: create a new one */
-  uv = &luaC_newobj(L, LUA_TUPVAL, sizeof(UpVal), pp, 0)->uv;
+  uv = luaM_new(L, UpVal);
+  uv->refcount = 0;
+  uv->u.op.next = *pp;
+  *pp = uv;
   uv->v = level;  /* current value lives in the stack */
   uv->v = level;  /* current value lives in the stack */
   return uv;
   return uv;
 }
 }
@@ -71,27 +72,15 @@ UpVal *luaF_findupval (lua_State *L, StkId level) {
 
 
 void luaF_close (lua_State *L, StkId level) {
 void luaF_close (lua_State *L, StkId level) {
   UpVal *uv;
   UpVal *uv;
-  global_State *g = G(L);
-  while (L->openupval != NULL && (uv = gco2uv(L->openupval))->v >= level) {
-    GCObject *o = obj2gco(uv);
-    lua_assert(!isblack(o) && uv->v != &uv->value);
-    L->openupval = uv->next;  /* remove from `open' list */
-    if (isdead(g, o))
+  while (L->openupval != NULL && (uv = L->openupval)->v >= level) {
+    lua_assert(upisopen(uv));
+    L->openupval = uv->u.op.next;  /* remove from `open' list */
+    if (uv->refcount == 0)  /* no references? */
       luaM_free(L, uv);  /* free upvalue */
       luaM_free(L, uv);  /* free upvalue */
     else {
     else {
-      setobj(L, &uv->value, uv->v);  /* move value to upvalue slot */
-      uv->v = &uv->value;  /* now current value lives here */
-      if (islocal(o)) {
-        gch(o)->next = g->localupv;  /* link upvalue into 'localupv' list */
-        g->localupv = o;
-        resetbit(o->gch.marked, LOCALBLACK);
-      }
-      else {  /* link upvalue into 'allgc' list */
-        gch(o)->next = g->allgc;
-        g->allgc = o;
-      }
-      valnolocal(uv->v);  /* keep local invariant */
-      luaC_checkupvalcolor(g, uv);
+      setobj(L, &uv->u.value, uv->v);  /* move value to upvalue slot */
+      uv->v = &uv->u.value;  /* now current value lives here */
+      luaC_upvalbarrier(L, uv);
     }
     }
   }
   }
 }
 }

+ 20 - 2
lfunc.h

@@ -1,5 +1,5 @@
 /*
 /*
-** $Id: lfunc.h,v 2.8 2012/05/08 13:53:33 roberto Exp roberto $
+** $Id: lfunc.h,v 2.9 2013/08/07 12:18:11 roberto Exp roberto $
 ** Auxiliary functions to manipulate prototypes and closures
 ** Auxiliary functions to manipulate prototypes and closures
 ** See Copyright Notice in lua.h
 ** See Copyright Notice in lua.h
 */
 */
@@ -18,10 +18,28 @@
                          cast(int, sizeof(TValue *)*((n)-1)))
                          cast(int, sizeof(TValue *)*((n)-1)))
 
 
 
 
+/*
+** Upvalues for Lua closures
+*/
+struct UpVal {
+  TValue *v;  /* points to stack or to its own value */
+  unsigned int refcount;  /* reference counter */
+  union {
+    struct {  /* (when open) */
+      UpVal *next;  /* linked list */
+      int touched;  /* mark to avoid cycles with dead threads */
+    } op;
+    TValue value;  /* the value (when closed) */
+  } u;
+};
+
+#define upisopen(up)	((up)->v != &(up)->u.value)
+
+
 LUAI_FUNC Proto *luaF_newproto (lua_State *L);
 LUAI_FUNC Proto *luaF_newproto (lua_State *L);
 LUAI_FUNC Closure *luaF_newCclosure (lua_State *L, int nelems);
 LUAI_FUNC Closure *luaF_newCclosure (lua_State *L, int nelems);
 LUAI_FUNC Closure *luaF_newLclosure (lua_State *L, int nelems);
 LUAI_FUNC Closure *luaF_newLclosure (lua_State *L, int nelems);
-LUAI_FUNC UpVal *luaF_newupval (lua_State *L);
+LUAI_FUNC void luaF_initupvals (lua_State *L, LClosure *cl);
 LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level);
 LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level);
 LUAI_FUNC void luaF_close (lua_State *L, StkId level);
 LUAI_FUNC void luaF_close (lua_State *L, StkId level);
 LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f);
 LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f);

+ 58 - 84
lgc.c

@@ -1,5 +1,5 @@
 /*
 /*
-** $Id: lgc.c,v 2.151 2013/08/23 13:34:54 roberto Exp roberto $
+** $Id: lgc.c,v 2.152 2013/08/26 12:41:10 roberto Exp roberto $
 ** Garbage Collector
 ** Garbage Collector
 ** See Copyright Notice in lua.h
 ** See Copyright Notice in lua.h
 */
 */
@@ -85,12 +85,9 @@
   lua_longassert(!(iscollectable(o) && islocal(gcvalue(o)))); \
   lua_longassert(!(iscollectable(o) && islocal(gcvalue(o)))); \
   marklocalvalue(g,o); }
   marklocalvalue(g,o); }
 
 
-#define marklocalobject(g,t) { \
-	if ((t) && iswhite(obj2gco(t))) \
-	  reallymarkobject(g, obj2gco(t)); }
-
 #define markobject(g,t) \
 #define markobject(g,t) \
-  { lua_assert((t) == NULL || !islocal(obj2gco(t))); marklocalobject(g,t); }
+  { lua_assert((t) == NULL || !islocal(obj2gco(t))); \
+    if ((t) && iswhite(obj2gco(t))) reallymarkobject(g, obj2gco(t)); }
 
 
 static void reallymarkobject (global_State *g, GCObject *o);
 static void reallymarkobject (global_State *g, GCObject *o);
 
 
@@ -176,22 +173,18 @@ void luaC_barrierback_ (lua_State *L, GCObject *o) {
 
 
 
 
 /*
 /*
-** check color (and invariants) for an upvalue that is being closed,
-** i.e., moved into the 'allgc' list
+** barrier for assignments to closed upvalues. Because upvalues are
+** shared among closures, it is impossible to know the color of all
+** closured pointing to it. So, we assume that the object being assigned
+** must be marked.
 */
 */
-void luaC_checkupvalcolor (global_State *g, UpVal *uv) {
-  GCObject *o = obj2gco(uv);
-  lua_assert(!isblack(o));  /* open upvalues are never black */
-  if (isgray(o)) {
-    if (keepinvariant(g)) {
-      gray2black(o);  /* it is being visited now */
-      markvalue(g, uv->v);
-    }
-    else {
-      lua_assert(issweepphase(g));
-      makewhite(g, o);
-    }
-  }
+LUAI_FUNC void luaC_upvalbarrier_ (lua_State *L, UpVal *uv) {
+  global_State *g = G(L);
+  GCObject *o = gcvalue(uv->v);
+  lua_assert(!upisopen(uv));  /* ensured by macro luaC_upvalbarrier */
+  nolocal(o);
+  if (keepinvariant(g))
+    markobject(g, o);
 }
 }
 
 
 
 
@@ -257,14 +250,6 @@ static void reallymarkobject (global_State *g, GCObject *o) {
       size = sizeudata(gco2u(o));
       size = sizeudata(gco2u(o));
       break;
       break;
     }
     }
-    case LUA_TUPVAL: {
-      UpVal *uv = gco2uv(o);
-      marklocalvalue(g, uv->v);
-      if (uv->v != &uv->value)  /* open? */
-        return;  /* open upvalues remain gray */
-      size = sizeof(UpVal);
-      break;
-    }
     case LUA_TLCL: {
     case LUA_TLCL: {
       gco2lcl(o)->gclist = g->gray;
       gco2lcl(o)->gclist = g->gray;
       g->gray = o;
       g->gray = o;
@@ -328,10 +313,12 @@ static void remarkupvals (global_State *g) {
   for (; thread != NULL; thread = gch(thread)->next) {
   for (; thread != NULL; thread = gch(thread)->next) {
     lua_assert(!isblack(thread));  /* threads are never black */
     lua_assert(!isblack(thread));  /* threads are never black */
     if (!isgray(thread)) {  /* dead thread? */
     if (!isgray(thread)) {  /* dead thread? */
-      GCObject *uv = gco2th(thread)->openupval;
-      for (; uv != NULL; uv = gch(uv)->next) {
-        if (isgray(uv))  /* marked? */
-          marklocalvalue(g, gco2uv(uv)->v);  /* remark upvalue's value */
+      UpVal *uv = gco2th(thread)->openupval;
+      for (; uv != NULL; uv = uv->u.op.next) {
+        if (uv->u.op.touched) {
+          marklocalvalue(g, uv->v);  /* remark upvalue's value */
+          uv->u.op.touched = 0;
+        }
       }
       }
     }
     }
   }
   }
@@ -493,8 +480,15 @@ static lu_mem traverseCclosure (global_State *g, CClosure *cl) {
 static lu_mem traverseLclosure (global_State *g, LClosure *cl) {
 static lu_mem traverseLclosure (global_State *g, LClosure *cl) {
   int i;
   int i;
   markobject(g, cl->p);  /* mark its prototype */
   markobject(g, cl->p);  /* mark its prototype */
-  for (i = 0; i < cl->nupvalues; i++)  /* mark its upvalues */
-    marklocalobject(g, cl->upvals[i]);
+  for (i = 0; i < cl->nupvalues; i++) {  /* mark its upvalues */
+    UpVal *uv = cl->upvals[i];
+    if (uv != NULL) {
+      if (upisopen(uv))
+        uv->u.op.touched = 1;  /* can be marked in 'remarkupvals' */
+      else
+        markvalue(g, uv->v);
+    }
+  }
   return sizeLclosure(cl->nupvalues);
   return sizeLclosure(cl->nupvalues);
 }
 }
 
 
@@ -511,10 +505,14 @@ static lu_mem traversestack (global_State *g, lua_State *th) {
     for (; o < lim; o++)  /* clear not-marked stack slice */
     for (; o < lim; o++)  /* clear not-marked stack slice */
       setnilvalue(o);
       setnilvalue(o);
   }
   }
-  else {  /* count call infos to compute size */
+  else {
     CallInfo *ci;
     CallInfo *ci;
+    luaE_freeCI(th);  /* free extra CallInfo slots */
     for (ci = &th->base_ci; ci != th->ci; ci = ci->next)
     for (ci = &th->base_ci; ci != th->ci; ci = ci->next)
-      n++;
+      n++;  /* count call infos to compute size */
+    /* should not change the stack during an emergency gc cycle */
+    if (g->gckind != KGC_EMERGENCY)
+      luaD_shrinkstack(th);
   }
   }
   return sizeof(lua_State) + sizeof(TValue) * th->stacksize +
   return sizeof(lua_State) + sizeof(TValue) * th->stacksize +
          sizeof(CallInfo) * n;
          sizeof(CallInfo) * n;
@@ -667,18 +665,36 @@ static void clearvalues (global_State *g, GCObject *l, GCObject *f) {
 }
 }
 
 
 
 
+void luaC_upvdeccount (lua_State *L, UpVal *uv) {
+  lua_assert(uv->refcount > 0);
+  uv->refcount--;
+  if (uv->refcount == 0 && !upisopen(uv))
+    luaM_free(L, uv);
+}
+
+
+static void freeLclosure (lua_State *L, LClosure *cl) {
+  int i;
+  for (i = 0; i < cl->nupvalues; i++) {
+    UpVal *uv = cl->upvals[i];
+    if (uv)
+      luaC_upvdeccount(L, uv);
+  }
+  luaM_freemem(L, cl, sizeLclosure(cl->nupvalues));
+}
+
+
 static void freeobj (lua_State *L, GCObject *o) {
 static void freeobj (lua_State *L, GCObject *o) {
   switch (gch(o)->tt) {
   switch (gch(o)->tt) {
     case LUA_TPROTO: luaF_freeproto(L, gco2p(o)); break;
     case LUA_TPROTO: luaF_freeproto(L, gco2p(o)); break;
     case LUA_TLCL: {
     case LUA_TLCL: {
-      luaM_freemem(L, o, sizeLclosure(gco2lcl(o)->nupvalues));
+      freeLclosure(L, gco2lcl(o));
       break;
       break;
     }
     }
     case LUA_TCCL: {
     case LUA_TCCL: {
       luaM_freemem(L, o, sizeCclosure(gco2ccl(o)->nupvalues));
       luaM_freemem(L, o, sizeCclosure(gco2ccl(o)->nupvalues));
       break;
       break;
     }
     }
-    case LUA_TUPVAL: luaM_free(L, gco2uv(o)); break;
     case LUA_TTABLE: luaH_free(L, gco2t(o)); break;
     case LUA_TTABLE: luaH_free(L, gco2t(o)); break;
     case LUA_TTHREAD: luaE_freethread(L, gco2th(o)); break;
     case LUA_TTHREAD: luaE_freethread(L, gco2th(o)); break;
     case LUA_TUSERDATA: luaM_freemem(L, o, sizeudata(gco2u(o))); break;
     case LUA_TUSERDATA: luaM_freemem(L, o, sizeudata(gco2u(o))); break;
@@ -698,20 +714,6 @@ static void freeobj (lua_State *L, GCObject *o) {
 static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count);
 static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count);
 
 
 
 
-/*
-** sweep the (open) upvalues of a thread and resize its stack and
-** list of call-info structures.
-*/
-static void sweepthread (lua_State *L, lua_State *L1) {
-  if (L1->stack == NULL) return;  /* stack not completely built yet */
-  sweepwholelist(L, &L1->openupval);  /* sweep open upvalues */
-  luaE_freeCI(L1);  /* free extra CallInfo slots */
-  /* should not change the stack during an emergency gc cycle */
-  if (G(L)->gckind != KGC_EMERGENCY)
-    luaD_shrinkstack(L1);
-}
-
-
 /*
 /*
 ** sweep at most 'count' elements from a list of GCObjects erasing dead
 ** sweep at most 'count' elements from a list of GCObjects erasing dead
 ** objects, where a dead (not alive) object is one marked with the "old"
 ** objects, where a dead (not alive) object is one marked with the "old"
@@ -730,10 +732,7 @@ static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count) {
       *p = gch(curr)->next;  /* remove 'curr' from list */
       *p = gch(curr)->next;  /* remove 'curr' from list */
       freeobj(L, curr);  /* erase 'curr' */
       freeobj(L, curr);  /* erase 'curr' */
     }
     }
-    else {
-      if (gch(curr)->tt == LUA_TTHREAD)
-        sweepthread(L, gco2th(curr));  /* sweep thread's upvalues */
-      /* update marks */
+    else {  /* update marks */
       gch(curr)->marked = cast_byte((marked & maskcolors) | white);
       gch(curr)->marked = cast_byte((marked & maskcolors) | white);
       p = &gch(curr)->next;  /* go to next element */
       p = &gch(curr)->next;  /* go to next element */
     }
     }
@@ -886,16 +885,6 @@ void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) {
 */
 */
 
 
 
 
-static void localmarkclosure (LClosure *cl, int bit) {
-  int i;
-  for (i = 0; i < cl->nupvalues; i++) {
-    if (cl->upvals[i]) {
-      l_setbit(cl->upvals[i]->marked, bit);
-    }
-  }
-}
-
-
 /*
 /*
 ** Traverse a thread, local marking all its collectable objects
 ** Traverse a thread, local marking all its collectable objects
 */
 */
@@ -904,16 +893,8 @@ static void localmarkthread (lua_State *l) {
   if (o == NULL)
   if (o == NULL)
     return;  /* stack not completely built yet */
     return;  /* stack not completely built yet */
   for (; o < l->top; o++) {  /* mark live elements in the stack */
   for (; o < l->top; o++) {  /* mark live elements in the stack */
-    if (iscollectable(o)) {
-      GCObject *obj = gcvalue(o);
-      if (obj->gch.tt == LUA_TLCL &&  /* is it a Lua closure? */
-          islocal(obj) &&  /* is it still local? */
-          !testbit(obj->gch.marked, LOCALBLACK)) {  /* not visited yet? */
-        /* mark its upvalues as local black */
-        localmarkclosure(gco2lcl(obj), LOCALBLACK);
-      }
-      l_setbit(obj->gch.marked, LOCALBLACK);
-    }
+    if (iscollectable(o))
+      l_setbit(gcvalue(o)->gch.marked, LOCALBLACK);
   }
   }
 }
 }
 
 
@@ -937,10 +918,6 @@ static void localsweep (lua_State *L, global_State *g, GCObject **p) {
       *p = curr->gch.next;  /* remove 'curr' from list */
       *p = curr->gch.next;  /* remove 'curr' from list */
       curr->gch.next = g->allgc;  /* link 'curr' in 'allgc' list */
       curr->gch.next = g->allgc;  /* link 'curr' in 'allgc' list */
       g->allgc = curr;
       g->allgc = curr;
-      if (curr->gch.tt == LUA_TLCL) {  /* is it a Lua closure? */
-        /* mark its upvalues as non local */
-        localmarkclosure(gco2lcl(curr), LOCALBIT);
-      }
     }
     }
     else {  /* still local */
     else {  /* still local */
       if (testbit(curr->gch.marked, LOCALBLACK)) {  /* locally alive? */
       if (testbit(curr->gch.marked, LOCALBLACK)) {  /* locally alive? */
@@ -965,7 +942,6 @@ static void luaC_localcollection (lua_State *L) {
   lua_assert(g->gcstate == GCSpause);
   lua_assert(g->gcstate == GCSpause);
   localmark(g);
   localmark(g);
   localsweep(L, g, &g->localgc);
   localsweep(L, g, &g->localgc);
-  localsweep(L, g, &g->localupv);
 }
 }
 
 
 /* }====================================================== */
 /* }====================================================== */
@@ -1036,7 +1012,6 @@ void luaC_freeallobjects (lua_State *L) {
   g->gckind = KGC_NORMAL;
   g->gckind = KGC_NORMAL;
   sweepwholelist(L, &g->finobj);  /* finalizers can create objs. in 'finobj' */
   sweepwholelist(L, &g->finobj);  /* finalizers can create objs. in 'finobj' */
   sweepwholelist(L, &g->localgc);
   sweepwholelist(L, &g->localgc);
-  sweepwholelist(L, &g->localupv);
   sweepwholelist(L, &g->allgc);
   sweepwholelist(L, &g->allgc);
   sweepwholelist(L, &g->fixedgc);  /* collect fixed objects */
   sweepwholelist(L, &g->fixedgc);  /* collect fixed objects */
   lua_assert(g->strt.nuse == 0);
   lua_assert(g->strt.nuse == 0);
@@ -1119,7 +1094,6 @@ static lu_mem singlestep (lua_State *L) {
       }
       }
       else {
       else {
         sweepwholelist(L, &g->localgc);
         sweepwholelist(L, &g->localgc);
-        sweepwholelist(L, &g->localupv);
         g->gcstate = GCSsweep;
         g->gcstate = GCSsweep;
         return GCLOCALPAUSE / 4;  /* some magic for now */
         return GCLOCALPAUSE / 4;  /* some magic for now */
       }
       }

+ 10 - 4
lgc.h

@@ -1,5 +1,5 @@
 /*
 /*
-** $Id: lgc.h,v 2.65 2013/08/21 20:09:51 roberto Exp roberto $
+** $Id: lgc.h,v 2.66 2013/08/23 13:34:54 roberto Exp roberto $
 ** Garbage Collector
 ** Garbage Collector
 ** See Copyright Notice in lua.h
 ** See Copyright Notice in lua.h
 */
 */
@@ -55,7 +55,7 @@
 ** all objects are white again.
 ** all objects are white again.
 */
 */
 
 
-#define keepinvariant(g)	(g->gcstate <= GCSatomic)
+#define keepinvariant(g)	((g)->gcstate <= GCSatomic)
 
 
 
 
 /*
 /*
@@ -91,7 +91,7 @@
 
 
 #define tofinalize(x)	testbit((x)->gch.marked, FINALIZEDBIT)
 #define tofinalize(x)	testbit((x)->gch.marked, FINALIZEDBIT)
 
 
-#define otherwhite(g)	(g->currentwhite ^ WHITEBITS)
+#define otherwhite(g)	((g)->currentwhite ^ WHITEBITS)
 #define isdeadm(ow,m)	(!(((m) ^ WHITEBITS) & (ow)))
 #define isdeadm(ow,m)	(!(((m) ^ WHITEBITS) & (ow)))
 #define isdead(g,v)	isdeadm(otherwhite(g), (v)->gch.marked)
 #define isdead(g,v)	isdeadm(otherwhite(g), (v)->gch.marked)
 
 
@@ -127,6 +127,10 @@
    { if (nolocal(obj2gco(o)), isblack(obj2gco(p)) && iswhite(obj2gco(o))) \
    { if (nolocal(obj2gco(o)), isblack(obj2gco(p)) && iswhite(obj2gco(o))) \
 	luaC_barrierback_(L,p); }
 	luaC_barrierback_(L,p); }
 
 
+#define luaC_upvalbarrier(L,uv) \
+  { if (iscollectable((uv)->v) && !upisopen(uv)) \
+         luaC_upvalbarrier_(L,uv); }
+
 LUAI_FUNC void luaC_fix (lua_State *L, GCObject *o);
 LUAI_FUNC void luaC_fix (lua_State *L, GCObject *o);
 LUAI_FUNC void luaC_freeallobjects (lua_State *L);
 LUAI_FUNC void luaC_freeallobjects (lua_State *L);
 LUAI_FUNC void luaC_step (lua_State *L);
 LUAI_FUNC void luaC_step (lua_State *L);
@@ -138,7 +142,9 @@ LUAI_FUNC GCObject *luaC_newobj (lua_State *L, int tt, size_t sz,
 LUAI_FUNC void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v);
 LUAI_FUNC void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v);
 LUAI_FUNC void luaC_barrierback_ (lua_State *L, GCObject *o);
 LUAI_FUNC void luaC_barrierback_ (lua_State *L, GCObject *o);
 LUAI_FUNC void luaC_barrierproto_ (lua_State *L, Proto *p, Closure *c);
 LUAI_FUNC void luaC_barrierproto_ (lua_State *L, Proto *p, Closure *c);
+LUAI_FUNC void luaC_upvalbarrier_ (lua_State *L, UpVal *uv);
 LUAI_FUNC void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt);
 LUAI_FUNC void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt);
-LUAI_FUNC void luaC_checkupvalcolor (global_State *g, UpVal *uv);
+LUAI_FUNC void luaC_upvdeccount (lua_State *L, UpVal *uv);
+
 
 
 #endif
 #endif

+ 4 - 9
lobject.h

@@ -1,5 +1,5 @@
 /*
 /*
-** $Id: lobject.h,v 2.79 2013/08/07 12:18:11 roberto Exp roberto $
+** $Id: lobject.h,v 2.80 2013/08/18 16:12:18 roberto Exp roberto $
 ** Type definitions for Lua objects
 ** Type definitions for Lua objects
 ** See Copyright Notice in lua.h
 ** See Copyright Notice in lua.h
 */
 */
@@ -20,13 +20,12 @@
 ** Extra tags for non-values
 ** Extra tags for non-values
 */
 */
 #define LUA_TPROTO	LUA_NUMTAGS
 #define LUA_TPROTO	LUA_NUMTAGS
-#define LUA_TUPVAL	(LUA_NUMTAGS+1)
-#define LUA_TDEADKEY	(LUA_NUMTAGS+2)
+#define LUA_TDEADKEY	(LUA_NUMTAGS+1)
 
 
 /*
 /*
 ** number of all possible tags (including LUA_TNONE but excluding DEADKEY)
 ** number of all possible tags (including LUA_TNONE but excluding DEADKEY)
 */
 */
-#define LUA_TOTALTAGS	(LUA_TUPVAL+2)
+#define LUA_TOTALTAGS	(LUA_TPROTO + 2)
 
 
 
 
 /*
 /*
@@ -392,11 +391,7 @@ typedef struct Proto {
 /*
 /*
 ** Lua Upvalues
 ** Lua Upvalues
 */
 */
-typedef struct UpVal {
-  CommonHeader;
-  TValue *v;  /* points to stack or to its own value */
-  TValue value;  /* the value (when closed) */
-} UpVal;
+typedef struct UpVal UpVal;
 
 
 
 
 /*
 /*

+ 2 - 2
lstate.c

@@ -1,5 +1,5 @@
 /*
 /*
-** $Id: lstate.c,v 2.105 2013/08/23 13:34:54 roberto Exp roberto $
+** $Id: lstate.c,v 2.106 2013/08/26 12:41:10 roberto Exp roberto $
 ** Global State
 ** Global State
 ** See Copyright Notice in lua.h
 ** See Copyright Notice in lua.h
 */
 */
@@ -289,7 +289,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
   g->version = lua_version(NULL);
   g->version = lua_version(NULL);
   g->gcstate = GCSpause;
   g->gcstate = GCSpause;
   g->allgc = NULL;
   g->allgc = NULL;
-  g->localgc = g->localupv = NULL;
+  g->localgc = NULL;
   g->finobj = NULL;
   g->finobj = NULL;
   g->tobefnz = NULL;
   g->tobefnz = NULL;
   g->fixedgc = NULL;
   g->fixedgc = NULL;

+ 2 - 12
lstate.h

@@ -1,5 +1,5 @@
 /*
 /*
-** $Id: lstate.h,v 2.89 2013/08/23 13:34:54 roberto Exp roberto $
+** $Id: lstate.h,v 2.90 2013/08/26 12:41:10 roberto Exp roberto $
 ** Global State
 ** Global State
 ** See Copyright Notice in lua.h
 ** See Copyright Notice in lua.h
 */
 */
@@ -27,13 +27,6 @@
 ** List 'fixedgc' keep objects that are not to be collected (currently
 ** List 'fixedgc' keep objects that are not to be collected (currently
 ** only small strings, such as reserved words).
 ** only small strings, such as reserved words).
 **
 **
-** Open upvalues are not subject to independent garbage collection. They
-** are collected together with their respective threads. (They are
-** always gray, so they must be remarked in the atomic step. Usually
-** their contents would be marked when traversing the respective
-** threads, but the thread may already be dead, while the upvalue is
-** still accessible through closures.)
-**
 ** Live objects with finalizers are kept in the list g->finobj. The
 ** Live objects with finalizers are kept in the list g->finobj. The
 ** list g->tobefnz links all objects being finalized. In particular, an
 ** list g->tobefnz links all objects being finalized. In particular, an
 ** object has its FINALIZEDBIT set iff it is in one of these lists.
 ** object has its FINALIZEDBIT set iff it is in one of these lists.
@@ -128,7 +121,6 @@ typedef struct global_State {
   lu_byte gcrunning;  /* true if GC is running */
   lu_byte gcrunning;  /* true if GC is running */
   GCObject *allgc;  /* list of all collectable objects */
   GCObject *allgc;  /* list of all collectable objects */
   GCObject *localgc;  /* list of local objects */
   GCObject *localgc;  /* list of local objects */
-  GCObject *localupv;  /* list of local upvalues */
   GCObject *finobj;  /* list of collectable objects with finalizers */
   GCObject *finobj;  /* list of collectable objects with finalizers */
   GCObject **sweepgc;  /* current position of sweep in list 'allgc' */
   GCObject **sweepgc;  /* current position of sweep in list 'allgc' */
   GCObject **sweepfin;  /* current position of sweep in list 'finobj' */
   GCObject **sweepfin;  /* current position of sweep in list 'finobj' */
@@ -171,7 +163,7 @@ struct lua_State {
   int basehookcount;
   int basehookcount;
   int hookcount;
   int hookcount;
   lua_Hook hook;
   lua_Hook hook;
-  GCObject *openupval;  /* list of open upvalues in this stack */
+  UpVal *openupval;  /* list of open upvalues in this stack */
   GCObject *gclist;
   GCObject *gclist;
   struct lua_longjmp *errorJmp;  /* current error recover point */
   struct lua_longjmp *errorJmp;  /* current error recover point */
   ptrdiff_t errfunc;  /* current error handling function (stack index) */
   ptrdiff_t errfunc;  /* current error handling function (stack index) */
@@ -192,7 +184,6 @@ union GCObject {
   union Closure cl;
   union Closure cl;
   struct Table h;
   struct Table h;
   struct Proto p;
   struct Proto p;
-  struct UpVal uv;
   struct lua_State th;  /* thread */
   struct lua_State th;  /* thread */
 };
 };
 
 
@@ -211,7 +202,6 @@ union GCObject {
 	check_exp(novariant((o)->gch.tt) == LUA_TFUNCTION, &((o)->cl))
 	check_exp(novariant((o)->gch.tt) == LUA_TFUNCTION, &((o)->cl))
 #define gco2t(o)	check_exp((o)->gch.tt == LUA_TTABLE, &((o)->h))
 #define gco2t(o)	check_exp((o)->gch.tt == LUA_TTABLE, &((o)->h))
 #define gco2p(o)	check_exp((o)->gch.tt == LUA_TPROTO, &((o)->p))
 #define gco2p(o)	check_exp((o)->gch.tt == LUA_TPROTO, &((o)->p))
-#define gco2uv(o)	check_exp((o)->gch.tt == LUA_TUPVAL, &((o)->uv))
 #define gco2th(o)	check_exp((o)->gch.tt == LUA_TTHREAD, &((o)->th))
 #define gco2th(o)	check_exp((o)->gch.tt == LUA_TTHREAD, &((o)->th))
 
 
 /* macro to convert any Lua object into a GCObject */
 /* macro to convert any Lua object into a GCObject */

+ 9 - 31
ltests.c

@@ -1,5 +1,5 @@
 /*
 /*
-** $Id: ltests.c,v 2.148 2013/08/22 15:21:48 roberto Exp roberto $
+** $Id: ltests.c,v 2.149 2013/08/26 12:41:10 roberto Exp roberto $
 ** Internal Module for Debugging of the Lua Implementation
 ** Internal Module for Debugging of the Lua Implementation
 ** See Copyright Notice in lua.h
 ** See Copyright Notice in lua.h
 */
 */
@@ -195,14 +195,6 @@ static int testobjref2 (GCObject *f, GCObject *t) {
   /* not a local or pointed by a thread? */
   /* not a local or pointed by a thread? */
   if (!islocal(t) || gch(f)->tt == LUA_TTHREAD)
   if (!islocal(t) || gch(f)->tt == LUA_TTHREAD)
     return 1;  /* ok */
     return 1;  /* ok */
-  if (gch(t)->tt == LUA_TUPVAL) {
-    lua_assert(gch(f)->tt == LUA_TLCL);
-    return 1;  /* upvalue pointed by a closure */
-  }
-  if (gch(f)->tt == LUA_TUPVAL) {
-    UpVal *uv = gco2uv(f);
-    return (uv->v != &uv->value);  /* open upvalue can point to local stuff */
-  }
   if (gch(f)->tt == LUA_TPROTO && gch(t)->tt == LUA_TLCL)
   if (gch(f)->tt == LUA_TPROTO && gch(t)->tt == LUA_TLCL)
     return 1;  /* cache from a prototype */
     return 1;  /* cache from a prototype */
   return 0;
   return 0;
@@ -311,9 +303,11 @@ static void checkLclosure (global_State *g, LClosure *cl) {
   int i;
   int i;
   if (cl->p) checkobjref(g, clgc, cl->p);
   if (cl->p) checkobjref(g, clgc, cl->p);
   for (i=0; i<cl->nupvalues; i++) {
   for (i=0; i<cl->nupvalues; i++) {
-    if (cl->upvals[i]) {
-      lua_assert(cl->upvals[i]->tt == LUA_TUPVAL);
-      checkobjref(g, clgc, cl->upvals[i]);
+    UpVal *uv = cl->upvals[i];
+    if (uv) {
+      if (!upisopen(uv))  /* only closed upvalues matter to invariant */
+        checkvalref(g, clgc, uv->v);
+      lua_assert(uv->refcount > 0);
     }
     }
   }
   }
 }
 }
@@ -332,13 +326,10 @@ static int lua_checkpc (pCallInfo ci) {
 static void checkstack (global_State *g, lua_State *L1) {
 static void checkstack (global_State *g, lua_State *L1) {
   StkId o;
   StkId o;
   CallInfo *ci;
   CallInfo *ci;
-  GCObject *uvo;
+  UpVal *uv;
   lua_assert(!isdead(g, obj2gco(L1)));
   lua_assert(!isdead(g, obj2gco(L1)));
-  for (uvo = L1->openupval; uvo != NULL; uvo = gch(uvo)->next) {
-    UpVal *uv = gco2uv(uvo);
-    lua_assert(uv->v != &uv->value);  /* must be open */
-    lua_assert(!isblack(uvo));  /* open upvalues cannot be black */
-  }
+  for (uv = L1->openupval; uv != NULL; uv = uv->u.op.next)
+    lua_assert(upisopen(uv));  /* must be open */
   for (ci = L1->ci; ci != NULL; ci = ci->previous) {
   for (ci = L1->ci; ci != NULL; ci = ci->previous) {
     lua_assert(ci->top <= L1->stack_last);
     lua_assert(ci->top <= L1->stack_last);
     lua_assert(lua_checkpc(ci));
     lua_assert(lua_checkpc(ci));
@@ -357,13 +348,6 @@ static void checkobject (global_State *g, GCObject *o, int maybedead) {
   else {
   else {
     lua_assert(g->gcstate != GCSpause || iswhite(o));
     lua_assert(g->gcstate != GCSpause || iswhite(o));
     switch (gch(o)->tt) {
     switch (gch(o)->tt) {
-      case LUA_TUPVAL: {
-        UpVal *uv = gco2uv(o);
-        lua_assert(uv->v == &uv->value);  /* must be closed */
-        lua_assert(!isgray(o));  /* closed upvalues are never gray */
-        checkvalref(g, o, uv->v);
-        break;
-      }
       case LUA_TUSERDATA: {
       case LUA_TUSERDATA: {
         Table *mt = gco2u(o)->metatable;
         Table *mt = gco2u(o)->metatable;
         if (mt) checkobjref(g, o, mt);
         if (mt) checkobjref(g, o, mt);
@@ -490,12 +474,6 @@ int lua_checkmemory (lua_State *L) {
   for (o = g->localgc; o != NULL; o = gch(o)->next) {
   for (o = g->localgc; o != NULL; o = gch(o)->next) {
     checkobject(g, o, 1);
     checkobject(g, o, 1);
   }
   }
-  /* check 'localupv' list */
-  checkgray(g, g->localupv);
-  for (o = g->localupv; o != NULL; o = gch(o)->next) {
-    lua_assert(gch(o)->tt == LUA_TUPVAL);
-    checkobject(g, o, 1);
-  }
   return 0;
   return 0;
 }
 }
 
 

+ 2 - 2
ltm.c

@@ -1,5 +1,5 @@
 /*
 /*
-** $Id: ltm.c,v 2.20 2013/05/06 17:19:11 roberto Exp roberto $
+** $Id: ltm.c,v 2.21 2013/08/21 20:09:51 roberto Exp roberto $
 ** Tag methods
 ** Tag methods
 ** See Copyright Notice in lua.h
 ** See Copyright Notice in lua.h
 */
 */
@@ -28,7 +28,7 @@ LUAI_DDEF const char *const luaT_typenames_[LUA_TOTALTAGS] = {
   "no value",
   "no value",
   "nil", "boolean", udatatypename, "number",
   "nil", "boolean", udatatypename, "number",
   "string", "table", "function", udatatypename, "thread",
   "string", "table", "function", udatatypename, "thread",
-  "proto", "upval"  /* these last two cases are used for tests only */
+  "proto" /* this last case is used for tests only */
 };
 };
 
 
 
 

+ 4 - 3
lvm.c

@@ -1,5 +1,5 @@
 /*
 /*
-** $Id: lvm.c,v 2.177 2013/08/16 18:55:49 roberto Exp roberto $
+** $Id: lvm.c,v 2.178 2013/08/19 14:18:43 roberto Exp roberto $
 ** Lua virtual machine
 ** Lua virtual machine
 ** See Copyright Notice in lua.h
 ** See Copyright Notice in lua.h
 */
 */
@@ -416,7 +416,8 @@ static void pushclosure (lua_State *L, Proto *p, UpVal **encup, StkId base,
       ncl->l.upvals[i] = luaF_findupval(L, base + uv[i].idx);
       ncl->l.upvals[i] = luaF_findupval(L, base + uv[i].idx);
     else  /* get upvalue from enclosing function */
     else  /* get upvalue from enclosing function */
       ncl->l.upvals[i] = encup[uv[i].idx];
       ncl->l.upvals[i] = encup[uv[i].idx];
-    /* new closure is white and local, so we do not need a barrier here */
+    ncl->l.upvals[i]->refcount++;
+    /* new closure is white, so we do not need a barrier here */
   }
   }
   if (!isblack(obj2gco(p)))  /* cache will not break GC invariant? */
   if (!isblack(obj2gco(p)))  /* cache will not break GC invariant? */
     p->cache = ncl;  /* save it on cache for reuse */
     p->cache = ncl;  /* save it on cache for reuse */
@@ -591,7 +592,7 @@ void luaV_execute (lua_State *L) {
       vmcase(OP_SETUPVAL,
       vmcase(OP_SETUPVAL,
         UpVal *uv = cl->upvals[GETARG_B(i)];
         UpVal *uv = cl->upvals[GETARG_B(i)];
         setobj(L, uv->v, ra);
         setobj(L, uv->v, ra);
-        luaC_barrier(L, uv, ra);
+        luaC_upvalbarrier(L, uv);
       )
       )
       vmcase(OP_SETTABLE,
       vmcase(OP_SETTABLE,
         Protect(luaV_settable(L, ra, RKB(i), RKC(i)));
         Protect(luaV_settable(L, ra, RKB(i), RKC(i)));