瀏覽代碼

new list 'twups' to allow traversal of upvalues from dead threads
(+ fixed some problems with cycles involving those upvalues)

Roberto Ierusalimschy 11 年之前
父節點
當前提交
d764cc5522
共有 5 個文件被更改,包括 58 次插入17 次删除
  1. 12 4
      lfunc.c
  2. 5 1
      lfunc.h
  3. 35 10
      lgc.c
  4. 3 1
      lstate.c
  5. 3 1
      lstate.h

+ 12 - 4
lfunc.c

@@ -1,5 +1,5 @@
 /*
-** $Id: lfunc.c,v 2.39 2014/02/13 12:11:34 roberto Exp roberto $
+** $Id: lfunc.c,v 2.40 2014/02/15 13:12:01 roberto Exp roberto $
 ** Auxiliary functions to manipulate prototypes and closures
 ** See Copyright Notice in lua.h
 */
@@ -35,7 +35,9 @@ Closure *luaF_newLclosure (lua_State *L, int n) {
   return c;
 }
 
-
+/*
+** fill a closure with new closed upvalues
+*/
 void luaF_initupvals (lua_State *L, LClosure *cl) {
   int i;
   for (i = 0; i < cl->nupvalues; i++) {
@@ -52,18 +54,24 @@ UpVal *luaF_findupval (lua_State *L, StkId level) {
   UpVal **pp = &L->openupval;
   UpVal *p;
   UpVal *uv;
+  lua_assert(isintwups(L) || L->openupval == NULL);
   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.open.next;
   }
-  /* not found: create a new one */
+  /* not found: create a new upvalue */
   uv = luaM_new(L, UpVal);
   uv->refcount = 0;
-  uv->u.open.next = *pp;
+  uv->u.open.next = *pp;  /* link it to list of open upvalues */
+  uv->u.open.touched = 1;
   *pp = uv;
   uv->v = level;  /* current value lives in the stack */
+  if (!isintwups(L)) {  /* thread not in list of threads with upvalues? */
+    L->twups = G(L)->twups;  /* link it to the list */
+    G(L)->twups = L;
+  }
   return uv;
 }
 

+ 5 - 1
lfunc.h

@@ -1,5 +1,5 @@
 /*
-** $Id: lfunc.h,v 2.11 2013/09/11 15:17:00 roberto Exp roberto $
+** $Id: lfunc.h,v 2.12 2014/02/15 13:12:01 roberto Exp roberto $
 ** Auxiliary functions to manipulate prototypes and closures
 ** See Copyright Notice in lua.h
 */
@@ -18,6 +18,10 @@
                          cast(int, sizeof(TValue *)*((n)-1)))
 
 
+/* test whether thread is in 'twups' list */
+#define isintwups(L)	(L->twups != L)
+
+
 /*
 ** Upvalues for Lua closures
 */

+ 35 - 10
lgc.c

@@ -1,5 +1,5 @@
 /*
-** $Id: lgc.c,v 2.174 2014/02/14 16:43:14 roberto Exp roberto $
+** $Id: lgc.c,v 2.175 2014/02/15 13:12:01 roberto Exp roberto $
 ** Garbage Collector
 ** See Copyright Notice in lua.h
 */
@@ -23,6 +23,11 @@
 #include "ltm.h"
 
 
