Skip to content

Commit 8a89da0

Browse files
committed
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.
1 parent f849885 commit 8a89da0

File tree

1 file changed

+50
-44
lines changed

1 file changed

+50
-44
lines changed

lgc.c

Lines changed: 50 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,11 @@
7575
#define set2gray(x) resetbits(x->marked, maskcolors)
7676

7777

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+
7883
#define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x)))
7984

8085
#define keyiswhite(n) (keyiscollectable(n) && iswhite(gckey(n)))
@@ -135,15 +140,23 @@ static GCObject **getgclist (GCObject *o) {
135140

136141

137142
/*
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.)
139145
*/
140-
#define linkgclist(o,p) ((o)->gclist = (p), (p) = obj2gco(o))
146+
#define linkgclist(o,p) linkgclist_(obj2gco(o), &(o)->gclist, &(p))
147+
148+
static 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+
}
141154

142155

143156
/*
144-
** Link a generic collectable object 'o' into list pointed by 'p'.
157+
** Link a generic collectable object 'o' into the list 'p'.
145158
*/
146-
#define linkobjgclist(o,p) (*getgclist(o) = (p), (p) = obj2gco(o))
159+
#define linkobjgclist(o,p) linkgclist_(obj2gco(o), getgclist(o), &(p))
147160

148161

149162

