first implementation of ephemerons

This commit is contained in:
Roberto Ierusalimschy 2007-10-31 13:41:19 -02:00
parent 0e961ad47a
commit 5e8dd55574
3 changed files with 83 additions and 51 deletions

118
lgc.c
View File

@ -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 ** Garbage Collector
** See Copyright Notice in lua.h ** 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) { static void reallymarkobject (global_State *g, GCObject *o) {
lua_assert(iswhite(o) && !isdead(g, o)); lua_assert(iswhite(o) && !isdead(g, o));
white2gray(o); white2gray(o);
@ -157,9 +175,7 @@ size_t luaC_separateudata (lua_State *L, int all) {
static void traverseweakvalue (global_State *g, Table *h) { static void traverseweakvalue (global_State *g, Table *h) {
int i; int i = sizenode(h);
linktable(h, &g->weakvalue);
i = sizenode(h);
while (i--) { while (i--) {
Node *n = gnode(h, i); Node *n = gnode(h, i);
lua_assert(ttype(gkey(n)) != LUA_TDEADKEY || ttisnil(gval(n))); lua_assert(ttype(gkey(n)) != LUA_TDEADKEY || ttisnil(gval(n)));
@ -170,27 +186,42 @@ static void traverseweakvalue (global_State *g, Table *h) {
markvalue(g, gkey(n)); markvalue(g, gkey(n));
} }
} }
linktable(h, &g->weak);
} }
static void traverseephemeron (global_State *g, Table *h) { static int traverseephemeron (global_State *g, Table *h) {
int i; int marked = 0;
linktable(h, &g->weakkey); int hasclears = 0;
i = h->sizearray; int i = h->sizearray;
while (i--) /* mark array part (numeric keys are 'strong') */ while (i--) { /* mark array part (numeric keys are 'strong') */
markvalue(g, &h->array[i]); if (iscollectable(&h->array[i]) && iswhite(gcvalue(&h->array[i]))) {
marked = 1;
reallymarkobject(g, gcvalue(&h->array[i]));
}
}
i = sizenode(h); i = sizenode(h);
while (i--) { while (i--) {
Node *n = gnode(h, i); Node *n = gnode(h, i);
lua_assert(ttype(gkey(n)) != LUA_TDEADKEY || ttisnil(gval(n))); lua_assert(ttype(gkey(n)) != LUA_TDEADKEY || ttisnil(gval(n)));
if (ttisnil(gval(n))) if (ttisnil(gval(n))) /* entry is empty? */
removeentry(n); /* remove empty entries */ removeentry(n); /* remove it */
else { else if (iscollectable(gval(n)) && iswhite(gcvalue(gval(n)))) {
lua_assert(!ttisnil(gkey(n))); /* value is not marked yet */
markvalue(g, gval(n)); 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;
}
static void traversestrongtable (global_State *g, Table *h) { static void traversestrongtable (global_State *g, Table *h) {
@ -227,7 +258,7 @@ static void traversetable (global_State *g, Table *h) {
traverseephemeron(g, h); traverseephemeron(g, h);
else { else {
lua_assert(weakkey && weakvalue); /* nothing to traverse now */ lua_assert(weakkey && weakvalue); /* nothing to traverse now */
linktable(h, &g->weakkeyvalue); linktable(h, &g->allweak);
} }
return; return;
} /* else go through */ } /* else go through */
@ -307,7 +338,7 @@ static void traversestack (global_State *g, lua_State *l) {
for (; o <= lim; o++) for (; o <= lim; o++)
setnilvalue(o); setnilvalue(o);
if (!g->emergencygc) /* cannot change stack in emergency... */ 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,22 +398,23 @@ static size_t propagateall (global_State *g) {
} }
/* static void convergeephemerons (global_State *g) {
** The next function tells whether a key or value can be cleared from int changed;
** a weak table. Non-collectable objects are never removed from weak do {
** tables. Strings behave as `values', so are never removed too. for GCObject *w;
** other objects: if really collected, cannot keep them; for userdata GCObject *next = g->ephemeron;
** being finalized, keep them in keys, but not in values g->ephemeron = NULL;
*/ changed = 0;
static int iscleared (const TValue *o, int iskey) { while ((w = next) != NULL) {
if (!iscollectable(o)) return 0; next = gco2h(w)->gclist;
if (ttisstring(o)) { if (traverseephemeron(g, gco2h(w))) {
stringmark(rawtsvalue(o)); /* strings are `values', so are never weak */ changed = 1;
return 0; propagateall(g);
} }
return iswhite(gcvalue(o)) ||
(ttisuserdata(o) && (!iskey && isfinalized(uvalue(o))));
} }
} while (changed);
}
/* /*
@ -552,7 +584,7 @@ static void markroot (lua_State *L) {
global_State *g = G(L); global_State *g = G(L);
g->gray = NULL; g->gray = NULL;
g->grayagain = NULL; g->grayagain = NULL;
g->weakvalue = g->weakkey = g->weakkeyvalue = NULL; g->weak = g->ephemeron = g->allweak = NULL;
markobject(g, g->mainthread); markobject(g, g->mainthread);
/* make global table be traversed before main stack */ /* make global table be traversed before main stack */
markvalue(g, gt(g->mainthread)); markvalue(g, gt(g->mainthread));
@ -579,28 +611,30 @@ static void atomic (lua_State *L) {
remarkupvals(g); remarkupvals(g);
/* traverse objects cautch by write barrier and by 'remarkupvals' */ /* traverse objects cautch by write barrier and by 'remarkupvals' */
propagateall(g); propagateall(g);
/* remark value-weak tables */ /* remark weak tables */
g->gray = g->weakvalue; g->gray = g->weak;
g->weakvalue = NULL; g->weak = NULL;
lua_assert(!iswhite(obj2gco(g->mainthread))); lua_assert(!iswhite(obj2gco(g->mainthread)));
markobject(g, L); /* mark running thread */ markobject(g, L); /* mark running thread */
markmt(g); /* mark basic metatables (again) */ markmt(g); /* mark basic metatables (again) */
propagateall(g); propagateall(g);
/* remark key-weak tables */ /* remark ephemeron tables */
g->gray = g->weakkey; g->gray = g->ephemeron;
g->weakkey = NULL; g->ephemeron = NULL;
propagateall(g); propagateall(g);
/* remark gray again */ /* remark gray again */
g->gray = g->grayagain; g->gray = g->grayagain;
g->grayagain = NULL; g->grayagain = NULL;
propagateall(g); propagateall(g);
convergeephemerons(g);
udsize = luaC_separateudata(L, 0); /* separate userdata to be finalized */ udsize = luaC_separateudata(L, 0); /* separate userdata to be finalized */
marktmu(g); /* mark `preserved' userdata */ marktmu(g); /* mark `preserved' userdata */
udsize += propagateall(g); /* remark, to propagate `preserveness' */ udsize += propagateall(g); /* remark, to propagate `preserveness' */
convergeephemerons(g);
/* remove collected objects from weak tables */ /* remove collected objects from weak tables */
cleartable(g->weakvalue); cleartable(g->weak);
cleartable(g->weakkey); cleartable(g->ephemeron);
cleartable(g->weakkeyvalue); cleartable(g->allweak);
/* flip current white */ /* flip current white */
g->currentwhite = cast_byte(otherwhite(g)); g->currentwhite = cast_byte(otherwhite(g));
g->sweepstrgc = 0; g->sweepstrgc = 0;
@ -699,7 +733,7 @@ void luaC_fullgc (lua_State *L, int isemergency) {
/* reset other collector lists */ /* reset other collector lists */
g->gray = NULL; g->gray = NULL;
g->grayagain = NULL; g->grayagain = NULL;
g->weakvalue = g->weakkey = g->weakkeyvalue = NULL; g->weak = g->ephemeron = g->allweak = NULL;
g->gcstate = GCSsweepstring; g->gcstate = GCSsweepstring;
} }
lua_assert(g->gcstate != GCSpause && g->gcstate != GCSpropagate); lua_assert(g->gcstate != GCSpause && g->gcstate != GCSpropagate);

View File

@ -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 ** Global State
** See Copyright Notice in lua.h ** 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->sweepgc = &g->rootgc;
g->gray = NULL; g->gray = NULL;
g->grayagain = NULL; g->grayagain = NULL;
g->weakvalue = NULL; g->weak = g->ephemeron = g->allweak = NULL;
g->weakkey = NULL;
g->weakkeyvalue = NULL;
g->tmudata = NULL; g->tmudata = NULL;
g->totalbytes = sizeof(LG); g->totalbytes = sizeof(LG);
g->gcpause = LUAI_GCPAUSE; g->gcpause = LUAI_GCPAUSE;

View File

@ -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 ** Global State
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -78,9 +78,9 @@ typedef struct global_State {
GCObject **sweepgc; /* position of sweep in `rootgc' */ GCObject **sweepgc; /* position of sweep in `rootgc' */
GCObject *gray; /* list of gray objects */ GCObject *gray; /* list of gray objects */
GCObject *grayagain; /* list of objects to be traversed atomically */ GCObject *grayagain; /* list of objects to be traversed atomically */
GCObject *weakvalue; /* list of value-weak tables */ GCObject *weak; /* list of (something) weak tables */
GCObject *weakkey; /* list of key-weak tables */ GCObject *ephemeron; /* list of ephemeron tables */
GCObject *weakkeyvalue; /* list of all-weak tables */ GCObject *allweak; /* list of all-weak tables */
GCObject *tmudata; /* last element of list of userdata to be GC */ GCObject *tmudata; /* last element of list of userdata to be GC */
Mbuffer buff; /* temporary buffer for string concatentation */ Mbuffer buff; /* temporary buffer for string concatentation */
lu_mem GCthreshold; lu_mem GCthreshold;