1
0
mirror of https://github.com/lua/lua.git synced 2025-01-28 06:03:00 +08:00

generational collector (still not complete)

This commit is contained in:
Roberto Ierusalimschy 2017-04-05 13:50:51 -03:00
parent 1a1b2f3d7f
commit e4287da3a6
4 changed files with 326 additions and 104 deletions

361
lgc.c
View File

@ -1,5 +1,5 @@
/* /*
** $Id: lgc.c,v 2.215 2016/12/22 13:08:50 roberto Exp roberto $ ** $Id: lgc.c,v 2.216 2017/02/23 21:07:34 roberto Exp roberto $
** Garbage Collector ** Garbage Collector
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -9,7 +9,7 @@
#include "lprefix.h" #include "lprefix.h"
#include <stdio.h>
#include <string.h> #include <string.h>
#include "lua.h" #include "lua.h"
@ -26,12 +26,6 @@
#include "ltm.h" #include "ltm.h"
/*
** internal state for collector while inside the atomic phase. The
** collector should never be in this state while running regular code.
*/
#define GCSinsideatomic (GCSpause + 1)
/* /*
** cost of sweeping one element (the size of a small object divided ** cost of sweeping one element (the size of a small object divided
** by some adjust for the sweep speed) ** by some adjust for the sweep speed)
@ -59,8 +53,9 @@
#define PAUSEADJ 100 #define PAUSEADJ 100
/* mask to erase all color bits */ /* mask to erase all color bits (plus gen. related stuff) */
#define maskcolors (~(bitmask(BLACKBIT) | WHITEBITS)) #define maskcolors (~(bitmask(BLACKBIT) | WHITEBITS | AGEBITS))
/* macro to erase all color bits then sets only the current white bit */ /* macro to erase all color bits then sets only the current white bit */
#define makewhite(g,x) \ #define makewhite(g,x) \
@ -157,8 +152,10 @@ void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) {
lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o)); lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o));
if (keepinvariant(g)) { /* must keep invariant? */ if (keepinvariant(g)) { /* must keep invariant? */
reallymarkobject(g, v); /* restore invariant */ reallymarkobject(g, v); /* restore invariant */
if (isold(o)) if (isold(o)) {
l_setbit((v)->marked, OLDBIT); lua_assert(!isold(v));
setage(v, G_OLD0);
}
} }
else { /* sweep phase */ else { /* sweep phase */
lua_assert(issweepphase(g)); lua_assert(issweepphase(g));
@ -174,8 +171,12 @@ void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) {
void luaC_barrierback_ (lua_State *L, Table *t) { void luaC_barrierback_ (lua_State *L, Table *t) {
global_State *g = G(L); global_State *g = G(L);
lua_assert(isblack(t) && !isdead(g, t)); lua_assert(isblack(t) && !isdead(g, t));
lua_assert(issweepphase(g) || getage(t) != G_TOUCHED1);
lua_assert(g->gckind != KGC_GEN || isold(t));
if (getage(t) != G_TOUCHED2) /* not already in gray list? */
linkgclist(t, g->grayagain); /* link it in 'grayagain' */
black2gray(t); /* make table gray (again) */ black2gray(t); /* make table gray (again) */
linkgclist(t, g->grayagain); setage(t, G_TOUCHED1); /* touched in current cycle */
} }
@ -188,10 +189,10 @@ void luaC_barrierback_ (lua_State *L, Table *t) {
void luaC_upvalbarrier_ (lua_State *L, UpVal *uv) { void luaC_upvalbarrier_ (lua_State *L, UpVal *uv) {
global_State *g = G(L); global_State *g = G(L);
GCObject *o = gcvalue(uv->v); GCObject *o = gcvalue(uv->v);
lua_assert(!upisopen(uv)); /* ensured by macro luaC_upvalbarrier */
if (keepinvariant(g)) { if (keepinvariant(g)) {
markobject(g, o); markobject(g, o);
l_setbit((o)->marked, OLDBIT); if (!isold(o))
setage(o, G_OLD0);
} }
} }
@ -379,10 +380,10 @@ static void traverseweakvalue (global_State *g, Table *h) {
hasclears = 1; /* table will have to be cleared */ hasclears = 1; /* table will have to be cleared */
} }
} }
if (g->gcstate == GCSpropagate) if (g->gcstate == GCSatomic && hasclears)
linkgclist(h, g->grayagain); /* must retraverse it in atomic phase */
else if (hasclears)
linkgclist(h, g->weak); /* has to be cleared later */ linkgclist(h, g->weak); /* has to be cleared later */
else
linkgclist(h, g->grayagain); /* must retraverse it in atomic phase */
} }
@ -431,6 +432,8 @@ static int traverseephemeron (global_State *g, Table *h) {
linkgclist(h, g->ephemeron); /* have to propagate again */ linkgclist(h, g->ephemeron); /* have to propagate again */
else if (hasclears) /* table has white keys? */ else if (hasclears) /* table has white keys? */
linkgclist(h, g->allweak); /* may have to clean white keys */ linkgclist(h, g->allweak); /* may have to clean white keys */
else if (g->gckind == KGC_GEN)
linkgclist(h, g->grayagain); /* keep it in some list */
return marked; return marked;
} }
@ -450,6 +453,10 @@ static void traversestrongtable (global_State *g, Table *h) {
markvalue(g, gval(n)); /* mark value */ markvalue(g, gval(n)); /* mark value */
} }
} }
if (g->gckind == KGC_GEN) {
linkgclist(h, g->grayagain); /* keep it in some gray list */
black2gray(h);
}
} }
@ -522,7 +529,7 @@ static lu_mem traverseLclosure (global_State *g, LClosure *cl) {
for (i = 0; i < cl->nupvalues; i++) { /* mark its upvalues */ for (i = 0; i < cl->nupvalues; i++) { /* mark its upvalues */
UpVal *uv = cl->upvals[i]; UpVal *uv = cl->upvals[i];
if (uv != NULL) { if (uv != NULL) {
if (upisopen(uv) && g->gcstate != GCSinsideatomic) if (upisopen(uv) && g->gcstate != GCSatomic)
uv->u.open.touched = 1; /* can be marked in 'remarkupvals' */ uv->u.open.touched = 1; /* can be marked in 'remarkupvals' */
else else
markvalue(g, uv->v); markvalue(g, uv->v);
@ -536,11 +543,11 @@ static lu_mem traversethread (global_State *g, lua_State *th) {
StkId o = th->stack; StkId o = th->stack;
if (o == NULL) if (o == NULL)
return 1; /* stack not completely built yet */ return 1; /* stack not completely built yet */
lua_assert(g->gcstate == GCSinsideatomic || lua_assert(g->gcstate == GCSatomic ||
th->openupval == NULL || isintwups(th)); th->openupval == NULL || isintwups(th));
for (; o < th->top; o++) /* mark live elements in the stack */ for (; o < th->top; o++) /* mark live elements in the stack */
markvalue(g, o); markvalue(g, o);
if (g->gcstate == GCSinsideatomic) { /* final traversal? */ if (g->gcstate == GCSatomic) { /* final traversal? */
StkId lim = th->stack + th->stacksize; /* real end of stack */ StkId lim = th->stack + th->stacksize; /* real end of stack */
for (; o < lim; o++) /* clear not-marked stack slice */ for (; o < lim; o++) /* clear not-marked stack slice */
setnilvalue(o); setnilvalue(o);
@ -564,7 +571,7 @@ static lu_mem traversethread (global_State *g, lua_State *th) {
static void propagatemark (global_State *g) { static void propagatemark (global_State *g) {
lu_mem size; lu_mem size;
GCObject *o = g->gray; GCObject *o = g->gray;
lua_assert(isgray(o)); lua_assert(ongraylist(o));
gray2black(o); gray2black(o);
switch (o->tt) { switch (o->tt) {
case LUA_TTABLE: { case LUA_TTABLE: {
@ -638,11 +645,10 @@ static void convergeephemerons (global_State *g) {
/* /*
** clear entries with unmarked keys from all weaktables in list 'l' up ** clear entries with unmarked keys from all weaktables in list 'l'
** to element 'f'
*/ */
static void clearkeys (global_State *g, GCObject *l, GCObject *f) { static void clearkeys (global_State *g, GCObject *l) {
for (; l != f; l = gco2t(l)->gclist) { for (; l; l = gco2t(l)->gclist) {
Table *h = gco2t(l); Table *h = gco2t(l);
Node *n, *limit = gnodelast(h); Node *n, *limit = gnodelast(h);
for (n = gnode(h, 0); n < limit; n++) { for (n = gnode(h, 0); n < limit; n++) {
@ -885,11 +891,13 @@ static void separatetobefnz (global_State *g, int all) {
GCObject *curr; GCObject *curr;
GCObject **p = &g->finobj; GCObject **p = &g->finobj;
GCObject **lastnext = findlast(&g->tobefnz); GCObject **lastnext = findlast(&g->tobefnz);
while ((curr = *p) != NULL) { /* traverse all finalizable objects */ while ((curr = *p) != g->finobjold) { /* traverse all finalizable objects */
lua_assert(tofinalize(curr)); lua_assert(tofinalize(curr));
if (!(iswhite(curr) || all)) /* not being collected? */ if (!(iswhite(curr) || all)) /* not being collected? */
p = &curr->next; /* don't bother with it */ p = &curr->next; /* don't bother with it */
else { else {
if (curr == g->finobjsur)
g->finobjsur = curr->next;
*p = curr->next; /* remove 'curr' from 'finobj' list */ *p = curr->next; /* remove 'curr' from 'finobj' list */
curr->next = *lastnext; /* link at the end of 'tobefnz' list */ curr->next = *lastnext; /* link at the end of 'tobefnz' list */
*lastnext = curr; *lastnext = curr;
@ -915,6 +923,14 @@ void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) {
if (g->sweepgc == &o->next) /* should not remove 'sweepgc' object */ if (g->sweepgc == &o->next) /* should not remove 'sweepgc' object */
g->sweepgc = sweeptolive(L, g->sweepgc); /* change 'sweepgc' */ g->sweepgc = sweeptolive(L, g->sweepgc); /* change 'sweepgc' */
} }
else {
if (o == g->survival)
g->survival = o->next;
if (o == g->old)
g->old = o->next;
if (o == g->reallyold)
g->reallyold = o->next;
}
/* search for pointer pointing to 'o' */ /* search for pointer pointing to 'o' */
for (p = &g->allgc; *p != o; p = &(*p)->next) { /* empty */ } for (p = &g->allgc; *p != o; p = &(*p)->next) { /* empty */ }
*p = o->next; /* remove 'o' from 'allgc' list */ *p = o->next; /* remove 'o' from 'allgc' list */
@ -934,31 +950,65 @@ void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) {
*/ */
/* mask to erase all color bits (not changing gen-related stuff) */
#define maskgencolors (~(bitmask(BLACKBIT) | WHITEBITS))
#if 0 #if 0
static int count (GCObject *p, GCObject *limit) { static int count (GCObject *p, GCObject *limit) {
int res = 0; int res = 0;
for (; p != NULL && p != limit; p = p->next) for (; p != NULL && p != limit; p = p->next) {
res++; res++;
}
return res; return res;
} }
#endif #endif
static GCObject **sweepgen (lua_State *L, GCObject **p, GCObject *limit, static void sweep2old (lua_State *L, GCObject **p) {
int zeromask, int onemask) {
global_State *g = G(L);
int ow = otherwhite(g);
GCObject *curr; GCObject *curr;
while ((curr = *p) != limit) { while ((curr = *p) != NULL) {
int marked = curr->marked; if (iswhite(curr)) { /* is 'curr' dead? */
if (isdeadm(ow, marked)) { /* is 'curr' dead? */ lua_assert(isdead(G(L), curr));
lua_assert(!isold(curr));
*p = curr->next; /* remove 'curr' from list */ *p = curr->next; /* remove 'curr' from list */
freeobj(L, curr); /* erase 'curr' */ freeobj(L, curr); /* erase 'curr' */
} }
else { /* correct mark */ else { /* all surviving objects become old */
if (!isold(curr)) /* don't change old objects */ setage(curr, G_OLD);
curr->marked = cast_byte((marked & zeromask) | onemask); p = &curr->next; /* go to next element */
}
}
}
static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p,
GCObject *limit) {
int white = luaC_white(g);
GCObject *curr;
while ((curr = *p) != limit) {
int marked = curr->marked;
if (iswhite(curr)) { /* is 'curr' dead? */
lua_assert(!isold(curr) && !testbits(curr->marked, white));
*p = curr->next; /* remove 'curr' from list */
freeobj(L, curr); /* erase 'curr' */
}
else { /* correct mark and age */
switch (getage(curr)) {
case G_NEW: /* make white and go to next age */
curr->marked = cast_byte((marked & maskgencolors) | white);
changeage(curr, G_NEW, G_SURVIVAL);
break;
case G_SURVIVAL: /* go to next age */
changeage(curr, G_SURVIVAL, G_OLD1);
break;
case G_OLD0: /* go to next age */
changeage(curr, G_OLD0, G_OLD1);
break;
case G_OLD1: /* go to next age */
changeage(curr, G_OLD1, G_OLD);
break;
default: /* don't change 'old', 'touched1', and 'touched2' */
break;
}
p = &curr->next; /* go to next element */ p = &curr->next; /* go to next element */
} }
} }
@ -966,51 +1016,184 @@ static GCObject **sweepgen (lua_State *L, GCObject **p, GCObject *limit,
} }
static void startgencycle (lua_State *L, global_State *g) { static void whitelist (global_State *g, GCObject *p) {
propagateall(g); int white = luaC_white(g);
atomic(L); for (; p != NULL; p = p->next)
p->marked = cast_byte((p->marked & maskcolors) | white);
} }
static void finishgencycle (lua_State *L, global_State *g, int mask) { static void finishgencycle (lua_State *L, global_State *g) {
sweepgen(L, &g->finobj, NULL, ~0, mask); // sweepgen(L, &g->tobefnz, ~0, mask);
sweepgen(L, &g->tobefnz, NULL, ~0, mask);
checkSizes(L, g); checkSizes(L, g);
g->gcstate = GCSpropagate; /* skip restart */ g->gcstate = GCSpropagate; /* skip restart */
callallpendingfinalizers(L); callallpendingfinalizers(L);
} }
static void printgray (GCObject *o) {
printf("gray: ");
while (o) {
printf("%p %d %02x ", (void*)o, o->tt, o->marked);
switch (o->tt) {
case LUA_TTABLE: {
Table *h = gco2t(o);
o = h->gclist;
break;
}
case LUA_TLCL: {
LClosure *cl = gco2lcl(o);
o = cl->gclist;
break;
}
case LUA_TCCL: {
CClosure *cl = gco2ccl(o);
o = cl->gclist;
break;
}
case LUA_TTHREAD: {
lua_State *th = gco2th(o);
o = th->gclist;
break;
}
case LUA_TPROTO: {
Proto *p = gco2p(o);
o = p->gclist;
break;
}
default: lua_assert(0); return;
}
}
printf("\n");
}
static GCObject **correctgraylist (GCObject **p) {
GCObject *curr;
while ((curr = *p) != NULL) {
switch (curr->tt) {
case LUA_TTABLE: {
Table *h = gco2t(curr);
if (getage(h) == G_TOUCHED1) { /* touched in this cycle? */
lua_assert(isgray(h));
gray2black(h); /* make it black, for next barrier */
changeage(h, G_TOUCHED1, G_TOUCHED2);
p = &h->gclist; /* go to next element */
}
else {
if (!iswhite(h)) {
lua_assert(isold(h));
if (getage(h) == G_TOUCHED2)
changeage(h, G_TOUCHED2, G_OLD);
gray2black(h); /* make it black */
}
*p = h->gclist; /* remove 'curr' from gray list */
}
break;
}
case LUA_TTHREAD: {
lua_State *th = gco2th(curr);
lua_assert(!isblack(th));
if (iswhite(th)) /* new object? */
*p = th->gclist; /* remove from gray list */
else /* old threads remain gray */
p = &th->gclist; /* go to next element */
break;
}
default: lua_assert(0); /* nothing more could be gray here */
}
}
return p;
}
static void correctgraylists (global_State *g) {
GCObject **list = correctgraylist(&g->grayagain);
*list = g->weak; g->weak = NULL;
list = correctgraylist(list);
*list = g->allweak; g->allweak = NULL;
list = correctgraylist(list);
*list = g->ephemeron; g->ephemeron = NULL;
correctgraylist(list);
}
static void markold (global_State *g, GCObject *from, GCObject *to) {
GCObject *p;
for (p = from; p != to; p = p->next) {
if (getage(p) == G_OLD1) {
lua_assert(!iswhite(p));
if (isblack(p)) {
black2gray(p); /* should be '2white', but gray works too */
reallymarkobject(g, p);
}
else
lua_assert(p->tt == LUA_TTHREAD); /* threads are always gray */
}
}
}
static void youngcollection (lua_State *L, global_State *g) { static void youngcollection (lua_State *L, global_State *g) {
GCObject **psurvival; GCObject **psurvival;
lua_assert(g->gcstate == GCSpropagate); lua_assert(g->gcstate == GCSpropagate);
startgencycle(L, g); markold(g, g->survival, g->reallyold);
markold(g, g->finobj, g->finobjrold); /* ??? */
atomic(L);
/* sweep nursery */ /* sweep nursery */
psurvival = sweepgen(L, &g->allgc, g->survival, maskcolors, luaC_white(g)); psurvival = sweepgen(L, g, &g->allgc, g->survival);
lua_assert(*psurvival == g->survival); /* sweep 'survival' and 'old' */
/* sweep 'survival' list, making elements old */ sweepgen(L, g, psurvival, g->reallyold);
sweepgen(L, psurvival, g->old, ~0, bitmask(OLDBIT)); g->reallyold = g->old;
/* incorporate 'survival' list into old list */ g->old = *psurvival; /* 'survival' survivals are old now */
g->old = *psurvival; g->survival = g->allgc; /* all news are survivals */
/* surviving young objects go to 'survival' list */
g->survival = g->allgc; /* repeat for 'finobj' lists */
finishgencycle(L, g, 0); psurvival = sweepgen(L, g, &g->finobj, g->finobjsur);
lua_checkmemory(L); /* sweep 'survival' and 'old' */
sweepgen(L, g, psurvival, g->finobjrold);
g->finobjrold = g->finobjold;
g->finobjold = *psurvival; /* 'survival' survivals are old now */
g->finobjsur = g->finobj; /* all news are survivals */
sweepgen(L, g, &g->tobefnz, NULL);
finishgencycle(L, g);
correctgraylists(g);
//printf("check: \n");lua_checkmemory(L);
} }
static void entergen (lua_State *L, global_State *g) { static void entergen (lua_State *L, global_State *g) {
lua_checkmemory(L); lua_assert(g->reallyold == NULL && g->old == NULL && g->survival == NULL);
lua_assert(g->old == NULL && g->survival == NULL);
luaC_runtilstate(L, bitmask(GCSpause)); /* prepare to start a new cycle */ luaC_runtilstate(L, bitmask(GCSpause)); /* prepare to start a new cycle */
luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */ luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */
startgencycle(L, g); atomic(L);
/* sweep all ellements making them old */ /* sweep all ellements making them old */
sweepgen(L, &g->allgc, g->survival, ~0, bitmask(OLDBIT)); sweep2old(L, &g->allgc);
/* everything alive now is old; 'survival' is empty */ /* everything alive now is old */
g->old = g->survival = g->allgc; g->reallyold = g->old = g->survival = g->allgc;
finishgencycle(L, g, bitmask(OLDBIT));
lua_checkmemory(L); /* repeat for 'finobj' lists */
sweep2old(L, &g->finobj);
g->finobjrold = g->finobjold = g->finobjsur = g->finobj;
finishgencycle(L, g);
correctgraylists(g);
g->gckind = KGC_GEN;
}
static void enterinc (global_State *g) {
makewhite(g, g->mainthread);
whitelist(g, g->allgc);
g->reallyold = g->old = g->survival = NULL;
whitelist(g, g->finobj);
g->finobjrold = g->finobjold = g->finobjsur = NULL;
lua_assert(g->tobefnz == NULL); /* no need to sweep */
g->gcstate = GCSpause;
g->gckind = KGC_NORMAL;
} }
@ -1019,14 +1202,15 @@ void luaC_changemode (lua_State *L, int newmode) {
if (newmode != g->gckind) { /* otherwise, nothing to be done */ if (newmode != g->gckind) { /* otherwise, nothing to be done */
if (newmode == KGC_GEN) /* entering generational mode? */ if (newmode == KGC_GEN) /* entering generational mode? */
entergen(L, g); entergen(L, g);
else { /* entering incremental mode */ else
lua_checkmemory(L); enterinc(g); /* entering incremental mode */
youngcollection(L, g);
g->old = g->survival = NULL;
lua_checkmemory(L);
} }
g->gckind = newmode;
} }
static void fullgen (lua_State *L, global_State *g) {
enterinc(g);
entergen(L, g);
} }
@ -1094,10 +1278,10 @@ static void deletealllist (lua_State *L, GCObject *p) {
void luaC_freeallobjects (lua_State *L) { void luaC_freeallobjects (lua_State *L) {
global_State *g = G(L); global_State *g = G(L);
luaC_changemode(L, KGC_NORMAL);
separatetobefnz(g, 1); /* separate all objects with finalizers */ separatetobefnz(g, 1); /* separate all objects with finalizers */
lua_assert(g->finobj == NULL); lua_assert(g->finobj == NULL);
callallpendingfinalizers(L); callallpendingfinalizers(L);
lua_assert(g->tobefnz == NULL);
deletealllist(L, g->finobj); deletealllist(L, g->finobj);
deletealllist(L, g->allgc); deletealllist(L, g->allgc);
deletealllist(L, g->fixedgc); /* collect fixed objects */ deletealllist(L, g->fixedgc); /* collect fixed objects */
@ -1110,9 +1294,10 @@ static l_mem atomic (lua_State *L) {
l_mem work; l_mem work;
GCObject *origweak, *origall; GCObject *origweak, *origall;
GCObject *grayagain = g->grayagain; /* save original list */ GCObject *grayagain = g->grayagain; /* save original list */
g->grayagain = NULL;
lua_assert(g->ephemeron == NULL && g->weak == NULL); lua_assert(g->ephemeron == NULL && g->weak == NULL);
lua_assert(!iswhite(g->mainthread)); lua_assert(!iswhite(g->mainthread));
g->gcstate = GCSinsideatomic; g->gcstate = GCSatomic;
g->GCmemtrav = 0; /* start counting work */ g->GCmemtrav = 0; /* start counting work */
markobject(g, L); /* mark running thread */ markobject(g, L); /* mark running thread */
/* registry and global metatables may be changed by API */ /* registry and global metatables may be changed by API */
@ -1123,7 +1308,6 @@ static l_mem atomic (lua_State *L) {
propagateall(g); /* propagate changes */ propagateall(g); /* propagate changes */
work = g->GCmemtrav; /* stop counting (do not recount 'grayagain') */ work = g->GCmemtrav; /* stop counting (do not recount 'grayagain') */
g->gray = grayagain; g->gray = grayagain;
g->grayagain = NULL;
propagateall(g); /* traverse 'grayagain' list */ propagateall(g); /* traverse 'grayagain' list */
g->GCmemtrav = 0; /* restart counting */ g->GCmemtrav = 0; /* restart counting */
convergeephemerons(g); convergeephemerons(g);
@ -1141,13 +1325,14 @@ static l_mem atomic (lua_State *L) {
convergeephemerons(g); convergeephemerons(g);
/* at this point, all resurrected objects are marked. */ /* at this point, all resurrected objects are marked. */
/* remove dead objects from weak tables */ /* remove dead objects from weak tables */
clearkeys(g, g->ephemeron, NULL); /* clear keys from all ephemeron tables */ clearkeys(g, g->ephemeron); /* clear keys from all ephemeron tables */
clearkeys(g, g->allweak, NULL); /* clear keys from all 'allweak' tables */ clearkeys(g, g->allweak); /* clear keys from all 'allweak' tables */
/* clear values from resurrected weak tables */ /* clear values from resurrected weak tables */
clearvalues(g, g->weak, origweak); clearvalues(g, g->weak, origweak);
clearvalues(g, g->allweak, origall); clearvalues(g, g->allweak, origall);
luaS_clearcache(g); luaS_clearcache(g);
g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */ g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */
lua_assert(g->gray == NULL);
work += g->GCmemtrav; /* complete counting */ work += g->GCmemtrav; /* complete counting */
return work; /* estimate of memory marked by 'atomic' */ return work; /* estimate of memory marked by 'atomic' */
} }
@ -1181,12 +1366,12 @@ static lu_mem singlestep (lua_State *L) {
case GCSpropagate: { case GCSpropagate: {
g->GCmemtrav = 0; g->GCmemtrav = 0;
if (g->gray == NULL) /* no more gray objects? */ if (g->gray == NULL) /* no more gray objects? */
g->gcstate = GCSatomic; /* finish propagate phase */ g->gcstate = GCSenteratomic; /* finish propagate phase */
else else
propagatemark(g); /* traverse one gray object */ propagatemark(g); /* traverse one gray object */
return g->GCmemtrav; /* memory traversed in this step */ return g->GCmemtrav; /* memory traversed in this step */
} }
case GCSatomic: { case GCSenteratomic: {
lu_mem work; lu_mem work;
propagateall(g); /* make sure gray list is empty */ propagateall(g); /* make sure gray list is empty */
work = atomic(L); /* work is what was traversed by 'atomic' */ work = atomic(L); /* work is what was traversed by 'atomic' */
@ -1291,23 +1476,31 @@ void luaC_step (lua_State *L) {
** to sweep all objects to turn them back to white (as white has not ** to sweep all objects to turn them back to white (as white has not
** changed, nothing will be collected). ** changed, nothing will be collected).
*/ */
void luaC_fullgc (lua_State *L, int isemergency) { static void fullinc (lua_State *L, global_State *g) {
global_State *g = G(L); if (keepinvariant(g)) /* black objects? */
lua_assert(g->gckind == KGC_NORMAL);
if (isemergency) g->gckind = KGC_EMERGENCY; /* set flag */
if (keepinvariant(g)) { /* black objects? */
entersweep(L); /* sweep everything to turn them back to white */ entersweep(L); /* sweep everything to turn them back to white */
}
/* finish any pending sweep phase to start a new cycle */ /* finish any pending sweep phase to start a new cycle */
luaC_runtilstate(L, bitmask(GCSpause)); luaC_runtilstate(L, bitmask(GCSpause));
luaC_runtilstate(L, bitmask(GCScallfin)); /* run up to finalizers */ luaC_runtilstate(L, bitmask(GCScallfin)); /* run up to finalizers */
/* estimate must be correct after a full GC cycle */ /* estimate must be correct after a full GC cycle */
lua_assert(g->GCestimate == gettotalbytes(g)); lua_assert(g->GCestimate == gettotalbytes(g));
luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */ luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */
g->gckind = KGC_NORMAL;
setpause(g); setpause(g);
} }
void luaC_fullgc (lua_State *L, int isemergency) {
global_State *g = G(L);
int gckind = g->gckind;
if (isemergency)
g->gckind = KGC_EMERGENCY; /* set flag */
if (gckind == KGC_NORMAL)
fullinc(L, g);
else
fullgen(L, g);
g->gckind = gckind;
}
/* }====================================================== */ /* }====================================================== */

54
lgc.h
View File

@ -1,5 +1,5 @@
/* /*
** $Id: lgc.h,v 2.91 2015/12/21 13:02:14 roberto Exp roberto $ ** $Id: lgc.h,v 2.92 2017/02/23 21:07:34 roberto Exp roberto $
** Garbage Collector ** Garbage Collector
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -37,13 +37,14 @@
** Possible states of the Garbage Collector ** Possible states of the Garbage Collector
*/ */
#define GCSpropagate 0 #define GCSpropagate 0
#define GCSatomic 1 #define GCSenteratomic 1
#define GCSswpallgc 2 #define GCSatomic 2
#define GCSswpfinobj 3 #define GCSswpallgc 3
#define GCSswptobefnz 4 #define GCSswpfinobj 4
#define GCSswpend 5 #define GCSswptobefnz 5
#define GCScallfin 6 #define GCSswpend 6
#define GCSpause 7 #define GCScallfin 7
#define GCSpause 8
#define issweepphase(g) \ #define issweepphase(g) \
@ -74,14 +75,17 @@
#define testbit(x,b) testbits(x, bitmask(b)) #define testbit(x,b) testbits(x, bitmask(b))
/* Layout for bit use in 'marked' field: */ /*
#define WHITE0BIT 0 /* object is white (type 0) */ ** Layout for bit use in 'marked' field. First three bits are
#define WHITE1BIT 1 /* object is white (type 1) */ ** used for object "age" in generational mode.
#define BLACKBIT 2 /* object is black */ */
#define FINALIZEDBIT 3 /* object has been marked for finalization */ #define WHITE0BIT 3 /* object is white (type 0) */
#define OLDBIT 4 /* object is old (gen. mode) */ #define WHITE1BIT 4 /* object is white (type 1) */
#define BLACKBIT 5 /* object is black */
#define FINALIZEDBIT 6 /* object has been marked for finalization */
#define TESTGRAYBIT 7 /* used by tests (luaL_checkmemory) */ #define TESTGRAYBIT 7 /* used by tests (luaL_checkmemory) */
#define WHITEBITS bit2mask(WHITE0BIT, WHITE1BIT) #define WHITEBITS bit2mask(WHITE0BIT, WHITE1BIT)
@ -89,7 +93,6 @@
#define isblack(x) testbit((x)->marked, BLACKBIT) #define isblack(x) testbit((x)->marked, BLACKBIT)
#define isgray(x) /* neither white nor black */ \ #define isgray(x) /* neither white nor black */ \
(!testbits((x)->marked, WHITEBITS | bitmask(BLACKBIT))) (!testbits((x)->marked, WHITEBITS | bitmask(BLACKBIT)))
#define isold(x) testbit((x)->marked, OLDBIT)
#define tofinalize(x) testbit((x)->marked, FINALIZEDBIT) #define tofinalize(x) testbit((x)->marked, FINALIZEDBIT)
@ -103,6 +106,27 @@
#define luaC_white(g) cast(lu_byte, (g)->currentwhite & WHITEBITS) #define luaC_white(g) cast(lu_byte, (g)->currentwhite & WHITEBITS)
/* object age in generational mode */
#define G_NEW 0 /* created in current cycle */
#define G_SURVIVAL 1 /* created in previous cycle */
#define G_OLD1 2 /* first full cycle as old */
#define G_OLD0 3 /* marked old by frw. barrier in this cycle */
#define G_OLD 4 /* really old object (not to be visited) */
#define G_TOUCHED1 5 /* old object touched this cycle */
#define G_TOUCHED2 6 /* old object touched in previous cycle */
#define AGEBITS 7 /* all age bits (111) */
#define getage(o) ((o)->marked & AGEBITS)
#define setage(o,a) ((o)->marked = cast_byte(((o)->marked & (~AGEBITS)) | a))
#define isold(o) (getage(o) > G_SURVIVAL)
#define changeage(o,f,t) \
check_exp(getage(o) == (f), (o)->marked ^= ((f)^(t)))
#define ongraylist(o) (isgray(o) || getage(o) == G_TOUCHED2)
/* /*
** Does one step of collection when debt becomes positive. 'pre'/'pos' ** Does one step of collection when debt becomes positive. 'pre'/'pos'
** allows some adjustments to be done only when needed. macro ** allows some adjustments to be done only when needed. macro

View File

@ -1,5 +1,5 @@
/* /*
** $Id: lstate.c,v 2.133 2015/11/13 12:16:51 roberto Exp roberto $ ** $Id: lstate.c,v 2.134 2017/02/23 21:07:34 roberto Exp roberto $
** Global State ** Global State
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -319,7 +319,8 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
g->gcstate = GCSpause; g->gcstate = GCSpause;
g->gckind = KGC_NORMAL; g->gckind = KGC_NORMAL;
g->allgc = g->finobj = g->tobefnz = g->fixedgc = NULL; g->allgc = g->finobj = g->tobefnz = g->fixedgc = NULL;
g->survival = g->old = NULL; g->survival = g->old = g->reallyold = NULL;
g->finobjsur = g->finobjold = g->finobjrold = NULL;
g->sweepgc = NULL; g->sweepgc = NULL;
g->gray = g->grayagain = NULL; g->gray = g->grayagain = NULL;
g->weak = g->ephemeron = g->allweak = NULL; g->weak = g->ephemeron = g->allweak = NULL;

View File

@ -1,5 +1,5 @@
/* /*
** $Id: lstate.h,v 2.134 2017/02/15 18:52:13 roberto Exp roberto $ ** $Id: lstate.h,v 2.135 2017/02/23 21:07:34 roberto Exp $
** Global State ** Global State
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -160,8 +160,12 @@ typedef struct global_State {
GCObject *tobefnz; /* list of userdata to be GC */ GCObject *tobefnz; /* list of userdata to be GC */
GCObject *fixedgc; /* list of objects not to be collected */ GCObject *fixedgc; /* list of objects not to be collected */
/* fields for generational collector */ /* fields for generational collector */
GCObject *old; /* start of old objects */
GCObject *survival; /* start of objects that survived one GC cycle */ GCObject *survival; /* start of objects that survived one GC cycle */
GCObject *old; /* start of old objects */
GCObject *reallyold; /* old objects with more than one cycle */
GCObject *finobjsur; /* list of survival objects with finalizers */
GCObject *finobjold; /* list of old objects with finalizers */
GCObject *finobjrold; /* list of really old objects with finalizers */
struct lua_State *twups; /* list of threads with open upvalues */ struct lua_State *twups; /* list of threads with open upvalues */
unsigned int gcfinnum; /* number of finalizers to call in each GC step */ unsigned int gcfinnum; /* number of finalizers to call in each GC step */
int gcpause; /* size of pause between successive GCs */ int gcpause; /* size of pause between successive GCs */