diff --git a/lgc.c b/lgc.c index 3dc486d3..d9a23307 100644 --- a/lgc.c +++ b/lgc.c @@ -1,5 +1,5 @@ /* -** $Id: lgc.c,v 2.40 2006/09/11 14:07:24 roberto Exp roberto $ +** $Id: lgc.c,v 2.41 2007/10/29 16:51:20 roberto Exp roberto $ ** Garbage Collector ** See Copyright Notice in lua.h */ @@ -68,6 +68,24 @@ static void removeentry (Node *n) { } +/* +** The next function tells whether a key or value can be cleared from +** a weak table. Non-collectable objects are never removed from weak +** tables. Strings behave as `values', so are never removed too. for +** other objects: if really collected, cannot keep them; for userdata +** being finalized, keep them in keys, but not in values +*/ +static int iscleared (const TValue *o, int iskey) { + if (!iscollectable(o)) return 0; + if (ttisstring(o)) { + stringmark(rawtsvalue(o)); /* strings are `values', so are never weak */ + return 0; + } + return iswhite(gcvalue(o)) || + (ttisuserdata(o) && (!iskey && isfinalized(uvalue(o)))); +} + + static void reallymarkobject (global_State *g, GCObject *o) { lua_assert(iswhite(o) && !isdead(g, o)); white2gray(o); @@ -157,9 +175,7 @@ size_t luaC_separateudata (lua_State *L, int all) { static void traverseweakvalue (global_State *g, Table *h) { - int i; - linktable(h, &g->weakvalue); - i = sizenode(h); + int i = sizenode(h); while (i--) { Node *n = gnode(h, i); lua_assert(ttype(gkey(n)) != LUA_TDEADKEY || ttisnil(gval(n))); @@ -170,26 +186,41 @@ static void traverseweakvalue (global_State *g, Table *h) { markvalue(g, gkey(n)); } } + linktable(h, &g->weak); } -static void traverseephemeron (global_State *g, Table *h) { - int i; - linktable(h, &g->weakkey); - i = h->sizearray; - while (i--) /* mark array part (numeric keys are 'strong') */ - markvalue(g, &h->array[i]); +static int traverseephemeron (global_State *g, Table *h) { + int marked = 0; + int hasclears = 0; + int i = h->sizearray; + while (i--) { /* mark array part (numeric keys are 'strong') */ + if (iscollectable(&h->array[i]) && iswhite(gcvalue(&h->array[i]))) { + marked = 1; + reallymarkobject(g, gcvalue(&h->array[i])); + } + } i = sizenode(h); while (i--) { Node *n = gnode(h, i); lua_assert(ttype(gkey(n)) != LUA_TDEADKEY || ttisnil(gval(n))); - if (ttisnil(gval(n))) - removeentry(n); /* remove empty entries */ - else { - lua_assert(!ttisnil(gkey(n))); - markvalue(g, gval(n)); + if (ttisnil(gval(n))) /* entry is empty? */ + removeentry(n); /* remove it */ + else if (iscollectable(gval(n)) && iswhite(gcvalue(gval(n)))) { + /* value is not marked yet */ + if (iscleared(key2tval(n), 1)) /* key is not marked (yet)? */ + hasclears = 1; /* may have to propagate mark from key to value */ + else { /* mark value only if key is marked */ + marked = 1; /* some mark changed status */ + reallymarkobject(g, gcvalue(gval(n))); + } } } + if (hasclears) + linktable(h, &g->ephemeron); + else /* nothing to propagate */ + linktable(h, &g->weak); /* avoid convergence phase */ + return marked; } @@ -227,7 +258,7 @@ static void traversetable (global_State *g, Table *h) { traverseephemeron(g, h); else { lua_assert(weakkey && weakvalue); /* nothing to traverse now */ - linktable(h, &g->weakkeyvalue); + linktable(h, &g->allweak); } return; } /* else go through */ @@ -307,7 +338,7 @@ static void traversestack (global_State *g, lua_State *l) { for (; o <= lim; o++) setnilvalue(o); if (!g->emergencygc) /* cannot change stack in emergency... */ - checkstacksizes(l, lim); /* ...(interpreter does not expect that change) */ + checkstacksizes(l, lim); /* ...(mutator does not expect that change) */ } @@ -367,24 +398,25 @@ static size_t propagateall (global_State *g) { } -/* -** The next function tells whether a key or value can be cleared from -** a weak table. Non-collectable objects are never removed from weak -** tables. Strings behave as `values', so are never removed too. for -** other objects: if really collected, cannot keep them; for userdata -** being finalized, keep them in keys, but not in values -*/ -static int iscleared (const TValue *o, int iskey) { - if (!iscollectable(o)) return 0; - if (ttisstring(o)) { - stringmark(rawtsvalue(o)); /* strings are `values', so are never weak */ - return 0; - } - return iswhite(gcvalue(o)) || - (ttisuserdata(o) && (!iskey && isfinalized(uvalue(o)))); +static void convergeephemerons (global_State *g) { + int changed; + do { + GCObject *w; + GCObject *next = g->ephemeron; + g->ephemeron = NULL; + changed = 0; + while ((w = next) != NULL) { + next = gco2h(w)->gclist; + if (traverseephemeron(g, gco2h(w))) { + changed = 1; + propagateall(g); + } + } + } while (changed); } + /* ** clear collected entries from weaktables */ @@ -552,7 +584,7 @@ static void markroot (lua_State *L) { global_State *g = G(L); g->gray = NULL; g->grayagain = NULL; - g->weakvalue = g->weakkey = g->weakkeyvalue = NULL; + g->weak = g->ephemeron = g->allweak = NULL; markobject(g, g->mainthread); /* make global table be traversed before main stack */ markvalue(g, gt(g->mainthread)); @@ -579,28 +611,30 @@ static void atomic (lua_State *L) { remarkupvals(g); /* traverse objects cautch by write barrier and by 'remarkupvals' */ propagateall(g); - /* remark value-weak tables */ - g->gray = g->weakvalue; - g->weakvalue = NULL; + /* remark weak tables */ + g->gray = g->weak; + g->weak = NULL; lua_assert(!iswhite(obj2gco(g->mainthread))); markobject(g, L); /* mark running thread */ markmt(g); /* mark basic metatables (again) */ propagateall(g); - /* remark key-weak tables */ - g->gray = g->weakkey; - g->weakkey = NULL; + /* remark ephemeron tables */ + g->gray = g->ephemeron; + g->ephemeron = NULL; propagateall(g); /* remark gray again */ g->gray = g->grayagain; g->grayagain = NULL; propagateall(g); + convergeephemerons(g); udsize = luaC_separateudata(L, 0); /* separate userdata to be finalized */ marktmu(g); /* mark `preserved' userdata */ udsize += propagateall(g); /* remark, to propagate `preserveness' */ + convergeephemerons(g); /* remove collected objects from weak tables */ - cleartable(g->weakvalue); - cleartable(g->weakkey); - cleartable(g->weakkeyvalue); + cleartable(g->weak); + cleartable(g->ephemeron); + cleartable(g->allweak); /* flip current white */ g->currentwhite = cast_byte(otherwhite(g)); g->sweepstrgc = 0; @@ -699,7 +733,7 @@ void luaC_fullgc (lua_State *L, int isemergency) { /* reset other collector lists */ g->gray = NULL; g->grayagain = NULL; - g->weakvalue = g->weakkey = g->weakkeyvalue = NULL; + g->weak = g->ephemeron = g->allweak = NULL; g->gcstate = GCSsweepstring; } lua_assert(g->gcstate != GCSpause && g->gcstate != GCSpropagate); diff --git a/lstate.c b/lstate.c index 109f3f3f..4b4b50a3 100644 --- a/lstate.c +++ b/lstate.c @@ -1,5 +1,5 @@ /* -** $Id: lstate.c,v 2.40 2006/10/10 17:40:17 roberto Exp roberto $ +** $Id: lstate.c,v 2.41 2007/10/29 16:51:20 roberto Exp roberto $ ** Global State ** See Copyright Notice in lua.h */ @@ -182,9 +182,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { g->sweepgc = &g->rootgc; g->gray = NULL; g->grayagain = NULL; - g->weakvalue = NULL; - g->weakkey = NULL; - g->weakkeyvalue = NULL; + g->weak = g->ephemeron = g->allweak = NULL; g->tmudata = NULL; g->totalbytes = sizeof(LG); g->gcpause = LUAI_GCPAUSE; diff --git a/lstate.h b/lstate.h index 393f477a..cffaf0a2 100644 --- a/lstate.h +++ b/lstate.h @@ -1,5 +1,5 @@ /* -** $Id: lstate.h,v 2.28 2007/02/07 17:48:52 roberto Exp roberto $ +** $Id: lstate.h,v 2.29 2007/10/29 16:51:20 roberto Exp roberto $ ** Global State ** See Copyright Notice in lua.h */ @@ -78,9 +78,9 @@ typedef struct global_State { GCObject **sweepgc; /* position of sweep in `rootgc' */ GCObject *gray; /* list of gray objects */ GCObject *grayagain; /* list of objects to be traversed atomically */ - GCObject *weakvalue; /* list of value-weak tables */ - GCObject *weakkey; /* list of key-weak tables */ - GCObject *weakkeyvalue; /* list of all-weak tables */ + GCObject *weak; /* list of (something) weak tables */ + GCObject *ephemeron; /* list of ephemeron tables */ + GCObject *allweak; /* list of all-weak tables */ GCObject *tmudata; /* last element of list of userdata to be GC */ Mbuffer buff; /* temporary buffer for string concatentation */ lu_mem GCthreshold;