summaryrefslogtreecommitdiff
path: root/src/lgc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lgc.c')
-rw-r--r--src/lgc.c142
1 files changed, 96 insertions, 46 deletions
diff --git a/src/lgc.c b/src/lgc.c
index 598cc71c..37de55ca 100644
--- a/src/lgc.c
+++ b/src/lgc.c
@@ -1,5 +1,5 @@
/*
-** $Id: lgc.c,v 2.109 2011/05/05 19:42:25 roberto Exp $
+** $Id: lgc.c,v 2.114 2011/10/03 17:54:25 roberto Exp $
** Garbage Collector
** See Copyright Notice in lua.h
*/
@@ -100,12 +100,13 @@ static void reallymarkobject (global_State *g, GCObject *o);
/*
-** mark a table entry as dead (therefore removing it from the table)
+** if key is not marked, mark its entry as dead (therefore removing it
+** from the table)
*/
static void removeentry (Node *n) {
lua_assert(ttisnil(gval(n)));
- if (iscollectable(gkey(n)))
- setdeadvalue(gkey(n)); /* dead key; remove it */
+ if (valiswhite(gkey(n)))
+ setdeadvalue(gkey(n)); /* unused and unmarked key; remove it */
}
@@ -116,13 +117,13 @@ static void removeentry (Node *n) {
** other objects: if really collected, cannot keep them; for objects
** being finalized, keep them in keys, but not in values
*/
-static int iscleared (const TValue *o, int iskey) {
+static int iscleared (const TValue *o) {
if (!iscollectable(o)) return 0;
else if (ttisstring(o)) {
stringmark(rawtsvalue(o)); /* strings are `values', so are never weak */
return 0;
}
- else return iswhite(gcvalue(o)) || (!iskey && isfinalized(gcvalue(o)));
+ else return iswhite(gcvalue(o));
}
@@ -152,7 +153,7 @@ void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) {
*/
void luaC_barrierback_ (lua_State *L, GCObject *o) {
global_State *g = G(L);
- lua_assert(isblack(o) && !isdead(g, o));
+ lua_assert(isblack(o) && !isdead(g, o) && gch(o)->tt == LUA_TTABLE);
black2gray(o); /* make object gray (again) */
gco2t(o)->gclist = g->grayagain;
g->grayagain = o;
@@ -342,6 +343,9 @@ static void markroot (global_State *g) {
static void traverseweakvalue (global_State *g, Table *h) {
Node *n, *limit = gnode(h, sizenode(h));
+ /* if there is array part, assume it may have white values (do not
+ traverse it just to check) */
+ int hasclears = (h->sizearray > 0);
for (n = gnode(h, 0); n < limit; n++) {
checkdeadkey(n);
if (ttisnil(gval(n))) /* entry is empty? */
@@ -349,15 +353,21 @@ static void traverseweakvalue (global_State *g, Table *h) {
else {
lua_assert(!ttisnil(gkey(n)));
markvalue(g, gkey(n)); /* mark key */
+ if (!hasclears && iscleared(gval(n))) /* is there a white value? */
+ hasclears = 1; /* table will have to be cleared */
}
}
- linktable(h, &g->weak); /* link into appropriate list */
+ if (hasclears)
+ linktable(h, &g->weak); /* has to be cleared later */
+ else /* no white values */
+ linktable(h, &g->grayagain); /* no need to clean */
}
static int traverseephemeron (global_State *g, Table *h) {
int marked = 0; /* true if an object is marked in this traversal */
- int hasclears = 0; /* true if table has unmarked pairs */
+ int hasclears = 0; /* true if table has white keys */
+ int prop = 0; /* true if table has entry "white-key -> white-value" */
Node *n, *limit = gnode(h, sizenode(h));
int i;
/* traverse array part (numeric keys are 'strong') */
@@ -372,19 +382,22 @@ static int traverseephemeron (global_State *g, Table *h) {
checkdeadkey(n);
if (ttisnil(gval(n))) /* entry is empty? */
removeentry(n); /* remove it */
+ else if (iscleared(gkey(n))) { /* key is not marked (yet)? */
+ hasclears = 1; /* table must be cleared */
+ if (valiswhite(gval(n))) /* value not marked yet? */
+ prop = 1; /* must propagate again */
+ }
else if (valiswhite(gval(n))) { /* value not marked yet? */
- if (iscleared(gkey(n), 1)) /* key is not marked (yet)? */
- hasclears = 1; /* may have to propagate mark from key to value */
- else { /* key is marked, so mark value */
- marked = 1; /* value was not marked */
- reallymarkobject(g, gcvalue(gval(n)));
- }
+ marked = 1;
+ reallymarkobject(g, gcvalue(gval(n))); /* mark it now */
}
}
- if (hasclears) /* does table have unmarked pairs? */
- linktable(h, &g->ephemeron); /* will have to propagate again */
- else /* nothing to propagate */
- linktable(h, &g->weak); /* avoid convergence phase */
+ if (prop)
+ linktable(h, &g->ephemeron); /* have to propagate again */
+ else if (hasclears) /* does table have white keys? */
+ linktable(h, &g->allweak); /* may have to clean white keys */
+ else /* no white keys */
+ linktable(h, &g->grayagain); /* no need to clean */
return marked;
}
@@ -526,11 +539,26 @@ static void propagateall (global_State *g) {
}
-static void traverselistofgrays (global_State *g, GCObject **l) {
+static void propagatelist (global_State *g, GCObject *l) {
lua_assert(g->gray == NULL); /* no grays left */
- g->gray = *l; /* now 'l' is new gray list */
- *l = NULL;
- propagateall(g);
+ g->gray = l;
+ propagateall(g); /* traverse all elements from 'l' */
+}
+
+/*
+** retraverse all gray lists. Because tables may be reinserted in other
+** lists when traversed, traverse the original lists to avoid traversing
+** twice the same table (which is not wrong, but inefficient)
+*/
+static void retraversegrays (global_State *g) {
+ GCObject *weak = g->weak; /* save original lists */
+ GCObject *grayagain = g->grayagain;
+ GCObject *ephemeron = g->ephemeron;
+ g->weak = g->grayagain = g->ephemeron = NULL;
+ propagateall(g); /* traverse main gray list */
+ propagatelist(g, grayagain);
+ propagatelist(g, weak);
+ propagatelist(g, ephemeron);
}
@@ -562,21 +590,39 @@ static void convergeephemerons (global_State *g) {
/*
-** clear collected entries from all weaktables in list 'l'
+** clear entries with unmarked keys from all weaktables in list 'l' up
+** to element 'f'
+*/
+static void clearkeys (GCObject *l, GCObject *f) {
+ for (; l != f; l = gco2t(l)->gclist) {
+ Table *h = gco2t(l);
+ Node *n, *limit = gnode(h, sizenode(h));
+ for (n = gnode(h, 0); n < limit; n++) {
+ if (!ttisnil(gval(n)) && (iscleared(gkey(n)))) {
+ setnilvalue(gval(n)); /* remove value ... */
+ removeentry(n); /* and remove entry from table */
+ }
+ }
+ }
+}
+
+
+/*
+** clear entries with unmarked values from all weaktables in list 'l' up
+** to element 'f'
*/
-static void cleartable (GCObject *l) {
- for (; l != NULL; l = gco2t(l)->gclist) {
+static void clearvalues (GCObject *l, GCObject *f) {
+ for (; l != f; l = gco2t(l)->gclist) {
Table *h = gco2t(l);
Node *n, *limit = gnode(h, sizenode(h));
int i;
for (i = 0; i < h->sizearray; i++) {
TValue *o = &h->array[i];
- if (iscleared(o, 0)) /* value was collected? */
+ if (iscleared(o)) /* value was collected? */
setnilvalue(o); /* remove value */
}
for (n = gnode(h, 0); n < limit; n++) {
- if (!ttisnil(gval(n)) && /* non-empty entry? */
- (iscleared(gkey(n), 1) || iscleared(gval(n), 0))) {
+ if (!ttisnil(gval(n)) && iscleared(gval(n))) {
setnilvalue(gval(n)); /* remove value ... */
removeentry(n); /* and remove entry from table */
}
@@ -743,10 +789,10 @@ static void GCTM (lua_State *L, int propagateerrors) {
/*
-** move all unreachable objects that need finalization from list 'finobj'
-** to list 'tobefnz'
+** move all unreachable objects (or 'all' objects) that need
+** finalization from list 'finobj' to list 'tobefnz' (to be finalized)
*/
-void luaC_separateudata (lua_State *L, int all) {
+static void separatetobefnz (lua_State *L, int all) {
global_State *g = G(L);
GCObject **p = &g->finobj;
GCObject *curr;
@@ -842,14 +888,13 @@ static void callallpendingfinalizers (lua_State *L, int propagateerrors) {
void luaC_freeallobjects (lua_State *L) {
global_State *g = G(L);
int i;
+ separatetobefnz(L, 1); /* separate all objects with finalizers */
+ lua_assert(g->finobj == NULL);
callallpendingfinalizers(L, 0);
- /* following "white" makes all objects look dead */
- g->currentwhite = WHITEBITS;
+ g->currentwhite = WHITEBITS; /* this "white" makes all objects look dead */
g->gckind = KGC_NORMAL;
- sweepwholelist(L, &g->finobj);
- lua_assert(g->finobj == NULL);
+ sweepwholelist(L, &g->finobj); /* finalizers can create objs. in 'finobj' */
sweepwholelist(L, &g->allgc);
- lua_assert(g->allgc == NULL);
for (i = 0; i < g->strt.size; i++) /* free all string lists */
sweepwholelist(L, &g->strt.hash[i]);
lua_assert(g->strt.nuse == 0);
@@ -858,6 +903,7 @@ void luaC_freeallobjects (lua_State *L) {
static void atomic (lua_State *L) {
global_State *g = G(L);
+ GCObject *origweak, *origall;
lua_assert(!iswhite(obj2gco(g->mainthread)));
markobject(g, L); /* mark running thread */
/* registry and global metatables may be changed by API */
@@ -866,20 +912,24 @@ static void atomic (lua_State *L) {
/* remark occasional upvalues of (maybe) dead threads */
remarkupvals(g);
/* traverse objects caught by write barrier and by 'remarkupvals' */
- propagateall(g);
- traverselistofgrays(g, &g->weak); /* remark weak tables */
- traverselistofgrays(g, &g->ephemeron); /* remark ephemeron tables */
- traverselistofgrays(g, &g->grayagain); /* remark gray again */
+ retraversegrays(g);
convergeephemerons(g);
/* at this point, all strongly accessible objects are marked. */
- luaC_separateudata(L, 0); /* separate userdata to be finalized */
+ /* clear values from weak tables, before checking finalizers */
+ clearvalues(g->weak, NULL);
+ clearvalues(g->allweak, NULL);
+ origweak = g->weak; origall = g->allweak;
+ separatetobefnz(L, 0); /* separate objects to be finalized */
markbeingfnz(g); /* mark userdata that will be finalized */
propagateall(g); /* remark, to propagate `preserveness' */
convergeephemerons(g);
- /* remove collected objects from weak tables */
- cleartable(g->weak);
- cleartable(g->ephemeron);
- cleartable(g->allweak);
+ /* at this point, all resurrected objects are marked. */
+ /* remove dead objects from weak tables */
+ clearkeys(g->ephemeron, NULL); /* clear keys from all ephemeron tables */
+ clearkeys(g->allweak, NULL); /* clear keys from all allweak tables */
+ /* clear values from resurrected weak tables */
+ clearvalues(g->weak, origweak);
+ clearvalues(g->allweak, origall);
g->sweepstrgc = 0; /* prepare to sweep strings */
g->gcstate = GCSsweepstring;
g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */