diff options
| author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2014-02-18 10:39:37 -0300 |
|---|---|---|
| committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2014-02-18 10:39:37 -0300 |
| commit | d764cc552251fc69207c1bb4b34d3a6a5b7020c6 (patch) | |
| tree | 7b1b6532af3d22da36118cc81b8256ab53b887bc | |
| parent | ffa96d988d60f31591014fe417c27e44fc3116d1 (diff) | |
| download | lua-d764cc552251fc69207c1bb4b34d3a6a5b7020c6.tar.gz lua-d764cc552251fc69207c1bb4b34d3a6a5b7020c6.tar.bz2 lua-d764cc552251fc69207c1bb4b34d3a6a5b7020c6.zip | |
new list 'twups' to allow traversal of upvalues from dead threads
(+ fixed some problems with cycles involving those upvalues)
| -rw-r--r-- | lfunc.c | 16 | ||||
| -rw-r--r-- | lfunc.h | 6 | ||||
| -rw-r--r-- | lgc.c | 45 | ||||
| -rw-r--r-- | lstate.c | 4 | ||||
| -rw-r--r-- | lstate.h | 4 |
5 files changed, 58 insertions, 17 deletions
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | ** $Id: lfunc.c,v 2.39 2014/02/13 12:11:34 roberto Exp roberto $ | 2 | ** $Id: lfunc.c,v 2.40 2014/02/15 13:12:01 roberto Exp roberto $ |
| 3 | ** Auxiliary functions to manipulate prototypes and closures | 3 | ** Auxiliary functions to manipulate prototypes and closures |
| 4 | ** See Copyright Notice in lua.h | 4 | ** See Copyright Notice in lua.h |
| 5 | */ | 5 | */ |
| @@ -35,7 +35,9 @@ Closure *luaF_newLclosure (lua_State *L, int n) { | |||
| 35 | return c; | 35 | return c; |
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | 38 | /* | |
| 39 | ** fill a closure with new closed upvalues | ||
| 40 | */ | ||
| 39 | void luaF_initupvals (lua_State *L, LClosure *cl) { | 41 | void luaF_initupvals (lua_State *L, LClosure *cl) { |
| 40 | int i; | 42 | int i; |
| 41 | for (i = 0; i < cl->nupvalues; i++) { | 43 | for (i = 0; i < cl->nupvalues; i++) { |
| @@ -52,18 +54,24 @@ UpVal *luaF_findupval (lua_State *L, StkId level) { | |||
| 52 | UpVal **pp = &L->openupval; | 54 | UpVal **pp = &L->openupval; |
| 53 | UpVal *p; | 55 | UpVal *p; |
| 54 | UpVal *uv; | 56 | UpVal *uv; |
| 57 | lua_assert(isintwups(L) || L->openupval == NULL); | ||
| 55 | while (*pp != NULL && (p = *pp)->v >= level) { | 58 | while (*pp != NULL && (p = *pp)->v >= level) { |
| 56 | lua_assert(upisopen(p)); | 59 | lua_assert(upisopen(p)); |
| 57 | if (p->v == level) /* found a corresponding upvalue? */ | 60 | if (p->v == level) /* found a corresponding upvalue? */ |
| 58 | return p; /* return it */ | 61 | return p; /* return it */ |
| 59 | pp = &p->u.open.next; | 62 | pp = &p->u.open.next; |
| 60 | } | 63 | } |
| 61 | /* not found: create a new one */ | 64 | /* not found: create a new upvalue */ |
| 62 | uv = luaM_new(L, UpVal); | 65 | uv = luaM_new(L, UpVal); |
| 63 | uv->refcount = 0; | 66 | uv->refcount = 0; |
| 64 | uv->u.open.next = *pp; | 67 | uv->u.open.next = *pp; /* link it to list of open upvalues */ |
| 68 | uv->u.open.touched = 1; | ||
| 65 | *pp = uv; | 69 | *pp = uv; |
| 66 | uv->v = level; /* current value lives in the stack */ | 70 | uv->v = level; /* current value lives in the stack */ |
| 71 | if (!isintwups(L)) { /* thread not in list of threads with upvalues? */ | ||
| 72 | L->twups = G(L)->twups; /* link it to the list */ | ||
| 73 | G(L)->twups = L; | ||
| 74 | } | ||
| 67 | return uv; | 75 | return uv; |
| 68 | } | 76 | } |
| 69 | 77 | ||
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | ** $Id: lfunc.h,v 2.11 2013/09/11 15:17:00 roberto Exp roberto $ | 2 | ** $Id: lfunc.h,v 2.12 2014/02/15 13:12:01 roberto Exp roberto $ |
| 3 | ** Auxiliary functions to manipulate prototypes and closures | 3 | ** Auxiliary functions to manipulate prototypes and closures |
| 4 | ** See Copyright Notice in lua.h | 4 | ** See Copyright Notice in lua.h |
| 5 | */ | 5 | */ |
| @@ -18,6 +18,10 @@ | |||
| 18 | cast(int, sizeof(TValue *)*((n)-1))) | 18 | cast(int, sizeof(TValue *)*((n)-1))) |
| 19 | 19 | ||
| 20 | 20 | ||
| 21 | /* test whether thread is in 'twups' list */ | ||
| 22 | #define isintwups(L) (L->twups != L) | ||
| 23 | |||
| 24 | |||
| 21 | /* | 25 | /* |
| 22 | ** Upvalues for Lua closures | 26 | ** Upvalues for Lua closures |
| 23 | */ | 27 | */ |
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | ** $Id: lgc.c,v 2.174 2014/02/14 16:43:14 roberto Exp roberto $ | 2 | ** $Id: lgc.c,v 2.175 2014/02/15 13:12:01 roberto Exp roberto $ |
| 3 | ** Garbage Collector | 3 | ** Garbage Collector |
| 4 | ** See Copyright Notice in lua.h | 4 | ** See Copyright Notice in lua.h |
| 5 | */ | 5 | */ |
| @@ -23,6 +23,11 @@ | |||
| 23 | #include "ltm.h" | 23 | #include "ltm.h" |
| 24 | 24 | ||
| 25 | 25 | ||
| 26 | /* | ||
| 27 | ** internal state for collector while inside the atomic phase. The | ||
| 28 | ** collector should never be in this state while running regular code. | ||
| 29 | */ | ||
| 30 | #define GCSinsideatomic (GCSpause + 1) | ||
| 26 | 31 | ||
| 27 | /* | 32 | /* |
| 28 | ** cost of sweeping one element (the size of a small object divided | 33 | ** cost of sweeping one element (the size of a small object divided |
| @@ -288,15 +293,21 @@ static void markbeingfnz (global_State *g) { | |||
| 288 | /* | 293 | /* |
| 289 | ** Mark all values stored in marked open upvalues from non-marked threads. | 294 | ** Mark all values stored in marked open upvalues from non-marked threads. |
| 290 | ** (Values from marked threads were already marked when traversing the | 295 | ** (Values from marked threads were already marked when traversing the |
| 291 | ** thread.) | 296 | ** thread.) Remove from the list threads that no longer have upvalues and |
| 297 | ** not-marked threads. | ||
| 292 | */ | 298 | */ |
| 293 | static void remarkupvals (global_State *g) { | 299 | static void remarkupvals (global_State *g) { |
| 294 | GCObject *thread = g->mainthread->next; | 300 | lua_State *thread; |
| 295 | for (; thread != NULL; thread = gch(thread)->next) { | 301 | lua_State **p = &g->twups; |
| 296 | lua_assert(!isblack(thread)); /* threads are never black */ | 302 | while ((thread = *p) != NULL) { |
| 297 | if (!isgray(thread)) { /* dead thread? */ | 303 | lua_assert(!isblack(obj2gco(thread))); /* threads are never black */ |
| 298 | UpVal *uv = gco2th(thread)->openupval; | 304 | if (isgray(obj2gco(thread)) && thread->openupval != NULL) |
| 299 | for (; uv != NULL; uv = uv->u.open.next) { | 305 | p = &thread->twups; /* keep marked thread with upvalues in the list */ |
| 306 | else { /* thread is not marked or without upvalues */ | ||
| 307 | UpVal *uv; | ||
| 308 | *p = thread->twups; /* remove thread from the list */ | ||
| 309 | thread->twups = thread; /* mark that it is out of list */ | ||
| 310 | for (uv = thread->openupval; uv != NULL; uv = uv->u.open.next) { | ||
| 300 | if (uv->u.open.touched) { | 311 | if (uv->u.open.touched) { |
| 301 | markvalue(g, uv->v); /* remark upvalue's value */ | 312 | markvalue(g, uv->v); /* remark upvalue's value */ |
| 302 | uv->u.open.touched = 0; | 313 | uv->u.open.touched = 0; |
| @@ -459,13 +470,19 @@ static lu_mem traverseCclosure (global_State *g, CClosure *cl) { | |||
| 459 | return sizeCclosure(cl->nupvalues); | 470 | return sizeCclosure(cl->nupvalues); |
| 460 | } | 471 | } |
| 461 | 472 | ||
| 473 | /* | ||
| 474 | ** open upvalues point to values in a thread, so those values should | ||
| 475 | ** be marked when the thread is traversed except in the atomic phase | ||
| 476 | ** (because then the value cannot be changed by the thread and the | ||
| 477 | ** thread may not be traversed again) | ||
| 478 | */ | ||
| 462 | static lu_mem traverseLclosure (global_State *g, LClosure *cl) { | 479 | static lu_mem traverseLclosure (global_State *g, LClosure *cl) { |
| 463 | int i; | 480 | int i; |
| 464 | markobject(g, cl->p); /* mark its prototype */ | 481 | markobject(g, cl->p); /* mark its prototype */ |
| 465 | for (i = 0; i < cl->nupvalues; i++) { /* mark its upvalues */ | 482 | for (i = 0; i < cl->nupvalues; i++) { /* mark its upvalues */ |
| 466 | UpVal *uv = cl->upvals[i]; | 483 | UpVal *uv = cl->upvals[i]; |
| 467 | if (uv != NULL) { | 484 | if (uv != NULL) { |
| 468 | if (upisopen(uv)) | 485 | if (upisopen(uv) && g->gcstate != GCSinsideatomic) |
| 469 | uv->u.open.touched = 1; /* can be marked in 'remarkupvals' */ | 486 | uv->u.open.touched = 1; /* can be marked in 'remarkupvals' */ |
| 470 | else | 487 | else |
| 471 | markvalue(g, uv->v); | 488 | markvalue(g, uv->v); |
| @@ -480,12 +497,19 @@ static lu_mem traversestack (global_State *g, lua_State *th) { | |||
| 480 | StkId o = th->stack; | 497 | StkId o = th->stack; |
| 481 | if (o == NULL) | 498 | if (o == NULL) |
| 482 | return 1; /* stack not completely built yet */ | 499 | return 1; /* stack not completely built yet */ |
| 500 | lua_assert(g->gcstate == GCSinsideatomic || | ||
| 501 | th->openupval == NULL || isintwups(th)); | ||
| 483 | for (; o < th->top; o++) /* mark live elements in the stack */ | 502 | for (; o < th->top; o++) /* mark live elements in the stack */ |
| 484 | markvalue(g, o); | 503 | markvalue(g, o); |
| 485 | if (g->gcstate == GCSatomic) { /* final traversal? */ | 504 | if (g->gcstate == GCSinsideatomic) { /* final traversal? */ |
| 486 | StkId lim = th->stack + th->stacksize; /* real end of stack */ | 505 | StkId lim = th->stack + th->stacksize; /* real end of stack */ |
| 487 | for (; o < lim; o++) /* clear not-marked stack slice */ | 506 | for (; o < lim; o++) /* clear not-marked stack slice */ |
| 488 | setnilvalue(o); | 507 | setnilvalue(o); |
| 508 | /* 'remarkupvals' may have removed thread from 'twups' list */ | ||
| 509 | if (!isintwups(th) && th->openupval != NULL) { | ||
| 510 | th->twups = g->twups; /* link it back to the list */ | ||
| 511 | g->twups = th; | ||
| 512 | } | ||
| 489 | } | 513 | } |
| 490 | else { | 514 | else { |
| 491 | CallInfo *ci; | 515 | CallInfo *ci; |
| @@ -941,6 +965,7 @@ static l_mem atomic (lua_State *L) { | |||
| 941 | l_mem work = -cast(l_mem, g->GCmemtrav); /* start counting work */ | 965 | l_mem work = -cast(l_mem, g->GCmemtrav); /* start counting work */ |
| 942 | GCObject *origweak, *origall; | 966 | GCObject *origweak, *origall; |
| 943 | lua_assert(!iswhite(obj2gco(g->mainthread))); | 967 | lua_assert(!iswhite(obj2gco(g->mainthread))); |
| 968 | g->gcstate = GCSinsideatomic; | ||
| 944 | markobject(g, L); /* mark running thread */ | 969 | markobject(g, L); /* mark running thread */ |
| 945 | /* registry and global metatables may be changed by API */ | 970 | /* registry and global metatables may be changed by API */ |
| 946 | markvalue(g, &g->l_registry); | 971 | markvalue(g, &g->l_registry); |
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | ** $Id: lstate.c,v 2.118 2014/02/13 12:11:34 roberto Exp roberto $ | 2 | ** $Id: lstate.c,v 2.119 2014/02/13 14:46:38 roberto Exp roberto $ |
| 3 | ** Global State | 3 | ** Global State |
| 4 | ** See Copyright Notice in lua.h | 4 | ** See Copyright Notice in lua.h |
| 5 | */ | 5 | */ |
| @@ -222,6 +222,7 @@ static void preinit_thread (lua_State *L, global_State *g) { | |||
| 222 | L->stack = NULL; | 222 | L->stack = NULL; |
| 223 | L->ci = NULL; | 223 | L->ci = NULL; |
| 224 | L->stacksize = 0; | 224 | L->stacksize = 0; |
| 225 | L->twups = L; /* thread has no upvalues */ | ||
| 225 | L->errorJmp = NULL; | 226 | L->errorJmp = NULL; |
| 226 | L->nCcalls = 0; | 227 | L->nCcalls = 0; |
| 227 | L->hook = NULL; | 228 | L->hook = NULL; |
| @@ -317,6 +318,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { | |||
| 317 | g->sweepgc = NULL; | 318 | g->sweepgc = NULL; |
| 318 | g->gray = g->grayagain = NULL; | 319 | g->gray = g->grayagain = NULL; |
| 319 | g->weak = g->ephemeron = g->allweak = NULL; | 320 | g->weak = g->ephemeron = g->allweak = NULL; |
| 321 | g->twups = NULL; | ||
| 320 | g->totalbytes = sizeof(LG); | 322 | g->totalbytes = sizeof(LG); |
| 321 | g->GCdebt = 0; | 323 | g->GCdebt = 0; |
| 322 | g->gcfinnum = 0; | 324 | g->gcfinnum = 0; |
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | ** $Id: lstate.h,v 2.99 2014/02/13 12:11:34 roberto Exp roberto $ | 2 | ** $Id: lstate.h,v 2.100 2014/02/13 14:46:38 roberto Exp roberto $ |
| 3 | ** Global State | 3 | ** Global State |
| 4 | ** See Copyright Notice in lua.h | 4 | ** See Copyright Notice in lua.h |
| 5 | */ | 5 | */ |
| @@ -124,6 +124,7 @@ typedef struct global_State { | |||
| 124 | GCObject *allweak; /* list of all-weak tables */ | 124 | GCObject *allweak; /* list of all-weak tables */ |
| 125 | GCObject *tobefnz; /* list of userdata to be GC */ | 125 | GCObject *tobefnz; /* list of userdata to be GC */ |
| 126 | GCObject *fixedgc; /* list of objects not to be collected */ | 126 | GCObject *fixedgc; /* list of objects not to be collected */ |
| 127 | struct lua_State *twups; /* list of threads with open upvalues */ | ||
| 127 | Mbuffer buff; /* temporary buffer for string concatenation */ | 128 | Mbuffer buff; /* temporary buffer for string concatenation */ |
| 128 | unsigned int gcfinnum; /* number of finalizers to call in each GC step */ | 129 | unsigned int gcfinnum; /* number of finalizers to call in each GC step */ |
| 129 | int gcpause; /* size of pause between successive GCs */ | 130 | int gcpause; /* size of pause between successive GCs */ |
| @@ -159,6 +160,7 @@ struct lua_State { | |||
| 159 | lua_Hook hook; | 160 | lua_Hook hook; |
| 160 | UpVal *openupval; /* list of open upvalues in this stack */ | 161 | UpVal *openupval; /* list of open upvalues in this stack */ |
| 161 | GCObject *gclist; | 162 | GCObject *gclist; |
| 163 | struct lua_State *twups; /* list of threads with open upvalues */ | ||
| 162 | struct lua_longjmp *errorJmp; /* current error recover point */ | 164 | struct lua_longjmp *errorJmp; /* current error recover point */ |
| 163 | ptrdiff_t errfunc; /* current error handling function (stack index) */ | 165 | ptrdiff_t errfunc; /* current error handling function (stack index) */ |
| 164 | CallInfo base_ci; /* CallInfo for first level (C calling Lua) */ | 166 | CallInfo base_ci; /* CallInfo for first level (C calling Lua) */ |
