aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2020-08-17 15:38:51 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2020-08-17 15:38:51 -0300
commit8a89da07baf43f0dea4b06a7b2780302d75cb61c (patch)
treecd6874562ab435b8da04a8c96ea6560dea98b293
parentf849885a4b49f2d766e6befc67475c05240023eb (diff)
downloadlua-8a89da07baf43f0dea4b06a7b2780302d75cb61c.tar.gz
lua-8a89da07baf43f0dea4b06a7b2780302d75cb61c.tar.bz2
lua-8a89da07baf43f0dea4b06a7b2780302d75cb61c.zip
Better control of gray objects
Avoid turning an object to gray except at the moment it is inserted in a gray list or in the explicit exceptional cases such as open upvalues and fixed strings.
-rw-r--r--lgc.c94
1 files changed, 50 insertions, 44 deletions
diff --git a/lgc.c b/lgc.c
index be125dd7..4a7bcaed 100644
--- a/lgc.c
+++ b/lgc.c
@@ -75,6 +75,11 @@
75#define set2gray(x) resetbits(x->marked, maskcolors) 75#define set2gray(x) resetbits(x->marked, maskcolors)
76 76
77 77
78/* make an object black (coming from any color) */
79#define set2black(x) \
80 (x->marked = cast_byte((x->marked & ~WHITEBITS) | bitmask(BLACKBIT)))
81
82
78#define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x))) 83#define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x)))
79 84
80#define keyiswhite(n) (keyiscollectable(n) && iswhite(gckey(n))) 85#define keyiswhite(n) (keyiscollectable(n) && iswhite(gckey(n)))
@@ -135,15 +140,23 @@ static GCObject **getgclist (GCObject *o) {
135 140
136 141
137/* 142/*
138** Link a collectable object 'o' with a known type into list pointed by 'p'. 143** Link a collectable object 'o' with a known type into the list 'p'.
144** (Must be a macro to access the 'gclist' field in different types.)
139*/ 145*/
140#define linkgclist(o,p) ((o)->gclist = (p), (p) = obj2gco(o)) 146#define linkgclist(o,p) linkgclist_(obj2gco(o), &(o)->gclist, &(p))
147
148static void linkgclist_ (GCObject *o, GCObject **pnext, GCObject **list) {
149 lua_assert(!isgray(o)); /* cannot be in a gray list */
150 *pnext = *list;
151 *list = o;
152 set2gray(o); /* now it is */
153}
141 154
142 155
143/* 156/*
144** Link a generic collectable object 'o' into list pointed by 'p'. 157** Link a generic collectable object 'o' into the list 'p'.
145*/ 158*/
146#define linkobjgclist(o,p) (*getgclist(o) = (p), (p) = obj2gco(o)) 159#define linkobjgclist(o,p) linkgclist_(obj2gco(o), getgclist(o), &(p))
147 160
148 161
149 162
@@ -219,9 +232,10 @@ void luaC_barrierback_ (lua_State *L, GCObject *o) {
219 global_State *g = G(L); 232 global_State *g = G(L);
220 lua_assert(isblack(o) && !isdead(g, o)); 233 lua_assert(isblack(o) && !isdead(g, o));
221 lua_assert((g->gckind == KGC_GEN) == (isold(o) && getage(o) != G_TOUCHED1)); 234 lua_assert((g->gckind == KGC_GEN) == (isold(o) && getage(o) != G_TOUCHED1));
222 if (getage(o) != G_TOUCHED2) /* not already in gray list? */ 235 if (getage(o) == G_TOUCHED2) /* already in gray list? */
223 linkobjgclist(o, g->grayagain); /* link it in 'grayagain' */ 236 set2gray(o); /* make it gray to become touched1 */
224 set2gray(o); /* make object gray (again) */ 237 else /* link it in 'grayagain' and paint it gray */
238 linkobjgclist(o, g->grayagain);
225 if (isold(o)) /* generational mode? */ 239 if (isold(o)) /* generational mode? */
226 setage(o, G_TOUCHED1); /* touched in current cycle */ 240 setage(o, G_TOUCHED1); /* touched in current cycle */
227} 241}
@@ -264,25 +278,30 @@ GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) {
264 278
265 279
266/* 280/*
267** Mark an object. Userdata, strings, and closed upvalues are visited 281** Mark an object. Userdata with no user values, strings, and closed
268** and turned black here. Other objects are marked gray and added 282** upvalues are visited and turned black here. Open upvalues are
269** to appropriate list to be visited (and turned black) later. (Open 283** already indirectly linked through their respective threads in the
270** upvalues are already indirectly linked through the 'twups' list. They 284** 'twups' list, so they don't go to the gray list; nevertheless, they
271** are kept gray to avoid barriers, as their values will be revisited by 285** are kept gray to avoid barriers, as their values will be revisited
272** the thread or by 'remarkupvals'.) 286** by the thread or by 'remarkupvals'. Other objects are added to the
287** gray list to be visited (and turned black) later. Both userdata and
288** upvalues can call this function recursively, but this recursion goes
289** for at most two levels: An upvalue cannot refer to another upvalue
290** (only closures can), and a userdata's metatable must be a table.
273*/ 291*/
274static void reallymarkobject (global_State *g, GCObject *o) { 292static void reallymarkobject (global_State *g, GCObject *o) {
275 set2gray(o);
276 switch (o->tt) { 293 switch (o->tt) {
277 case LUA_VSHRSTR: 294 case LUA_VSHRSTR:
278 case LUA_VLNGSTR: { 295 case LUA_VLNGSTR: {
279 nw2black(o); /* nothing to visit */ 296 set2black(o); /* nothing to visit */
280 break; 297 break;
281 } 298 }
282 case LUA_VUPVAL: { 299 case LUA_VUPVAL: {
283 UpVal *uv = gco2upv(o); 300 UpVal *uv = gco2upv(o);
284 if (!upisopen(uv)) /* open upvalues are kept gray */ 301 if (upisopen(uv))
285 nw2black(o); /* closed upvalues are visited here */ 302 set2gray(uv); /* open upvalues are kept gray */
303 else
304 set2black(o); /* closed upvalues are visited here */
286 markvalue(g, uv->v); /* mark its content */ 305 markvalue(g, uv->v); /* mark its content */
287 break; 306 break;
288 } 307 }
@@ -290,7 +309,7 @@ static void reallymarkobject (global_State *g, GCObject *o) {
290 Udata *u = gco2u(o); 309 Udata *u = gco2u(o);
291 if (u->nuvalue == 0) { /* no user values? */ 310 if (u->nuvalue == 0) { /* no user values? */
292 markobjectN(g, u->metatable); /* mark its metatable */ 311 markobjectN(g, u->metatable); /* mark its metatable */
293 nw2black(o); /* nothing else to mark */ 312 set2black(o); /* nothing else to mark */
294 break; 313 break;
295 } 314 }
296 /* else... */ 315 /* else... */
@@ -402,17 +421,11 @@ static void restartcollection (global_State *g) {
402** TOUCHED1 objects need to be in the list. TOUCHED2 doesn't need to go 421** TOUCHED1 objects need to be in the list. TOUCHED2 doesn't need to go
403** back to a gray list, but then it must become OLD. (That is what 422** back to a gray list, but then it must become OLD. (That is what
404** 'correctgraylist' does when it finds a TOUCHED2 object.) 423** 'correctgraylist' does when it finds a TOUCHED2 object.)
405** It is defined as a macro because 'gclist' is not a unique field in
406** different collectable objects.
407*/ 424*/
408#define genlink(g,o) genlink_(g, obj2gco(o), &(o)->gclist) 425static void genlink (global_State *g, GCObject *o) {
409
410static void genlink_ (global_State *g, GCObject *o, GCObject **pnext) {
411 lua_assert(isblack(o)); 426 lua_assert(isblack(o));
412 if (getage(o) == G_TOUCHED1) { /* touched in this cycle? */ 427 if (getage(o) == G_TOUCHED1) { /* touched in this cycle? */
413 *pnext = g->grayagain; /* link it back in 'grayagain' */ 428 linkobjgclist(o, g->grayagain); /* link it back in 'grayagain' */
414 g->grayagain = o;
415 set2gray(o);
416 } /* everything else do not need to be linked back */ 429 } /* everything else do not need to be linked back */
417 else if (getage(o) == G_TOUCHED2) 430 else if (getage(o) == G_TOUCHED2)
418 changeage(o, G_TOUCHED2, G_OLD); /* advance age */ 431 changeage(o, G_TOUCHED2, G_OLD); /* advance age */
@@ -496,10 +509,8 @@ static int traverseephemeron (global_State *g, Table *h, int inv) {
496 linkgclist(h, g->ephemeron); /* have to propagate again */ 509 linkgclist(h, g->ephemeron); /* have to propagate again */
497 else if (hasclears) /* table has white keys? */ 510 else if (hasclears) /* table has white keys? */
498 linkgclist(h, g->allweak); /* may have to clean white keys */ 511 linkgclist(h, g->allweak); /* may have to clean white keys */
499 else { 512 else
500 nw2black(h); /* 'genlink' expects black objects */ 513 genlink(g, obj2gco(h)); /* check whether collector still needs to see it */
501 genlink(g, h); /* check whether collector still needs to see it */
502 }
503 return marked; 514 return marked;
504} 515}
505 516
@@ -519,7 +530,7 @@ static void traversestrongtable (global_State *g, Table *h) {
519 markvalue(g, gval(n)); 530 markvalue(g, gval(n));
520 } 531 }
521 } 532 }
522 genlink(g, h); 533 genlink(g, obj2gco(h));
523} 534}
524 535
525 536
@@ -531,7 +542,6 @@ static lu_mem traversetable (global_State *g, Table *h) {
531 (cast_void(weakkey = strchr(svalue(mode), 'k')), 542 (cast_void(weakkey = strchr(svalue(mode), 'k')),
532 cast_void(weakvalue = strchr(svalue(mode), 'v')), 543 cast_void(weakvalue = strchr(svalue(mode), 'v')),
533 (weakkey || weakvalue))) { /* is really weak? */ 544 (weakkey || weakvalue))) { /* is really weak? */
534 set2gray(h); /* turn it back to gray, as it probably goes to a list */
535 if (!weakkey) /* strong keys? */ 545 if (!weakkey) /* strong keys? */
536 traverseweakvalue(g, h); 546 traverseweakvalue(g, h);
537 else if (!weakvalue) /* strong values? */ 547 else if (!weakvalue) /* strong values? */
@@ -550,7 +560,7 @@ static int traverseudata (global_State *g, Udata *u) {
550 markobjectN(g, u->metatable); /* mark its metatable */ 560 markobjectN(g, u->metatable); /* mark its metatable */
551 for (i = 0; i < u->nuvalue; i++) 561 for (i = 0; i < u->nuvalue; i++)
552 markvalue(g, &u->uv[i].uv); 562 markvalue(g, &u->uv[i].uv);
553 genlink(g, u); 563 genlink(g, obj2gco(u));
554 return 1 + u->nuvalue; 564 return 1 + u->nuvalue;
555} 565}
556 566
@@ -612,10 +622,8 @@ static int traverseLclosure (global_State *g, LClosure *cl) {
612static int traversethread (global_State *g, lua_State *th) { 622static int traversethread (global_State *g, lua_State *th) {
613 UpVal *uv; 623 UpVal *uv;
614 StkId o = th->stack; 624 StkId o = th->stack;
615 if (isold(th) || g->gcstate == GCSpropagate) { 625 if (isold(th) || g->gcstate == GCSpropagate)
616 linkgclist(th, g->grayagain); /* insert into 'grayagain' list */ 626 linkgclist(th, g->grayagain); /* insert into 'grayagain' list */
617 set2gray(th);
618 }
619 if (o == NULL) 627 if (o == NULL)
620 return 1; /* stack not completely built yet */ 628 return 1; /* stack not completely built yet */
621 lua_assert(g->gcstate == GCSatomic || 629 lua_assert(g->gcstate == GCSatomic ||
@@ -641,8 +649,7 @@ static int traversethread (global_State *g, lua_State *th) {
641 649
642 650
643/* 651/*
644** traverse one gray object, turning it to black (except for threads, 652** traverse one gray object, turning it to black.
645** which are always gray).
646*/ 653*/
647static lu_mem propagatemark (global_State *g) { 654static lu_mem propagatemark (global_State *g) {
648 GCObject *o = g->gray; 655 GCObject *o = g->gray;
@@ -684,8 +691,10 @@ static void convergeephemerons (global_State *g) {
684 g->ephemeron = NULL; /* tables may return to this list when traversed */ 691 g->ephemeron = NULL; /* tables may return to this list when traversed */
685 changed = 0; 692 changed = 0;
686 while ((w = next) != NULL) { /* for each ephemeron table */ 693 while ((w = next) != NULL) { /* for each ephemeron table */
687 next = gco2t(w)->gclist; /* list is rebuilt during loop */ 694 Table *h = gco2t(w);
688 if (traverseephemeron(g, gco2t(w), dir)) { /* marked some value? */ 695 next = h->gclist; /* list is rebuilt during loop */
696 nw2black(h); /* out of the list (for now) */
697 if (traverseephemeron(g, h, dir)) { /* marked some value? */
689 propagateall(g); /* propagate changes */ 698 propagateall(g); /* propagate changes */
690 changed = 1; /* will have to revisit all ephemeron tables */ 699 changed = 1; /* will have to revisit all ephemeron tables */
691 } 700 }
@@ -1048,7 +1057,6 @@ static void sweep2old (lua_State *L, GCObject **p) {
1048 if (curr->tt == LUA_VTHREAD) { /* threads must be watched */ 1057 if (curr->tt == LUA_VTHREAD) { /* threads must be watched */
1049 lua_State *th = gco2th(curr); 1058 lua_State *th = gco2th(curr);
1050 linkgclist(th, g->grayagain); /* insert into 'grayagain' list */ 1059 linkgclist(th, g->grayagain); /* insert into 'grayagain' list */
1051 set2gray(th);
1052 } 1060 }
1053 else if (curr->tt == LUA_VUPVAL && upisopen(gco2upv(curr))) 1061 else if (curr->tt == LUA_VUPVAL && upisopen(gco2upv(curr)))
1054 set2gray(curr); /* open upvalues are always gray */ 1062 set2gray(curr); /* open upvalues are always gray */
@@ -1183,10 +1191,8 @@ static void markold (global_State *g, GCObject *from, GCObject *to) {
1183 if (getage(p) == G_OLD1) { 1191 if (getage(p) == G_OLD1) {
1184 lua_assert(!iswhite(p)); 1192 lua_assert(!iswhite(p));
1185 changeage(p, G_OLD1, G_OLD); /* now they are old */ 1193 changeage(p, G_OLD1, G_OLD); /* now they are old */
1186 if (isblack(p)) { 1194 if (isblack(p))
1187 set2gray(p); /* should be '2white', but gray works too */
1188 reallymarkobject(g, p); 1195 reallymarkobject(g, p);
1189 }
1190 } 1196 }
1191 } 1197 }
1192} 1198}