Selaa lähdekoodia

barrier for prototype's cache (with new gray list 'protogray' to keep
prototypes to have their caches visited again) + constant 'MAXMISS'

Roberto Ierusalimschy 8 vuotta sitten
vanhempi
commit
2376eb6347
7 muutettua tiedostoa jossa 95 lisäystä ja 22 poistoa
  1. 8 1
      lfunc.h
  2. 67 8
      lgc.c
  3. 5 1
      lgc.h
  4. 2 2
      lstate.c
  5. 4 2
      lstate.h
  6. 4 1
      ltests.c
  7. 5 7
      lvm.c

+ 8 - 1
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);

+ 67 - 8
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 */

+ 5 - 1
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);
 

+ 2 - 2
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;

+ 4 - 2
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 */

+ 4 - 1
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);
 }
 
 

+ 5 - 7
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++;
   }
 }