@@ -219,9 +232,10 @@ void luaC_barrierback_ (lua_State *L, GCObject *o) {
219232
global_State *g = G(L);
220233
lua_assert(isblack(o) && !isdead(g, o));
221234
lua_assert((g->gckind == KGC_GEN) == (isold(o) && getage(o) != G_TOUCHED1));
222-
if (getage(o) != G_TOUCHED2) /* not already in gray list? */
223-
linkobjgclist(o, g->grayagain); /* link it in 'grayagain' */
224-
set2gray(o); /* make object gray (again) */
235+
if (getage(o) == G_TOUCHED2) /* already in gray list? */
236+
set2gray(o); /* make it gray to become touched1 */
237+
else /* link it in 'grayagain' and paint it gray */
238+
linkobjgclist(o, g->grayagain);
225239
if (isold(o)) /* generational mode? */
226240
setage(o, G_TOUCHED1); /* touched in current cycle */
227241
}
@@ -264,33 +278,38 @@ GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) {
264278

265279

266280
/*
267-
** Mark an object. Userdata, strings, and closed upvalues are visited
268-
** and turned black here. Other objects are marked gray and added
269-
** to appropriate list to be visited (and turned black) later. (Open
270-
** upvalues are already indirectly linked through the 'twups' list. They
271-
** are kept gray to avoid barriers, as their values will be revisited by
272-
** the thread or by 'remarkupvals'.)
281+
** Mark an object. Userdata with no user values, strings, and closed
282+
** upvalues are visited and turned black here. Open upvalues are
283+
** already indirectly linked through their respective threads in the
284+
** 'twups' list, so they don't go to the gray list; nevertheless, they
285+
** are kept gray to avoid barriers, as their values will be revisited
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.
273291
*/
274292
static void reallymarkobject (global_State *g, GCObject *o) {
275-
set2gray(o);
276293
switch (o->tt) {
277294
case LUA_VSHRSTR:
278295
case LUA_VLNGSTR: {
279-
nw2black(o); /* nothing to visit */
296+
set2black(o); /* nothing to visit */
280297
break;
281298
}
282299
case LUA_VUPVAL: {
283300
UpVal *uv = gco2upv(o);
284-
if (!upisopen(uv)) /* open upvalues are kept gray */
285-
nw2black(o); /* closed upvalues are visited here */
301+
if (upisopen(uv))
302+
set2gray(uv); /* open upvalues are kept gray */
303+
else
304+
set2black(o); /* closed upvalues are visited here */
286305
markvalue(g, uv->v); /* mark its content */
287306
break;
288307
}
289308
case LUA_VUSERDATA: {
290309
Udata *u = gco2u(o);
291310
if (u->nuvalue == 0) { /* no user values? */
292311
markobjectN(g, u->metatable); /* mark its metatable */
293-
nw2black(o); /* nothing else to mark */
312+
set2black(o); /* nothing else to mark */
294313
break;
295314
}
296315
/* else... */
@@ -402,17 +421,11 @@ static void restartcollection (global_State *g) {
402421
** TOUCHED1 objects need to be in the list. TOUCHED2 doesn't need to go
403422
** back to a gray list, but then it must become OLD. (That is what
404423
** '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.
407424
*/
408-
#define genlink(g,o) genlink_(g, obj2gco(o), &(o)->gclist)
409-
410-
static void genlink_ (global_State *g, GCObject *o, GCObject **pnext) {
425+
static void genlink (global_State *g, GCObject *o) {
411426
lua_assert(isblack(o));
412427
if (getage(o) == G_TOUCHED1) { /* touched in this cycle? */
413-
*pnext = g->grayagain; /* link it back in 'grayagain' */
414-
g->grayagain = o;
415-
set2gray(o);
428+
linkobjgclist(o, g->grayagain); /* link it back in 'grayagain' */
416429
} /* everything else do not need to be linked back */
417430
else if (getage(o) == G_TOUCHED2)
418431
changeage(o, G_TOUCHED2, G_OLD); /* advance age */
@@ -496,10 +509,8 @@ static int traverseephemeron (global_State *g, Table *h, int inv) {
496509
linkgclist(h, g->ephemeron); /* have to propagate again */
497510
else if (hasclears) /* table has white keys? */
498511
linkgclist(h, g->allweak); /* may have to clean white keys */
499-
else {
500-
nw2black(h); /* 'genlink' expects black objects */
501-
genlink(g, h); /* check whether collector still needs to see it */
502-
}
512+
else
513+
genlink(g, obj2gco(h)); /* check whether collector still needs to see it */
503514
return marked;
504515
}
505516

@@ -519,7 +530,7 @@ static void traversestrongtable (global_State *g, Table *h) {
519530
markvalue(g, gval(n));
520531
}
521532
}
522-
genlink(g, h);
533+
genlink(g, obj2gco(h));
523534
}
524535

525536

@@ -531,7 +542,6 @@ static lu_mem traversetable (global_State *g, Table *h) {
531542
(cast_void(weakkey = strchr(svalue(mode), 'k')),
532543
cast_void(weakvalue = strchr(svalue(mode), 'v')),
533544
(weakkey || weakvalue))) { /* is really weak? */
534-
set2gray(h); /* turn it back to gray, as it probably goes to a list */
535545
if (!weakkey) /* strong keys? */
536546
traverseweakvalue(g, h);
537547
else if (!weakvalue) /* strong values? */
@@ -550,7 +560,7 @@ static int traverseudata (global_State *g, Udata *u) {
550560
markobjectN(g, u->metatable); /* mark its metatable */
551561
for (i = 0; i < u->nuvalue; i++)
552562
markvalue(g, &u->uv[i].uv);
553-
genlink(g, u);
563+
genlink(g, obj2gco(u));
554564
return 1 + u->nuvalue;
555565
}
556566

@@ -612,10 +622,8 @@ static int traverseLclosure (global_State *g, LClosure *cl) {
612622
static int traversethread (global_State *g, lua_State *th) {
613623
UpVal *uv;
614624
StkId o = th->stack;
615-
if (isold(th) || g->gcstate == GCSpropagate) {
625+
if (isold(th) || g->gcstate == GCSpropagate)
616626
linkgclist(th, g->grayagain); /* insert into 'grayagain' list */
617-
set2gray(th);
618-
}
619627
if (o == NULL)
620628
return 1; /* stack not completely built yet */
621629
lua_assert(g->gcstate == GCSatomic ||
@@ -641,8 +649,7 @@ static int traversethread (global_State *g, lua_State *th) {
641649

642650

643651
/*
644-
** traverse one gray object, turning it to black (except for threads,
645-
** which are always gray).
652+
** traverse one gray object, turning it to black.
646653
*/
647654
static lu_mem propagatemark (global_State *g) {
648655
GCObject *o = g->gray;
@@ -684,8 +691,10 @@ static void convergeephemerons (global_State *g) {
684691
g->ephemeron = NULL; /* tables may return to this list when traversed */
685692
changed = 0;
686693
while ((w = next) != NULL) { /* for each ephemeron table */
687-
next = gco2t(w)->gclist; /* list is rebuilt during loop */
688-
if (traverseephemeron(g, gco2t(w), dir)) { /* marked some value? */
694+
Table *h = gco2t(w);
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? */
689698
propagateall(g); /* propagate changes */
690699
changed = 1; /* will have to revisit all ephemeron tables */
691700
}
@@ -1048,7 +1057,6 @@ static void sweep2old (lua_State *L, GCObject **p) {
10481057
if (curr->tt == LUA_VTHREAD) { /* threads must be watched */
10491058
lua_State *th = gco2th(curr);
10501059
linkgclist(th, g->grayagain); /* insert into 'grayagain' list */
1051-
set2gray(th);
10521060
}
10531061
else if (curr->tt == LUA_VUPVAL && upisopen(gco2upv(curr)))
10541062
set2gray(curr); /* open upvalues are always gray */
@@ -1183,10 +1191,8 @@ static void markold (global_State *g, GCObject *from, GCObject *to) {
11831191
if (getage(p) == G_OLD1) {
11841192
lua_assert(!iswhite(p));
11851193
changeage(p, G_OLD1, G_OLD); /* now they are old */
1186-
if (isblack(p)) {
1187-
set2gray(p); /* should be '2white', but gray works too */
1194+
if (isblack(p))
11881195
reallymarkobject(g, p);
1189-
}
11901196
}
11911197
}
11921198
}

0 commit comments

Comments
 (0)