diff options
| author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2020-07-27 10:26:20 -0300 |
|---|---|---|
| committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2020-07-27 10:26:20 -0300 |
| commit | a6da1472c0c5e05ff249325f979531ad51533110 (patch) | |
| tree | 984a5355d168a60e604f7bc27fc7a2b3628620f3 | |
| parent | 34affe7a63fc5d842580a9f23616d057e17dfe27 (diff) | |
| download | lua-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.c | 48 | ||||
| -rw-r--r-- | testes/gengc.lua | 28 |
2 files changed, 59 insertions, 17 deletions
| @@ -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 | */ |
| 193 | void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) { | 196 | void 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 | */ |
| 332 | static int remarkupvals (global_State *g) { | 341 | static 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 | */ |
| 999 | static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p, | 1013 | static 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 | ||
| 1167 | static void atomic2gen (lua_State *L, global_State *g) { | 1182 | static 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 |
| 64 | end | 64 | end |
| 65 | 65 | ||
| 66 | 66 | ||
| 67 | do -- 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") | ||
| 90 | end | ||
| 91 | |||
| 92 | |||
| 67 | if T == nil then | 93 | if 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') |
