From d764cc552251fc69207c1bb4b34d3a6a5b7020c6 Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Tue, 18 Feb 2014 10:39:37 -0300 Subject: [PATCH] new list 'twups' to allow traversal of upvalues from dead threads (+ fixed some problems with cycles involving those upvalues) --- lfunc.c | 16 ++++++++++++---- lfunc.h | 6 +++++- lgc.c | 45 +++++++++++++++++++++++++++++++++++---------- lstate.c | 4 +++- lstate.h | 4 +++- 5 files changed, 58 insertions(+), 17 deletions(-) diff --git a/lfunc.c b/lfunc.c index 685d7bdd..9d0703e1 100644 --- a/lfunc.c +++ b/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; } diff --git a/lfunc.h b/lfunc.h index 9ed51767..3ca72075 100644 --- a/lfunc.h +++ b/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 */ diff --git a/lgc.c b/lgc.c index d51c4f5f..9471b894 100644 --- a/lgc.c +++ b/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); diff --git a/lstate.c b/lstate.c index 4c768c07..bc235941 100644 --- a/lstate.c +++ b/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; diff --git a/lstate.h b/lstate.h index 4acdee84..9df36f32 100644 --- a/lstate.h +++ b/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) */