From 2376eb634751f92e6bcb9dc8dbc1ef88b9873319 Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Thu, 4 May 2017 10:32:01 -0300 Subject: [PATCH] barrier for prototype's cache (with new gray list 'protogray' to keep prototypes to have their caches visited again) + constant 'MAXMISS' --- lfunc.h | 9 ++++++- lgc.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++------ lgc.h | 6 ++++- lstate.c | 4 +-- lstate.h | 6 +++-- ltests.c | 5 +++- lvm.c | 12 ++++----- 7 files changed, 95 insertions(+), 22 deletions(-) diff --git a/lfunc.h b/lfunc.h index 7d0eca4a..eca83e4f 100644 --- a/lfunc.h +++ b/lfunc.h @@ -1,5 +1,5 @@ /* -** $Id: lfunc.h,v 2.15 2015/01/13 15:49:11 roberto Exp roberto $ +** $Id: lfunc.h,v 2.16 2017/04/11 18:41:09 roberto Exp roberto $ ** Auxiliary functions to manipulate prototypes and closures ** See Copyright Notice in lua.h */ @@ -32,6 +32,13 @@ #define upisopen(up) ((up)->v != &(up)->u.value) +/* +** maximum number of misses before giving up the cache of closures +** in prototypes +*/ +#define MAXMISS 10 + + LUAI_FUNC Proto *luaF_newproto (lua_State *L); LUAI_FUNC CClosure *luaF_newCclosure (lua_State *L, int nelems); LUAI_FUNC LClosure *luaF_newLclosure (lua_State *L, int nelems); diff --git a/lgc.c b/lgc.c index 65f4909c..a0253405 100644 --- a/lgc.c +++ b/lgc.c @@ -1,5 +1,5 @@ /* -** $Id: lgc.c,v 2.226 2017/04/24 17:52:18 roberto Exp roberto $ +** $Id: lgc.c,v 2.227 2017/04/30 20:43:26 roberto Exp roberto $ ** Garbage Collector ** See Copyright Notice in lua.h */ @@ -179,6 +179,26 @@ void luaC_barrierback_ (lua_State *L, Table *t) { } +/* +** Barrier for prototype's cache of closures. For an 'old1' +** object, making it gray stops it from being visited by 'markold', +** so it is linked in the 'grayagain' list to ensure it will be +** visited. Otherwise, it goes to 'protogray', as only its 'cache' field +** needs to be revisited. (A prototype to be in this barrier must be +** already finished, so its other fields cannot change and do not need +** to be revisited.) +*/ +LUAI_FUNC void luaC_protobarrier_ (lua_State *L, Proto *p) { + global_State *g = G(L); + lua_assert(g->gckind != KGC_GEN || isold(p)); + if (getage(p) == G_OLD1) /* still need to be visited? */ + linkgclist(p, g->grayagain); /* link it in 'grayagain' */ + else + linkgclist(p, g->protogray); /* link it in 'protogray' */ + black2gray(p); /* make prototype gray (to avoid other barriers) */ +} + + void luaC_fix (lua_State *L, GCObject *o) { global_State *g = G(L); lua_assert(g->allgc == o); /* object must be 1st in 'allgc' list! */ @@ -332,7 +352,7 @@ static void remarkupvals (global_State *g) { */ static void restartcollection (global_State *g) { g->gray = g->grayagain = NULL; - g->weak = g->allweak = g->ephemeron = NULL; + g->weak = g->allweak = g->ephemeron = g->protogray = NULL; markobject(g, g->mainthread); markvalue(g, &g->l_registry); markmt(g); @@ -476,6 +496,32 @@ static lu_mem traversetable (global_State *g, Table *h) { } +/* +** Check the cache of a prototype, to keep invariants. If the +** cache is white, clear it. (A cache should not prevent the +** collection of its reference.) Otherwise, if in generational +** mode, check the generational invariant. If the cache is old, +** everything is ok. If the prototype is 'old0', everything +** is ok too. (It will naturally be visited again.) If the +** prototype is older than 'old0', then its cache (whith is new) +** must be visited again in the next collection, so the prototype +** goes to the 'protogray' list. (If the prototype has a cache, +** it is already immutable and does not need other barriers; +** then, it can become gray without problems for its other fields.) +*/ +static void checkprotocache (global_State *g, Proto *p) { + if (p->cache) { + if (iswhite(p->cache)) + p->cache = NULL; /* allow cache to be collected */ + else if (g->gckind == KGC_GEN && !isold(p->cache) && getage(p) >= G_OLD1) { + linkgclist(p, g->protogray); /* link it in 'protogray' */ + black2gray(p); /* make prototype gray */ + } + } + p->cachemiss = 0; /* restart counting */ +} + + /* ** Traverse a prototype. (While a prototype is being build, its ** arrays can be larger than needed; the extra slots are filled with @@ -483,9 +529,7 @@ static lu_mem traversetable (global_State *g, Table *h) { */ static int traverseproto (global_State *g, Proto *f) { int i; - if (f->cache && iswhite(f->cache)) - f->cache = NULL; /* allow cache to be collected */ - f->cachemiss = 0; /* restart counting */ + checkprotocache(g, f); markobjectN(g, f->source); for (i = 0; i < f->sizek; i++) /* mark literals */ markvalue(g, &f->k[i]); @@ -629,6 +673,19 @@ static void convergeephemerons (global_State *g) { ** ======================================================= */ +static void clearprotolist (global_State *g) { + GCObject *p = g->protogray; + g->protogray = NULL; + while (p != NULL) { + Proto *pp = gco2p(p); + GCObject *next = pp->gclist; + lua_assert(isgray(pp) && (pp->cache != NULL || pp->cachemiss >= MAXMISS)); + gray2black(pp); + checkprotocache(g, pp); + p = next; + } +} + /* ** clear entries with unmarked keys from all weaktables in list 'l' @@ -1073,14 +1130,15 @@ static void correctgraylists (global_State *g) { /* -** Mark 'old1' objects when starting a new young collection. (Threads -** and open upvalues are always gray, and do not need to be marked. -** All other old objects are black.) +** Mark 'old1' objects when starting a new young collection. +** Gray objects are already in some gray list, and so will be visited +** in the atomic step. */ static void markold (global_State *g, GCObject *from, GCObject *to) { GCObject *p; for (p = from; p != to; p = p->next) { if (getage(p) == G_OLD1) { + lua_assert(!iswhite(p)); if (isblack(p)) { black2gray(p); /* should be '2white', but gray works too */ reallymarkobject(g, p); @@ -1337,6 +1395,7 @@ static l_mem atomic (lua_State *L) { clearvalues(g, g->weak, origweak); clearvalues(g, g->allweak, origall); luaS_clearcache(g); + clearprotolist(g); g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */ lua_assert(g->gray == NULL); work += g->GCmemtrav; /* complete counting */ diff --git a/lgc.h b/lgc.h index 396f5a4e..6ee0b012 100644 --- a/lgc.h +++ b/lgc.h @@ -1,5 +1,5 @@ /* -** $Id: lgc.h,v 2.95 2017/04/10 13:33:04 roberto Exp roberto $ +** $Id: lgc.h,v 2.96 2017/04/11 18:41:09 roberto Exp roberto $ ** Garbage Collector ** See Copyright Notice in lua.h */ @@ -151,6 +151,9 @@ (isblack(p) && iswhite(o)) ? \ luaC_barrier_(L,obj2gco(p),obj2gco(o)) : cast_void(0)) +#define luaC_protobarrier(L,p,o) \ + (isblack(p) ? luaC_protobarrier_(L,p) : cast_void(0)) + LUAI_FUNC void luaC_fix (lua_State *L, GCObject *o); LUAI_FUNC void luaC_freeallobjects (lua_State *L); LUAI_FUNC void luaC_step (lua_State *L); @@ -159,6 +162,7 @@ LUAI_FUNC void luaC_fullgc (lua_State *L, int isemergency); 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_barrierback_ (lua_State *L, Table *o); +LUAI_FUNC void luaC_protobarrier_ (lua_State *L, Proto *p); LUAI_FUNC void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt); LUAI_FUNC void luaC_changemode (lua_State *L, int newmode); diff --git a/lstate.c b/lstate.c index 653c90f2..3bce076d 100644 --- a/lstate.c +++ b/lstate.c @@ -1,5 +1,5 @@ /* -** $Id: lstate.c,v 2.137 2017/04/12 18:56:25 roberto Exp roberto $ +** $Id: lstate.c,v 2.138 2017/04/24 16:59:26 roberto Exp roberto $ ** Global State ** See Copyright Notice in lua.h */ @@ -324,7 +324,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { g->finobjsur = g->finobjold = g->finobjrold = NULL; g->sweepgc = NULL; g->gray = g->grayagain = NULL; - g->weak = g->ephemeron = g->allweak = NULL; + g->weak = g->ephemeron = g->allweak = g->protogray = NULL; g->twups = NULL; g->totalbytes = sizeof(LG); g->GCdebt = 0; diff --git a/lstate.h b/lstate.h index d2f23839..533d6ad6 100644 --- a/lstate.h +++ b/lstate.h @@ -1,5 +1,5 @@ /* -** $Id: lstate.h,v 2.138 2017/04/19 17:02:50 roberto Exp roberto $ +** $Id: lstate.h,v 2.139 2017/04/24 16:59:26 roberto Exp roberto $ ** Global State ** See Copyright Notice in lua.h */ @@ -43,7 +43,8 @@ ** 'weak': tables with weak values to be cleared; ** 'ephemeron': ephemeron tables with white->white entries; ** 'allweak': tables with weak keys and/or weak values to be cleared. -** The last three lists are used only during the atomic phase. +** There is also a list 'protogray' for prototypes that need to have +** their caches cleared. */ @@ -159,6 +160,7 @@ typedef struct global_State { GCObject *weak; /* list of tables with weak values */ GCObject *ephemeron; /* list of ephemeron tables (weak keys) */ GCObject *allweak; /* list of all-weak tables */ + GCObject *protogray; /* list of prototypes with "new" caches */ GCObject *tobefnz; /* list of userdata to be GC */ GCObject *fixedgc; /* list of objects not to be collected */ /* fields for generational collector */ diff --git a/ltests.c b/ltests.c index 4ac2c702..93a84284 100644 --- a/ltests.c +++ b/ltests.c @@ -1,5 +1,5 @@ /* -** $Id: ltests.c,v 2.215 2017/04/24 16:59:26 roberto Exp roberto $ +** $Id: ltests.c,v 2.216 2017/04/24 18:06:12 roberto Exp roberto $ ** Internal Module for Debugging of the Lua Implementation ** See Copyright Notice in lua.h */ @@ -409,6 +409,8 @@ static void checkobject (global_State *g, GCObject *o, int maybedead, getage(o) == G_TOUCHED1 || getage(o) == G_OLD0 || o->tt == LUA_TTHREAD || + (o->tt == LUA_TPROTO && + (gco2p(o)->cache != NULL || gco2p(o)->cachemiss >= MAXMISS)) || (o->tt == LUA_TUPVAL && upisopen(gco2upv(o)))); } } @@ -446,6 +448,7 @@ static void markgrays (global_State *g) { checkgraylist(g, g->weak); checkgraylist(g, g->ephemeron); checkgraylist(g, g->allweak); + checkgraylist(g, g->protogray); } diff --git a/lvm.c b/lvm.c index 744ca6dd..28833458 100644 --- a/lvm.c +++ b/lvm.c @@ -1,5 +1,5 @@ /* -** $Id: lvm.c,v 2.274 2017/04/28 20:57:45 roberto Exp roberto $ +** $Id: lvm.c,v 2.275 2017/04/30 20:43:26 roberto Exp roberto $ ** Lua virtual machine ** See Copyright Notice in lua.h */ @@ -626,9 +626,7 @@ static LClosure *getcached (Proto *p, UpVal **encup, StkId base) { /* ** create a new Lua closure, push it in the stack, and initialize -** its upvalues. Note that the closure is not cached if prototype is -** already black (which means that 'cache' was already cleared by the -** GC). +** its upvalues. ??? */ static void pushclosure (lua_State *L, Proto *p, UpVal **encup, StkId base, StkId ra) { @@ -645,11 +643,11 @@ static void pushclosure (lua_State *L, Proto *p, UpVal **encup, StkId base, ncl->upvals[i] = encup[uv[i].idx]; /* new closure is white, so we do not need a barrier here */ } - if (p->cachemiss >= 10) /* too many missings? */ + if (p->cachemiss >= MAXMISS) /* too many missings? */ p->cache = NULL; /* give up cache */ else { - if (!isblack(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 */ + luaC_protobarrier(L, p, ncl); p->cachemiss++; } }