+/*
+** internal state for collector while inside the atomic phase. The
+** collector should never be in this state while running regular code.
+*/
+#define GCSinsideatomic		(GCSpause + 1)
 
 /*
 ** cost of sweeping one element (the size of a small object divided
@@ -288,15 +293,21 @@ static void markbeingfnz (global_State *g) {
 /*
 ** Mark all values stored in marked open upvalues from non-marked threads.
 ** (Values from marked threads were already marked when traversing the
-** thread.)
+** thread.) Remove from the list threads that no longer have upvalues and
+** not-marked threads.
 */
 static void remarkupvals (global_State *g) {
-  GCObject *thread = g->mainthread->next;
-  for (; thread != NULL; thread = gch(thread)->next) {
-    lua_assert(!isblack(thread));  /* threads are never black */
-    if (!isgray(thread)) {  /* dead thread? */
-      UpVal *uv = gco2th(thread)->openupval;
-      for (; uv != NULL; uv = uv->u.open.next) {
+  lua_State *thread;
+  lua_State **p = &g->twups;
+  while ((thread = *p) != NULL) {
+    lua_assert(!isblack(obj2gco(thread)));  /* threads are never black */
+    if (isgray(obj2gco(thread)) && thread->openupval != NULL)
+      p = &thread->twups;  /* keep marked thread with upvalues in the list */
+    else {  /* thread is not marked or without upvalues */
+      UpVal *uv;
+      *p = thread->twups;  /* remove thread from the list */
+      thread->twups = thread;  /* mark that it is out of list */
+      for (uv = thread->openupval; uv != NULL; uv = uv->u.open.next) {
         if (uv->u.open.touched) {
           markvalue(g, uv->v);  /* remark upvalue's value */
           uv->u.open.touched = 0;
@@ -459,13 +470,19 @@ static lu_mem traverseCclosure (global_State *g, CClosure *cl) {
   return sizeCclosure(cl->nupvalues);
 }
 
+/*
+** open upvalues point to values in a thread, so those values should
+** be marked when the thread is traversed except in the atomic phase
+** (because then the value cannot be changed by the thread and the
+** thread may not be traversed again)
+*/
 static lu_mem traverseLclosure (global_State *g, LClosure *cl) {
   int i;
   markobject(g, cl->p);  /* mark its prototype */
   for (i = 0; i < cl->nupvalues; i++) {  /* mark its upvalues */
     UpVal *uv = cl->upvals[i];
     if (uv != NULL) {
-      if (upisopen(uv))
+      if (upisopen(uv) && g->gcstate != GCSinsideatomic)
         uv->u.open.touched = 1;  /* can be marked in 'remarkupvals' */
       else
         markvalue(g, uv->v);
@@ -480,12 +497,19 @@ static lu_mem traversestack (global_State *g, lua_State *th) {
   StkId o = th->stack;
   if (o == NULL)
     return 1;  /* stack not completely built yet */
+  lua_assert(g->gcstate == GCSinsideatomic ||
+             th->openupval == NULL || isintwups(th));
   for (; o < th->top; o++)  /* mark live elements in the stack */
     markvalue(g, o);
-  if (g->gcstate == GCSatomic) {  /* final traversal? */
+  if (g->gcstate == GCSinsideatomic) {  /* final traversal? */
     StkId lim = th->stack + th->stacksize;  /* real end of stack */
     for (; o < lim; o++)  /* clear not-marked stack slice */
       setnilvalue(o);
+    /* 'remarkupvals' may have removed thread from 'twups' list */ 
+    if (!isintwups(th) && th->openupval != NULL) {
+      th->twups = g->twups;  /* link it back to the list */
+      g->twups = th;
+    }
   }
   else {
     CallInfo *ci;
@@ -941,6 +965,7 @@ static l_mem atomic (lua_State *L) {
   l_mem work = -cast(l_mem, g->GCmemtrav);  /* start counting work */
   GCObject *origweak, *origall;
   lua_assert(!iswhite(obj2gco(g->mainthread)));
+  g->gcstate = GCSinsideatomic;
   markobject(g, L);  /* mark running thread */
   /* registry and global metatables may be changed by API */
   markvalue(g, &g->l_registry);

+ 3 - 1
lstate.c

@@ -1,5 +1,5 @@
 /*
-** $Id: lstate.c,v 2.118 2014/02/13 12:11:34 roberto Exp roberto $
+** $Id: lstate.c,v 2.119 2014/02/13 14:46:38 roberto Exp roberto $
 ** Global State
 ** See Copyright Notice in lua.h
 */
@@ -222,6 +222,7 @@ static void preinit_thread (lua_State *L, global_State *g) {
   L->stack = NULL;
   L->ci = NULL;
   L->stacksize = 0;
+  L->twups = L;  /* thread has no upvalues */
   L->errorJmp = NULL;
   L->nCcalls = 0;
   L->hook = NULL;
@@ -317,6 +318,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
   g->sweepgc = NULL;
   g->gray = g->grayagain = NULL;
   g->weak = g->ephemeron = g->allweak = NULL;
+  g->twups = NULL;
   g->totalbytes = sizeof(LG);
   g->GCdebt = 0;
   g->gcfinnum = 0;

+ 3 - 1
lstate.h

@@ -1,5 +1,5 @@
 /*
-** $Id: lstate.h,v 2.99 2014/02/13 12:11:34 roberto Exp roberto $
+** $Id: lstate.h,v 2.100 2014/02/13 14:46:38 roberto Exp roberto $
 ** Global State
 ** See Copyright Notice in lua.h
 */
@@ -124,6 +124,7 @@ typedef struct global_State {
   GCObject *allweak;  /* list of all-weak tables */
   GCObject *tobefnz;  /* list of userdata to be GC */
   GCObject *fixedgc;  /* list of objects not to be collected */
+  struct lua_State *twups;  /* list of threads with open upvalues */
   Mbuffer buff;  /* temporary buffer for string concatenation */
   unsigned int gcfinnum;  /* number of finalizers to call in each GC step */
   int gcpause;  /* size of pause between successive GCs */
@@ -159,6 +160,7 @@ struct lua_State {
   lua_Hook hook;
   UpVal *openupval;  /* list of open upvalues in this stack */
   GCObject *gclist;
+  struct lua_State *twups;  /* list of threads with open upvalues */
   struct lua_longjmp *errorJmp;  /* current error recover point */
   ptrdiff_t errfunc;  /* current error handling function (stack index) */
   CallInfo base_ci;  /* CallInfo for first level (C calling Lua) */