diff options
| author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2017-05-04 10:32:01 -0300 |
|---|---|---|
| committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2017-05-04 10:32:01 -0300 |
| commit | 2376eb634751f92e6bcb9dc8dbc1ef88b9873319 (patch) | |
| tree | 579780bc7ff3d1374a701298749eca3634a17edb | |
| parent | 8634b2a0119e698e362fdb765f30258e79e1dfd0 (diff) | |
| download | lua-2376eb634751f92e6bcb9dc8dbc1ef88b9873319.tar.gz lua-2376eb634751f92e6bcb9dc8dbc1ef88b9873319.tar.bz2 lua-2376eb634751f92e6bcb9dc8dbc1ef88b9873319.zip | |
barrier for prototype's cache (with new gray list 'protogray' to keep
prototypes to have their caches visited again) + constant 'MAXMISS'
| -rw-r--r-- | lfunc.h | 9 | ||||
| -rw-r--r-- | lgc.c | 75 | ||||
| -rw-r--r-- | lgc.h | 6 | ||||
| -rw-r--r-- | lstate.c | 4 | ||||
| -rw-r--r-- | lstate.h | 6 | ||||
| -rw-r--r-- | ltests.c | 5 | ||||
| -rw-r--r-- | lvm.c | 12 |
7 files changed, 95 insertions, 22 deletions
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | ** $Id: lfunc.h,v 2.15 2015/01/13 15:49:11 roberto Exp roberto $ | 2 | ** $Id: lfunc.h,v 2.16 2017/04/11 18:41:09 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 | */ |
| @@ -32,6 +32,13 @@ | |||
| 32 | #define upisopen(up) ((up)->v != &(up)->u.value) | 32 | #define upisopen(up) ((up)->v != &(up)->u.value) |
| 33 | 33 | ||
| 34 | 34 | ||
| 35 | /* | ||
| 36 | ** maximum number of misses before giving up the cache of closures | ||
| 37 | ** in prototypes | ||
| 38 | */ | ||
| 39 | #define MAXMISS 10 | ||
| 40 | |||
| 41 | |||
| 35 | LUAI_FUNC Proto *luaF_newproto (lua_State *L); | 42 | LUAI_FUNC Proto *luaF_newproto (lua_State *L); |
| 36 | LUAI_FUNC CClosure *luaF_newCclosure (lua_State *L, int nelems); | 43 | LUAI_FUNC CClosure *luaF_newCclosure (lua_State *L, int nelems); |
| 37 | LUAI_FUNC LClosure *luaF_newLclosure (lua_State *L, int nelems); | 44 | LUAI_FUNC LClosure *luaF_newLclosure (lua_State *L, int nelems); |
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | ** $Id: lgc.c,v 2.226 2017/04/24 17:52:18 roberto Exp roberto $ | 2 | ** $Id: lgc.c,v 2.227 2017/04/30 20:43:26 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 | */ |
| @@ -179,6 +179,26 @@ void luaC_barrierback_ (lua_State *L, Table *t) { | |||
| 179 | } | 179 | } |
| 180 | 180 | ||
| 181 | 181 | ||
| 182 | /* | ||
| 183 | ** Barrier for prototype's cache of closures. For an 'old1' | ||
| 184 | ** object, making it gray stops it from being visited by 'markold', | ||
| 185 | ** so it is linked in the 'grayagain' list to ensure it will be | ||
| 186 | ** visited. Otherwise, it goes to 'protogray', as only its 'cache' field | ||
| 187 | ** needs to be revisited. (A prototype to be in this barrier must be | ||
| 188 | ** already finished, so its other fields cannot change and do not need | ||
| 189 | ** to be revisited.) | ||
| 190 | */ | ||
| 191 | LUAI_FUNC void luaC_protobarrier_ (lua_State *L, Proto *p) { | ||
| 192 | global_State *g = G(L); | ||
| 193 | lua_assert(g->gckind != KGC_GEN || isold(p)); | ||
| 194 | if (getage(p) == G_OLD1) /* still need to be visited? */ | ||
| 195 | linkgclist(p, g->grayagain); /* link it in 'grayagain' */ | ||
| 196 | else | ||
| 197 | linkgclist(p, g->protogray); /* link it in 'protogray' */ | ||
| 198 | black2gray(p); /* make prototype gray (to avoid other barriers) */ | ||
| 199 | } | ||
| 200 | |||
| 201 | |||
| 182 | void luaC_fix (lua_State *L, GCObject *o) { | 202 | void luaC_fix (lua_State *L, GCObject *o) { |
| 183 | global_State *g = G(L); | 203 | global_State *g = G(L); |
| 184 | lua_assert(g->allgc == o); /* object must be 1st in 'allgc' list! */ | 204 | lua_assert(g->allgc == o); /* object must be 1st in 'allgc' list! */ |
| @@ -332,7 +352,7 @@ static void remarkupvals (global_State *g) { | |||
| 332 | */ | 352 | */ |
| 333 | static void restartcollection (global_State *g) { | 353 | static void restartcollection (global_State *g) { |
| 334 | g->gray = g->grayagain = NULL; | 354 | g->gray = g->grayagain = NULL; |
| 335 | g->weak = g->allweak = g->ephemeron = NULL; | 355 | g->weak = g->allweak = g->ephemeron = g->protogray = NULL; |
| 336 | markobject(g, g->mainthread); | 356 | markobject(g, g->mainthread); |
| 337 | markvalue(g, &g->l_registry); | 357 | markvalue(g, &g->l_registry); |
| 338 | markmt(g); | 358 | markmt(g); |
| @@ -477,15 +497,39 @@ static lu_mem traversetable (global_State *g, Table *h) { | |||
| 477 | 497 | ||
| 478 | 498 | ||
| 479 | /* | 499 | /* |
| 500 | ** Check the cache of a prototype, to keep invariants. If the | ||
| 501 | ** cache is white, clear it. (A cache should not prevent the | ||
| 502 | ** collection of its reference.) Otherwise, if in generational | ||
| 503 | ** mode, check the generational invariant. If the cache is old, | ||
| 504 | ** everything is ok. If the prototype is 'old0', everything | ||
| 505 | ** is ok too. (It will naturally be visited again.) If the | ||
| 506 | ** prototype is older than 'old0', then its cache (whith is new) | ||
| 507 | ** must be visited again in the next collection, so the prototype | ||
| 508 | ** goes to the 'protogray' list. (If the prototype has a cache, | ||
| 509 | ** it is already immutable and does not need other barriers; | ||
| 510 | ** then, it can become gray without problems for its other fields.) | ||
| 511 | */ | ||
| 512 | static void checkprotocache (global_State *g, Proto *p) { | ||
| 513 | if (p->cache) { | ||
| 514 | if (iswhite(p->cache)) | ||
| 515 | p->cache = NULL; /* allow cache to be collected */ | ||
| 516 | else if (g->gckind == KGC_GEN && !isold(p->cache) && getage(p) >= G_OLD1) { | ||
| 517 | linkgclist(p, g->protogray); /* link it in 'protogray' */ | ||
| 518 | black2gray(p); /* make prototype gray */ | ||
| 519 | } | ||
| 520 | } | ||
| 521 | p->cachemiss = 0; /* restart counting */ | ||
| 522 | } | ||
| 523 | |||
| 524 | |||
| 525 | /* | ||
| 480 | ** Traverse a prototype. (While a prototype is being build, its | 526 | ** Traverse a prototype. (While a prototype is being build, its |
| 481 | ** arrays can be larger than needed; the extra slots are filled with | 527 | ** arrays can be larger than needed; the extra slots are filled with |
| 482 | ** NULL, so the use of 'markobjectN') | 528 | ** NULL, so the use of 'markobjectN') |
| 483 | */ | 529 | */ |
| 484 | static int traverseproto (global_State *g, Proto *f) { | 530 | static int traverseproto (global_State *g, Proto *f) { |
| 485 | int i; | 531 | int i; |
| 486 | if (f->cache && iswhite(f->cache)) | 532 | checkprotocache(g, f); |
| 487 | f->cache = NULL; /* allow cache to be collected */ | ||
| 488 | f->cachemiss = 0; /* restart counting */ | ||
| 489 | markobjectN(g, f->source); | 533 | markobjectN(g, f->source); |
| 490 | for (i = 0; i < f->sizek; i++) /* mark literals */ | 534 | for (i = 0; i < f->sizek; i++) /* mark literals */ |
| 491 | markvalue(g, &f->k[i]); | 535 | markvalue(g, &f->k[i]); |
| @@ -629,6 +673,19 @@ static void convergeephemerons (global_State *g) { | |||
| 629 | ** ======================================================= | 673 | ** ======================================================= |
| 630 | */ | 674 | */ |
| 631 | 675 | ||
| 676 | static void clearprotolist (global_State *g) { | ||
| 677 | GCObject *p = g->protogray; | ||
| 678 | g->protogray = NULL; | ||
| 679 | while (p != NULL) { | ||
| 680 | Proto *pp = gco2p(p); | ||
| 681 | GCObject *next = pp->gclist; | ||
| 682 | lua_assert(isgray(pp) && (pp->cache != NULL || pp->cachemiss >= MAXMISS)); | ||
| 683 | gray2black(pp); | ||
| 684 | checkprotocache(g, pp); | ||
| 685 | p = next; | ||
| 686 | } | ||
| 687 | } | ||
| 688 | |||
| 632 | 689 | ||
| 633 | /* | 690 | /* |
| 634 | ** clear entries with unmarked keys from all weaktables in list 'l' | 691 | ** clear entries with unmarked keys from all weaktables in list 'l' |
| @@ -1073,14 +1130,15 @@ static void correctgraylists (global_State *g) { | |||
| 1073 | 1130 | ||
| 1074 | 1131 | ||
| 1075 | /* | 1132 | /* |
| 1076 | ** Mark 'old1' objects when starting a new young collection. (Threads | 1133 | ** Mark 'old1' objects when starting a new young collection. |
| 1077 | ** and open upvalues are always gray, and do not need to be marked. | 1134 | ** Gray objects are already in some gray list, and so will be visited |
| 1078 | ** All other old objects are black.) | 1135 | ** in the atomic step. |
| 1079 | */ | 1136 | */ |
| 1080 | static void markold (global_State *g, GCObject *from, GCObject *to) { | 1137 | static void markold (global_State *g, GCObject *from, GCObject *to) { |
| 1081 | GCObject *p; | 1138 | GCObject *p; |
| 1082 | for (p = from; p != to; p = p->next) { | 1139 | for (p = from; p != to; p = p->next) { |
| 1083 | if (getage(p) == G_OLD1) { | 1140 | if (getage(p) == G_OLD1) { |
| 1141 | lua_assert(!iswhite(p)); | ||
| 1084 | if (isblack(p)) { | 1142 | if (isblack(p)) { |
| 1085 | black2gray(p); /* should be '2white', but gray works too */ | 1143 | black2gray(p); /* should be '2white', but gray works too */ |
| 1086 | reallymarkobject(g, p); | 1144 | reallymarkobject(g, p); |
| @@ -1337,6 +1395,7 @@ static l_mem atomic (lua_State *L) { | |||
| 1337 | clearvalues(g, g->weak, origweak); | 1395 | clearvalues(g, g->weak, origweak); |
| 1338 | clearvalues(g, g->allweak, origall); | 1396 | clearvalues(g, g->allweak, origall); |
| 1339 | luaS_clearcache(g); | 1397 | luaS_clearcache(g); |
| 1398 | clearprotolist(g); | ||
| 1340 | g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */ | 1399 | g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */ |
| 1341 | lua_assert(g->gray == NULL); | 1400 | lua_assert(g->gray == NULL); |
| 1342 | work += g->GCmemtrav; /* complete counting */ | 1401 | work += g->GCmemtrav; /* complete counting */ |
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | ** $Id: lgc.h,v 2.95 2017/04/10 13:33:04 roberto Exp roberto $ | 2 | ** $Id: lgc.h,v 2.96 2017/04/11 18:41:09 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 | */ |
| @@ -151,6 +151,9 @@ | |||
| 151 | (isblack(p) && iswhite(o)) ? \ | 151 | (isblack(p) && iswhite(o)) ? \ |
| 152 | luaC_barrier_(L,obj2gco(p),obj2gco(o)) : cast_void(0)) | 152 | luaC_barrier_(L,obj2gco(p),obj2gco(o)) : cast_void(0)) |
| 153 | 153 | ||
| 154 | #define luaC_protobarrier(L,p,o) \ | ||
| 155 | (isblack(p) ? luaC_protobarrier_(L,p) : cast_void(0)) | ||
| 156 | |||
| 154 | LUAI_FUNC void luaC_fix (lua_State *L, GCObject *o); | 157 | LUAI_FUNC void luaC_fix (lua_State *L, GCObject *o); |
| 155 | LUAI_FUNC void luaC_freeallobjects (lua_State *L); | 158 | LUAI_FUNC void luaC_freeallobjects (lua_State *L); |
| 156 | LUAI_FUNC void luaC_step (lua_State *L); | 159 | LUAI_FUNC void luaC_step (lua_State *L); |
| @@ -159,6 +162,7 @@ LUAI_FUNC void luaC_fullgc (lua_State *L, int isemergency); | |||
| 159 | LUAI_FUNC GCObject *luaC_newobj (lua_State *L, int tt, size_t sz); | 162 | LUAI_FUNC GCObject *luaC_newobj (lua_State *L, int tt, size_t sz); |
| 160 | LUAI_FUNC void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v); | 163 | LUAI_FUNC void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v); |
| 161 | LUAI_FUNC void luaC_barrierback_ (lua_State *L, Table *o); | 164 | LUAI_FUNC void luaC_barrierback_ (lua_State *L, Table *o); |
| 165 | LUAI_FUNC void luaC_protobarrier_ (lua_State *L, Proto *p); | ||
| 162 | LUAI_FUNC void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt); | 166 | LUAI_FUNC void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt); |
| 163 | LUAI_FUNC void luaC_changemode (lua_State *L, int newmode); | 167 | LUAI_FUNC void luaC_changemode (lua_State *L, int newmode); |
| 164 | 168 | ||
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | ** $Id: lstate.c,v 2.137 2017/04/12 18:56:25 roberto Exp roberto $ | 2 | ** $Id: lstate.c,v 2.138 2017/04/24 16:59:26 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 | */ |
| @@ -324,7 +324,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { | |||
| 324 | g->finobjsur = g->finobjold = g->finobjrold = NULL; | 324 | g->finobjsur = g->finobjold = g->finobjrold = NULL; |
| 325 | g->sweepgc = NULL; | 325 | g->sweepgc = NULL; |
| 326 | g->gray = g->grayagain = NULL; | 326 | g->gray = g->grayagain = NULL; |
| 327 | g->weak = g->ephemeron = g->allweak = NULL; | 327 | g->weak = g->ephemeron = g->allweak = g->protogray = NULL; |
| 328 | g->twups = NULL; | 328 | g->twups = NULL; |
| 329 | g->totalbytes = sizeof(LG); | 329 | g->totalbytes = sizeof(LG); |
| 330 | g->GCdebt = 0; | 330 | g->GCdebt = 0; |
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | ** $Id: lstate.h,v 2.138 2017/04/19 17:02:50 roberto Exp roberto $ | 2 | ** $Id: lstate.h,v 2.139 2017/04/24 16:59:26 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 | */ |
| @@ -43,7 +43,8 @@ | |||
| 43 | ** 'weak': tables with weak values to be cleared; | 43 | ** 'weak': tables with weak values to be cleared; |
| 44 | ** 'ephemeron': ephemeron tables with white->white entries; | 44 | ** 'ephemeron': ephemeron tables with white->white entries; |
| 45 | ** 'allweak': tables with weak keys and/or weak values to be cleared. | 45 | ** 'allweak': tables with weak keys and/or weak values to be cleared. |
| 46 | ** The last three lists are used only during the atomic phase. | 46 | ** There is also a list 'protogray' for prototypes that need to have |
| 47 | ** their caches cleared. | ||
| 47 | 48 | ||
| 48 | */ | 49 | */ |
| 49 | 50 | ||
| @@ -159,6 +160,7 @@ typedef struct global_State { | |||
| 159 | GCObject *weak; /* list of tables with weak values */ | 160 | GCObject *weak; /* list of tables with weak values */ |
| 160 | GCObject *ephemeron; /* list of ephemeron tables (weak keys) */ | 161 | GCObject *ephemeron; /* list of ephemeron tables (weak keys) */ |
| 161 | GCObject *allweak; /* list of all-weak tables */ | 162 | GCObject *allweak; /* list of all-weak tables */ |
| 163 | GCObject *protogray; /* list of prototypes with "new" caches */ | ||
| 162 | GCObject *tobefnz; /* list of userdata to be GC */ | 164 | GCObject *tobefnz; /* list of userdata to be GC */ |
| 163 | GCObject *fixedgc; /* list of objects not to be collected */ | 165 | GCObject *fixedgc; /* list of objects not to be collected */ |
| 164 | /* fields for generational collector */ | 166 | /* fields for generational collector */ |
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | ** $Id: ltests.c,v 2.215 2017/04/24 16:59:26 roberto Exp roberto $ | 2 | ** $Id: ltests.c,v 2.216 2017/04/24 18:06:12 roberto Exp roberto $ |
| 3 | ** Internal Module for Debugging of the Lua Implementation | 3 | ** Internal Module for Debugging of the Lua Implementation |
| 4 | ** See Copyright Notice in lua.h | 4 | ** See Copyright Notice in lua.h |
| 5 | */ | 5 | */ |
| @@ -409,6 +409,8 @@ static void checkobject (global_State *g, GCObject *o, int maybedead, | |||
| 409 | getage(o) == G_TOUCHED1 || | 409 | getage(o) == G_TOUCHED1 || |
| 410 | getage(o) == G_OLD0 || | 410 | getage(o) == G_OLD0 || |
| 411 | o->tt == LUA_TTHREAD || | 411 | o->tt == LUA_TTHREAD || |
| 412 | (o->tt == LUA_TPROTO && | ||
| 413 | (gco2p(o)->cache != NULL || gco2p(o)->cachemiss >= MAXMISS)) || | ||
| 412 | (o->tt == LUA_TUPVAL && upisopen(gco2upv(o)))); | 414 | (o->tt == LUA_TUPVAL && upisopen(gco2upv(o)))); |
| 413 | } | 415 | } |
| 414 | } | 416 | } |
| @@ -446,6 +448,7 @@ static void markgrays (global_State *g) { | |||
| 446 | checkgraylist(g, g->weak); | 448 | checkgraylist(g, g->weak); |
| 447 | checkgraylist(g, g->ephemeron); | 449 | checkgraylist(g, g->ephemeron); |
| 448 | checkgraylist(g, g->allweak); | 450 | checkgraylist(g, g->allweak); |
| 451 | checkgraylist(g, g->protogray); | ||
| 449 | } | 452 | } |
| 450 | 453 | ||
| 451 | 454 | ||
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | ** $Id: lvm.c,v 2.274 2017/04/28 20:57:45 roberto Exp roberto $ | 2 | ** $Id: lvm.c,v 2.275 2017/04/30 20:43:26 roberto Exp roberto $ |
| 3 | ** Lua virtual machine | 3 | ** Lua virtual machine |
| 4 | ** See Copyright Notice in lua.h | 4 | ** See Copyright Notice in lua.h |
| 5 | */ | 5 | */ |
| @@ -626,9 +626,7 @@ static LClosure *getcached (Proto *p, UpVal **encup, StkId base) { | |||
| 626 | 626 | ||
| 627 | /* | 627 | /* |
| 628 | ** create a new Lua closure, push it in the stack, and initialize | 628 | ** create a new Lua closure, push it in the stack, and initialize |
| 629 | ** its upvalues. Note that the closure is not cached if prototype is | 629 | ** its upvalues. ??? |
| 630 | ** already black (which means that 'cache' was already cleared by the | ||
| 631 | ** GC). | ||
| 632 | */ | 630 | */ |
| 633 | static void pushclosure (lua_State *L, Proto *p, UpVal **encup, StkId base, | 631 | static void pushclosure (lua_State *L, Proto *p, UpVal **encup, StkId base, |
| 634 | StkId ra) { | 632 | StkId ra) { |
| @@ -645,11 +643,11 @@ static void pushclosure (lua_State *L, Proto *p, UpVal **encup, StkId base, | |||
| 645 | ncl->upvals[i] = encup[uv[i].idx]; | 643 | ncl->upvals[i] = encup[uv[i].idx]; |
| 646 | /* new closure is white, so we do not need a barrier here */ | 644 | /* new closure is white, so we do not need a barrier here */ |
| 647 | } | 645 | } |
| 648 | if (p->cachemiss >= 10) /* too many missings? */ | 646 | if (p->cachemiss >= MAXMISS) /* too many missings? */ |
| 649 | p->cache = NULL; /* give up cache */ | 647 | p->cache = NULL; /* give up cache */ |
| 650 | else { | 648 | else { |
| 651 | if (!isblack(p)) /* cache will not break GC invariant? */ | 649 | p->cache = ncl; /* save it on cache for reuse */ |
| 652 | p->cache = ncl; /* save it on cache for reuse */ | 650 | luaC_protobarrier(L, p, ncl); |
| 653 | p->cachemiss++; | 651 | p->cachemiss++; |
| 654 | } | 652 | } |
| 655 | } | 653 | } |
