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) { /* -** barrier that moves collector forward, that is, mark the white object -** 'v' being pointed by the black object 'o'. (If in sweep phase, clear -** the black object to white [sweep it] to avoid other barrier calls for -** this same object.) In the generational mode, 'v' must also become -** old, if 'o' is old; however, it cannot be changed directly to OLD, -** because it may still point to non-old objects. So, it is marked as -** OLD0. In the next cycle it will become OLD1, and in the next it -** will finally become OLD (regular old). +** Barrier that moves collector forward, that is, marks the white object +** 'v' being pointed by the black object 'o'. In the generational +** mode, 'v' must also become old, if 'o' is old; however, it cannot +** be changed directly to OLD, because it may still point to non-old +** objects. So, it is marked as OLD0. In the next cycle it will become +** OLD1, and in the next it will finally become OLD (regular old). By +** then, any object it points to will also be old. If called in the +** incremental sweep phase, it clears the black object to white (sweep +** it) to avoid other barrier calls for this same object. (That cannot +** be done is generational mode, as its sweep does not distinguish +** whites from deads.) */ void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) { global_State *g = G(L); @@ -202,7 +205,8 @@ void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) { } else { /* sweep phase */ lua_assert(issweepphase(g)); - makewhite(g, o); /* mark main obj. as white to avoid other barriers */ + if (g->gckind == KGC_INC) /* incremental mode? */ + makewhite(g, o); /* mark 'o' as white to avoid other barriers */ } } @@ -324,10 +328,15 @@ static lu_mem markbeingfnz (global_State *g) { /* -** Mark all values stored in marked open upvalues from non-marked threads. -** (Values from marked threads were already marked when traversing the -** thread.) Remove from the list threads that no longer have upvalues and -** not-marked threads. +** For each non-marked thread, simulates a barrier between each open +** upvalue and its value. (If the thread is collected, the value will be +** assigned to the upvalue, but then it can be too late for the barrier +** to act. The "barrier" does not need to check colors: A non-marked +** thread must be young; upvalues cannot be older than their threads; so +** any visited upvalue must be young too.) Also removes the thread from +** the list, as it was already visited. Removes also threads with no +** upvalues, as they have nothing to be checked. (If the thread gets an +** upvalue later, it will be linked in the list again.) */ static int remarkupvals (global_State *g) { lua_State *thread; @@ -340,9 +349,11 @@ static int remarkupvals (global_State *g) { p = &thread->twups; /* keep marked thread with upvalues in the list */ else { /* thread is not marked or without upvalues */ UpVal *uv; + lua_assert(!isold(thread) || thread->openupval == NULL); *p = thread->twups; /* remove thread from the list */ thread->twups = thread; /* mark that it is out of list */ for (uv = thread->openupval; uv != NULL; uv = uv->u.open.next) { + lua_assert(getage(uv) <= getage(thread)); work++; if (!iswhite(uv)) /* upvalue already visited? */ markvalue(g, uv->v); /* mark its value */ @@ -995,6 +1006,9 @@ static void sweep2old (lua_State *L, GCObject **p) { ** during the sweep. So, any white object must be dead.) For ** non-dead objects, advance their ages and clear the color of ** new objects. (Old objects keep their colors.) +** The ages of G_TOUCHED1 and G_TOUCHED2 objects will advance +** in 'correctgraylist'. (That function will also remove objects +** turned white here from any gray list.) */ static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p, GCObject *limit) { @@ -1055,16 +1069,16 @@ static GCObject **correctgraylist (GCObject **p) { lua_assert(isgray(curr)); gray2black(curr); /* make it black, for next barrier */ changeage(curr, G_TOUCHED1, G_TOUCHED2); - p = next; /* go to next element */ + p = next; /* keep it in the list and go to next element */ } - else { /* not touched in this cycle */ + else { /* everything else is removed */ + /* white objects are simply removed */ if (!iswhite(curr)) { /* not white? */ lua_assert(isold(curr)); if (getage(curr) == G_TOUCHED2) /* advance from G_TOUCHED2... */ changeage(curr, G_TOUCHED2, G_OLD); /* ... to G_OLD */ gray2black(curr); /* make it black */ } - /* else, object is white: just remove it from this list */ *p = *next; /* remove 'curr' from gray list */ } break; @@ -1143,6 +1157,7 @@ static void youngcollection (lua_State *L, global_State *g) { atomic(L); /* sweep nursery and get a pointer to its last live element */ + g->gcstate = GCSswpallgc; psurvival = sweepgen(L, g, &g->allgc, g->survival); /* sweep 'survival' and 'old' */ sweepgen(L, g, psurvival, g->reallyold); @@ -1166,6 +1181,7 @@ static void youngcollection (lua_State *L, global_State *g) { static void atomic2gen (lua_State *L, global_State *g) { /* sweep all elements making them old */ + g->gcstate = GCSswpallgc; sweep2old(L, &g->allgc); /* everything alive now is old */ 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 local obj = {} -- create a new object collectgarbage("step", 0) -- make it a survival assert(not T or T.gcage(obj) == "survival") - setmetatable(obj, {__gc = gcf, x = "ok"}) -- create its metatable + setmetatable(obj, {__gc = gcf, x = "+"}) -- create its metatable assert(not T or T.gcage(getmetatable(obj)) == "new") obj = nil -- clear object collectgarbage("step", 0) -- will call obj's finalizer end +do -- another bug in 5.4.0 + local old = {10} + collectgarbage() -- make 'old' old + local co = coroutine.create( + function () + local x = nil + local f = function () + return x[1] + end + x = coroutine.yield(f) + coroutine.yield() + end + ) + local _, f = coroutine.resume(co) -- create closure over 'x' in coroutine + collectgarbage("step", 0) -- make upvalue a survival + old[1] = {"hello"} -- 'old' go to grayagain as 'touched1' + coroutine.resume(co, {123}) -- its value will be new + co = nil + collectgarbage("step", 0) -- hit the barrier + assert(f() == 123 and old[1][1] == "hello") + collectgarbage("step", 0) -- run the collector once more + -- make sure old[1] was not collected + assert(f() == 123 and old[1][1] == "hello") +end + + if T == nil then (Message or print)('\n >>> testC not active: \z skipping some generational tests <<<\n')