aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2020-07-27 10:26:20 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2020-07-27 10:26:20 -0300
commita6da1472c0c5e05ff249325f979531ad51533110 (patch)
tree984a5355d168a60e604f7bc27fc7a2b3628620f3
parent34affe7a63fc5d842580a9f23616d057e17dfe27 (diff)
downloadlua-a6da1472c0c5e05ff249325f979531ad51533110.tar.gz
lua-a6da1472c0c5e05ff249325f979531ad51533110.tar.bz2
lua-a6da1472c0c5e05ff249325f979531ad51533110.zip
Fixed bug: barriers cannot be active during sweep
Barriers cannot be active during sweep, even in generational mode. (Although gen. mode is not incremental, it can hit a barrier when deleting a thread and closing its upvalues.) The colors of objects are being changed during sweep and, therefore, cannot be trusted.
-rw-r--r--lgc.c48
-rw-r--r--testes/gengc.lua28
2 files changed, 59 insertions, 17 deletions
diff --git a/lgc.c b/lgc.c
index f7fd7a59..64d1334b 100644
--- a/lgc.c
+++ b/lgc.c
@@ -181,14 +181,17 @@ static int iscleared (global_State *g, const GCObject *o) {
181 181
182 182
183/* 183/*
184** barrier that moves collector forward, that is, mark the white object 184** Barrier that moves collector forward, that is, marks the white object
185** 'v' being pointed by the black object 'o'. (If in sweep phase, clear 185** 'v' being pointed by the black object 'o'. In the generational
186** the black object to white [sweep it] to avoid other barrier calls for 186** mode, 'v' must also become old, if 'o' is old; however, it cannot
187** this same object.) In the generational mode, 'v' must also become 187** be changed directly to OLD, because it may still point to non-old
188** old, if 'o' is old; however, it cannot be changed directly to OLD, 188** objects. So, it is marked as OLD0. In the next cycle it will become
189** because it may still point to non-old objects. So, it is marked as 189** OLD1, and in the next it will finally become OLD (regular old). By
190** OLD0. In the next cycle it will become OLD1, and in the next it 190** then, any object it points to will also be old. If called in the
191** will finally become OLD (regular old). 191** incremental sweep phase, it clears the black object to white (sweep
192** it) to avoid other barrier calls for this same object. (That cannot
193** be done is generational mode, as its sweep does not distinguish
194** whites from deads.)
192*/ 195*/
193void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) { 196void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) {
194 global_State *g = G(L); 197 global_State *g = G(L);
@@ -202,7 +205,8 @@ void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) {
202 } 205 }
203 else { /* sweep phase */ 206 else { /* sweep phase */
204 lua_assert(issweepphase(g)); 207 lua_assert(issweepphase(g));
205 makewhite(g, o); /* mark main obj. as white to avoid other barriers */ 208 if (g->gckind == KGC_INC) /* incremental mode? */
209 makewhite(g, o); /* mark 'o' as white to avoid other barriers */
206 } 210 }
207} 211}
208 212
@@ -324,10 +328,15 @@ static lu_mem markbeingfnz (global_State *g) {
324 328
325 329
326/* 330/*
327** Mark all values stored in marked open upvalues from non-marked threads. 331** For each non-marked thread, simulates a barrier between each open
328** (Values from marked threads were already marked when traversing the 332** upvalue and its value. (If the thread is collected, the value will be
329** thread.) Remove from the list threads that no longer have upvalues and 333** assigned to the upvalue, but then it can be too late for the barrier
330** not-marked threads. 334** to act. The "barrier" does not need to check colors: A non-marked
335** thread must be young; upvalues cannot be older than their threads; so
336** any visited upvalue must be young too.) Also removes the thread from
337** the list, as it was already visited. Removes also threads with no
338** upvalues, as they have nothing to be checked. (If the thread gets an
339** upvalue later, it will be linked in the list again.)
331*/ 340*/
332static int remarkupvals (global_State *g) { 341static int remarkupvals (global_State *g) {
333 lua_State *thread; 342 lua_State *thread;
@@ -340,9 +349,11 @@ static int remarkupvals (global_State *g) {
340 p = &thread->twups; /* keep marked thread with upvalues in the list */ 349 p = &thread->twups; /* keep marked thread with upvalues in the list */
341 else { /* thread is not marked or without upvalues */ 350 else { /* thread is not marked or without upvalues */
342 UpVal *uv; 351 UpVal *uv;
352 lua_assert(!isold(thread) || thread->openupval == NULL);
343 *p = thread->twups; /* remove thread from the list */ 353 *p = thread->twups; /* remove thread from the list */
344 thread->twups = thread; /* mark that it is out of list */ 354 thread->twups = thread; /* mark that it is out of list */
345 for (uv = thread->openupval; uv != NULL; uv = uv->u.open.next) { 355 for (uv = thread->openupval; uv != NULL; uv = uv->u.open.next) {
356 lua_assert(getage(uv) <= getage(thread));
346 work++; 357 work++;
347 if (!iswhite(uv)) /* upvalue already visited? */ 358 if (!iswhite(uv)) /* upvalue already visited? */
348 markvalue(g, uv->v); /* mark its value */ 359 markvalue(g, uv->v); /* mark its value */
@@ -995,6 +1006,9 @@ static void sweep2old (lua_State *L, GCObject **p) {
995** during the sweep. So, any white object must be dead.) For 1006** during the sweep. So, any white object must be dead.) For
996** non-dead objects, advance their ages and clear the color of 1007** non-dead objects, advance their ages and clear the color of
997** new objects. (Old objects keep their colors.) 1008** new objects. (Old objects keep their colors.)
1009** The ages of G_TOUCHED1 and G_TOUCHED2 objects will advance
1010** in 'correctgraylist'. (That function will also remove objects
1011** turned white here from any gray list.)
998*/ 1012*/
999static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p, 1013static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p,
1000 GCObject *limit) { 1014 GCObject *limit) {
@@ -1055,16 +1069,16 @@ static GCObject **correctgraylist (GCObject **p) {
1055 lua_assert(isgray(curr)); 1069 lua_assert(isgray(curr));
1056 gray2black(curr); /* make it black, for next barrier */ 1070 gray2black(curr); /* make it black, for next barrier */
1057 changeage(curr, G_TOUCHED1, G_TOUCHED2); 1071 changeage(curr, G_TOUCHED1, G_TOUCHED2);
1058 p = next; /* go to next element */ 1072 p = next; /* keep it in the list and go to next element */
1059 } 1073 }
1060 else { /* not touched in this cycle */ 1074 else { /* everything else is removed */
1075 /* white objects are simply removed */
1061 if (!iswhite(curr)) { /* not white? */ 1076 if (!iswhite(curr)) { /* not white? */
1062 lua_assert(isold(curr)); 1077 lua_assert(isold(curr));
1063 if (getage(curr) == G_TOUCHED2) /* advance from G_TOUCHED2... */ 1078 if (getage(curr) == G_TOUCHED2) /* advance from G_TOUCHED2... */
1064 changeage(curr, G_TOUCHED2, G_OLD); /* ... to G_OLD */ 1079 changeage(curr, G_TOUCHED2, G_OLD); /* ... to G_OLD */
1065 gray2black(curr); /* make it black */ 1080 gray2black(curr); /* make it black */
1066 } 1081 }
1067 /* else, object is white: just remove it from this list */
1068 *p = *next; /* remove 'curr' from gray list */ 1082 *p = *next; /* remove 'curr' from gray list */
1069 } 1083 }
1070 break; 1084 break;
@@ -1143,6 +1157,7 @@ static void youngcollection (lua_State *L, global_State *g) {
1143 atomic(L); 1157 atomic(L);
1144 1158
1145 /* sweep nursery and get a pointer to its last live element */ 1159 /* sweep nursery and get a pointer to its last live element */
1160 g->gcstate = GCSswpallgc;
1146 psurvival = sweepgen(L, g, &g->allgc, g->survival); 1161 psurvival = sweepgen(L, g, &g->allgc, g->survival);
1147 /* sweep 'survival' and 'old' */ 1162 /* sweep 'survival' and 'old' */
1148 sweepgen(L, g, psurvival, g->reallyold); 1163 sweepgen(L, g, psurvival, g->reallyold);
@@ -1166,6 +1181,7 @@ static void youngcollection (lua_State *L, global_State *g) {
1166 1181
1167static void atomic2gen (lua_State *L, global_State *g) { 1182static void atomic2gen (lua_State *L, global_State *g) {
1168 /* sweep all elements making them old */ 1183 /* sweep all elements making them old */
1184 g->gcstate = GCSswpallgc;
1169 sweep2old(L, &g->allgc); 1185 sweep2old(L, &g->allgc);
1170 /* everything alive now is old */ 1186 /* everything alive now is old */
1171 g->reallyold = g->old = g->survival = g->allgc; 1187 g->reallyold = g->old = g->survival = g->allgc;
diff --git a/testes/gengc.lua b/testes/gengc.lua
index 4e80dd7e..7a7dabdd 100644
--- a/testes/gengc.lua
+++ b/testes/gengc.lua
@@ -57,13 +57,39 @@ do -- bug in 5.4.0
57 local obj = {} -- create a new object 57 local obj = {} -- create a new object
58 collectgarbage("step", 0) -- make it a survival 58 collectgarbage("step", 0) -- make it a survival
59 assert(not T or T.gcage(obj) == "survival") 59 assert(not T or T.gcage(obj) == "survival")
60 setmetatable(obj, {__gc = gcf, x = "ok"}) -- create its metatable 60 setmetatable(obj, {__gc = gcf, x = "+"}) -- create its metatable
61 assert(not T or T.gcage(getmetatable(obj)) == "new") 61 assert(not T or T.gcage(getmetatable(obj)) == "new")
62 obj = nil -- clear object 62 obj = nil -- clear object
63 collectgarbage("step", 0) -- will call obj's finalizer 63 collectgarbage("step", 0) -- will call obj's finalizer
64end 64end
65 65
66 66
67do -- another bug in 5.4.0
68 local old = {10}
69 collectgarbage() -- make 'old' old
70 local co = coroutine.create(
71 function ()
72 local x = nil
73 local f = function ()
74 return x[1]
75 end
76 x = coroutine.yield(f)
77 coroutine.yield()
78 end
79 )
80 local _, f = coroutine.resume(co) -- create closure over 'x' in coroutine
81 collectgarbage("step", 0) -- make upvalue a survival
82 old[1] = {"hello"} -- 'old' go to grayagain as 'touched1'
83 coroutine.resume(co, {123}) -- its value will be new
84 co = nil
85 collectgarbage("step", 0) -- hit the barrier
86 assert(f() == 123 and old[1][1] == "hello")
87 collectgarbage("step", 0) -- run the collector once more
88 -- make sure old[1] was not collected
89 assert(f() == 123 and old[1][1] == "hello")
90end
91
92
67if T == nil then 93if T == nil then
68 (Message or print)('\n >>> testC not active: \z 94 (Message or print)('\n >>> testC not active: \z
69 skipping some generational tests <<<\n') 95 skipping some generational tests <<<\n')