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

120
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
** 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);

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
** 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;

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
** 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;