diff --git a/lapi.c b/lapi.c index 34e64af1..3c620d4b 100644 --- a/lapi.c +++ b/lapi.c @@ -1154,11 +1154,11 @@ LUA_API int lua_gc (lua_State *L, int what, ...) { } case LUA_GCCOUNT: { /* GC values are expressed in Kbytes: #bytes/2^10 */ - res = cast_int(gettotalbytes(g) >> 10); + res = cast_int(g->totalbytes >> 10); break; } case LUA_GCCOUNTB: { - res = cast_int(gettotalbytes(g) & 0x3ff); + res = cast_int(g->totalbytes & 0x3ff); break; } case LUA_GCSTEP: { @@ -1171,7 +1171,7 @@ LUA_API int lua_gc (lua_State *L, int what, ...) { luaC_step(L); } else { /* add 'data' to total debt */ - debt = cast(l_mem, data) * 1024 + g->GCdebt; + debt = data + g->GCdebt; luaE_setdebt(g, debt); luaC_checkGC(L); } @@ -1217,7 +1217,8 @@ LUA_API int lua_gc (lua_State *L, int what, ...) { if (stepmul != 0) setgcparam(g->gcstepmul, stepmul); if (stepsize != 0) - g->gcstepsize = stepsize; + g->gcstepsize = (stepsize <= log2maxs(l_mem)) ? stepsize + : log2maxs(l_mem); luaC_changemode(L, KGC_INC); break; } diff --git a/lgc.c b/lgc.c index 20e9b4aa..9c5cc2e3 100644 --- a/lgc.c +++ b/lgc.c @@ -19,6 +19,7 @@ #include "ldo.h" #include "lfunc.h" #include "lgc.h" +#include "llex.h" #include "lmem.h" #include "lobject.h" #include "lstate.h" @@ -32,7 +33,7 @@ ** (Large enough to dissipate fixed overheads but small enough ** to allow small steps for the collector.) */ -#define GCSWEEPMAX 100 +#define GCSWEEPMAX 20 /* ** Maximum number of finalizers to call in each single step. @@ -46,19 +47,6 @@ #define GCFINALIZECOST 50 -/* -** The equivalent, in bytes, of one unit of "work" (visiting a slot, -** sweeping an object, etc.) -*/ -#define WORK2MEM sizeof(TValue) - - -/* -** macro to adjust 'pause': 'pause' is actually used like -** 'pause / PAUSEADJ' (value chosen by tests) -*/ -#define PAUSEADJ 100 - /* mask with all color bits */ #define maskcolors (bitmask(BLACKBIT) | WHITEBITS) @@ -105,7 +93,7 @@ #define markobjectN(g,t) { if (t) markobject(g,t); } static void reallymarkobject (global_State *g, GCObject *o); -static lu_mem atomic (lua_State *L); +static l_mem atomic (lua_State *L); static void entersweep (lua_State *L); @@ -259,7 +247,7 @@ GCObject *luaC_newobjdt (lua_State *L, int tt, size_t sz, size_t offset) { global_State *g = G(L); char *p = cast_charp(luaM_newobject(L, novariant(tt), sz)); GCObject *o = cast(GCObject *, p + offset); - g->totalobjs++; + g->GCdebt++; o->marked = luaC_white(g); o->tt = tt; o->next = g->allgc; @@ -268,6 +256,9 @@ GCObject *luaC_newobjdt (lua_State *L, int tt, size_t sz, size_t offset) { } +/* +** create a new collectable object with no offset. +*/ GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) { return luaC_newobjdt(L, tt, sz, 0); } @@ -296,6 +287,7 @@ GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) { ** (only closures can), and a userdata's metatable must be a table. */ static void reallymarkobject (global_State *g, GCObject *o) { + g->marked++; switch (o->tt) { case LUA_VSHRSTR: case LUA_VLNGSTR: { @@ -343,9 +335,9 @@ static void markmt (global_State *g) { /* ** mark all objects in list of being-finalized */ -static lu_mem markbeingfnz (global_State *g) { +static l_mem markbeingfnz (global_State *g) { GCObject *o; - lu_mem count = 0; + l_mem count = 0; for (o = g->tobefnz; o != NULL; o = o->next) { count++; markobject(g, o); @@ -365,12 +357,11 @@ static lu_mem markbeingfnz (global_State *g) { ** upvalues, as they have nothing to be checked. (If the thread gets an ** upvalue later, it will be linked in the list again.) */ -static int remarkupvals (global_State *g) { +static l_mem remarkupvals (global_State *g) { + l_mem work = 0; lua_State *thread; lua_State **p = &g->twups; - int work = 0; /* estimate of how much work was done here */ while ((thread = *p) != NULL) { - work++; if (!iswhite(thread) && thread->openupval != NULL) p = &thread->twups; /* keep marked thread with upvalues in the list */ else { /* thread is not marked or without upvalues */ @@ -380,13 +371,13 @@ static int remarkupvals (global_State *g) { thread->twups = thread; /* mark that it is out of list */ for (uv = thread->openupval; uv != NULL; uv = uv->u.open.next) { lua_assert(getage(uv) <= getage(thread)); - work++; if (!iswhite(uv)) { /* upvalue already visited? */ lua_assert(upisopen(uv) && isgray(uv)); markvalue(g, uv->v.p); /* mark its value */ } } } + work++; } return work; } @@ -399,10 +390,15 @@ static void cleargraylists (global_State *g) { /* -** mark root set and reset all gray lists, to start a new collection +** mark root set and reset all gray lists, to start a new collection. +** 'marked' is initialized with the number of fixed objects in the state, +** to count the total number of live objects during a cycle. (That is +** the metafield names, plus the reserved words, plus "_ENV" plus the +** memory-error message.) */ static void restartcollection (global_State *g) { cleargraylists(g); + g->marked = TM_N + NUM_RESERVED + 2; markobject(g, g->mainthread); markvalue(g, &g->l_registry); markmt(g); @@ -540,7 +536,7 @@ static void traversestrongtable (global_State *g, Table *h) { } -static lu_mem traversetable (global_State *g, Table *h) { +static void traversetable (global_State *g, Table *h) { const char *weakkey, *weakvalue; const TValue *mode = gfasttm(g, h->metatable, TM_MODE); markobjectN(g, h->metatable); @@ -557,17 +553,15 @@ static lu_mem traversetable (global_State *g, Table *h) { } else /* not weak */ traversestrongtable(g, h); - return 1 + h->alimit + 2 * allocsizenode(h); } -static int traverseudata (global_State *g, Udata *u) { +static void traverseudata (global_State *g, Udata *u) { int i; markobjectN(g, u->metatable); /* mark its metatable */ for (i = 0; i < u->nuvalue; i++) markvalue(g, &u->uv[i].uv); genlink(g, obj2gco(u)); - return 1 + u->nuvalue; } @@ -576,7 +570,7 @@ static int traverseudata (global_State *g, Udata *u) { ** arrays can be larger than needed; the extra slots are filled with ** NULL, so the use of 'markobjectN') */ -static int traverseproto (global_State *g, Proto *f) { +static void traverseproto (global_State *g, Proto *f) { int i; markobjectN(g, f->source); for (i = 0; i < f->sizek; i++) /* mark literals */ @@ -587,29 +581,26 @@ static int traverseproto (global_State *g, Proto *f) { markobjectN(g, f->p[i]); for (i = 0; i < f->sizelocvars; i++) /* mark local-variable names */ markobjectN(g, f->locvars[i].varname); - return 1 + f->sizek + f->sizeupvalues + f->sizep + f->sizelocvars; } -static int traverseCclosure (global_State *g, CClosure *cl) { +static void traverseCclosure (global_State *g, CClosure *cl) { int i; for (i = 0; i < cl->nupvalues; i++) /* mark its upvalues */ markvalue(g, &cl->upvalue[i]); - return 1 + cl->nupvalues; } /* ** Traverse a Lua closure, marking its prototype and its upvalues. ** (Both can be NULL while closure is being created.) */ -static int traverseLclosure (global_State *g, LClosure *cl) { +static void traverseLclosure (global_State *g, LClosure *cl) { int i; markobjectN(g, cl->p); /* mark its prototype */ for (i = 0; i < cl->nupvalues; i++) { /* visit its upvalues */ UpVal *uv = cl->upvals[i]; markobjectN(g, uv); /* mark upvalue */ } - return 1 + cl->nupvalues; } @@ -625,13 +616,13 @@ static int traverseLclosure (global_State *g, LClosure *cl) { ** (which can only happen in generational mode) or if the traverse is in ** the propagate phase (which can only happen in incremental mode). */ -static int traversethread (global_State *g, lua_State *th) { +static void traversethread (global_State *g, lua_State *th) { UpVal *uv; StkId o = th->stack.p; if (isold(th) || g->gcstate == GCSpropagate) linkgclist(th, g->grayagain); /* insert into 'grayagain' list */ if (o == NULL) - return 1; /* stack not completely built yet */ + return; /* stack not completely built yet */ lua_assert(g->gcstate == GCSatomic || th->openupval == NULL || isintwups(th)); for (; o < th->top.p; o++) /* mark live elements in the stack */ @@ -649,34 +640,35 @@ static int traversethread (global_State *g, lua_State *th) { } else if (!g->gcemergency) luaD_shrinkstack(th); /* do not change stack in emergency cycle */ - return 1 + stacksize(th); } /* ** traverse one gray object, turning it to black. */ -static lu_mem propagatemark (global_State *g) { +static void propagatemark (global_State *g) { GCObject *o = g->gray; nw2black(o); g->gray = *getgclist(o); /* remove from 'gray' list */ switch (o->tt) { - case LUA_VTABLE: return traversetable(g, gco2t(o)); - case LUA_VUSERDATA: return traverseudata(g, gco2u(o)); - case LUA_VLCL: return traverseLclosure(g, gco2lcl(o)); - case LUA_VCCL: return traverseCclosure(g, gco2ccl(o)); - case LUA_VPROTO: return traverseproto(g, gco2p(o)); - case LUA_VTHREAD: return traversethread(g, gco2th(o)); - default: lua_assert(0); return 0; + case LUA_VTABLE: traversetable(g, gco2t(o)); break; + case LUA_VUSERDATA: traverseudata(g, gco2u(o)); break; + case LUA_VLCL: traverseLclosure(g, gco2lcl(o)); break; + case LUA_VCCL: traverseCclosure(g, gco2ccl(o)); break; + case LUA_VPROTO: traverseproto(g, gco2p(o)); break; + case LUA_VTHREAD: traversethread(g, gco2th(o)); break; + default: lua_assert(0); } } -static lu_mem propagateall (global_State *g) { - lu_mem tot = 0; - while (g->gray) - tot += propagatemark(g); - return tot; +static l_mem propagateall (global_State *g) { + l_mem work = 0; + while (g->gray) { + propagatemark(g); + work++; + } + return work; } @@ -685,10 +677,10 @@ static lu_mem propagateall (global_State *g) { ** Repeat until it converges, that is, nothing new is marked. 'dir' ** inverts the direction of the traversals, trying to speed up ** convergence on chains in the same table. -** */ -static void convergeephemerons (global_State *g) { +static l_mem convergeephemerons (global_State *g) { int changed; + l_mem work = 0; int dir = 0; do { GCObject *w; @@ -703,9 +695,11 @@ static void convergeephemerons (global_State *g) { propagateall(g); /* propagate changes */ changed = 1; /* will have to revisit all ephemeron tables */ } + work++; } dir = !dir; /* invert direction next time */ } while (changed); /* repeat until no more changes */ + return work; } /* }====================================================== */ @@ -721,7 +715,8 @@ static void convergeephemerons (global_State *g) { /* ** clear entries with unmarked keys from all weaktables in list 'l' */ -static void clearbykeys (global_State *g, GCObject *l) { +static l_mem clearbykeys (global_State *g, GCObject *l) { + l_mem work = 0; for (; l; l = gco2t(l)->gclist) { Table *h = gco2t(l); Node *limit = gnodelast(h); @@ -732,7 +727,9 @@ static void clearbykeys (global_State *g, GCObject *l) { if (isempty(gval(n))) /* is entry empty? */ clearkey(n); /* clear its key */ } + work++; } + return work; } @@ -740,7 +737,8 @@ static void clearbykeys (global_State *g, GCObject *l) { ** clear entries with unmarked values from all weaktables in list 'l' up ** to element 'f' */ -static void clearbyvalues (global_State *g, GCObject *l, GCObject *f) { +static l_mem clearbyvalues (global_State *g, GCObject *l, GCObject *f) { + l_mem work = 0; for (; l != f; l = gco2t(l)->gclist) { Table *h = gco2t(l); Node *n, *limit = gnodelast(h); @@ -757,7 +755,9 @@ static void clearbyvalues (global_State *g, GCObject *l, GCObject *f) { if (isempty(gval(n))) /* is entry empty? */ clearkey(n); /* clear its key */ } + work++; } + return work; } @@ -819,10 +819,9 @@ static void freeobj (lua_State *L, GCObject *o) { ** objects, where a dead object is one marked with the old (non current) ** white; change all non-dead objects back to white, preparing for next ** collection cycle. Return where to continue the traversal or NULL if -** list is finished. ('*countout' gets the number of elements traversed.) +** list is finished. */ -static GCObject **sweeplist (lua_State *L, GCObject **p, int countin, - int *countout) { +static GCObject **sweeplist (lua_State *L, GCObject **p, int countin) { global_State *g = G(L); int ow = otherwhite(g); int i; @@ -839,8 +838,6 @@ static GCObject **sweeplist (lua_State *L, GCObject **p, int countin, p = &curr->next; /* go to next element */ } } - if (countout) - *countout = i; /* number of elements traversed */ return (*p == NULL) ? NULL : p; } @@ -851,7 +848,7 @@ static GCObject **sweeplist (lua_State *L, GCObject **p, int countin, static GCObject **sweeptolive (lua_State *L, GCObject **p) { GCObject **old = p; do { - p = sweeplist(L, p, 1, NULL); + p = sweeplist(L, p, 1); } while (p == old); return p; } @@ -870,11 +867,8 @@ static GCObject **sweeptolive (lua_State *L, GCObject **p) { */ static void checkSizes (lua_State *L, global_State *g) { if (!g->gcemergency) { - if (g->strt.nuse < g->strt.size / 4) { /* string table too big? */ - l_mem olddebt = g->GCdebt; + if (g->strt.nuse < g->strt.size / 4) /* string table too big? */ luaS_resize(L, g->strt.size / 2); - g->GCestimate += g->GCdebt - olddebt; /* correct estimate */ - } } } @@ -935,12 +929,11 @@ static void GCTM (lua_State *L) { /* ** Call a few finalizers */ -static int runafewfinalizers (lua_State *L, int n) { +static void runafewfinalizers (lua_State *L, int n) { global_State *g = G(L); int i; for (i = 0; i < n && g->tobefnz; i++) GCTM(L); /* call one finalizer */ - return i; } @@ -1052,19 +1045,16 @@ void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) { /* ** Set the "time" to wait before starting a new GC cycle; cycle will -** start when memory use hits the threshold of ('estimate' * pause / -** PAUSEADJ). (Division by 'estimate' should be OK: it cannot be zero, -** because Lua cannot even start with less than PAUSEADJ bytes). +** start when number of objects in use hits the threshold of +** approximately ('marked' * pause / 100). (A direct multiplication +** by 'pause' may overflow, and a direct division by 100 may undeflow +** to zero. So, the division is done in two steps. 8 * 12 is near 100 +** and the division by 8 is cheap.) */ static void setpause (global_State *g) { - l_mem threshold, debt; - int pause = getgcparam(g->gcpause); - l_mem estimate = g->GCestimate / PAUSEADJ; /* adjust 'estimate' */ - lua_assert(estimate > 0); - threshold = (pause < MAX_LMEM / estimate) /* overflow? */ - ? estimate * pause /* no overflow */ - : MAX_LMEM; /* overflow; truncate to maximum */ - debt = gettotalbytes(g) - threshold; + unsigned int pause = getgcparam(g->gcpause); + lu_mem threshold = g->marked / 8 * pause / 12; + l_mem debt = gettotalobjs(g) - threshold; if (debt > 0) debt = 0; luaE_setdebt(g, debt); } @@ -1306,17 +1296,17 @@ static void atomic2gen (lua_State *L, global_State *g) { g->gckind = KGC_GEN; g->lastatomic = 0; - g->GCestimate = gettotalbytes(g); /* base for memory control */ + g->GCestimate = gettotalobjs(g); /* base for memory control */ finishgencycle(L, g); } /* ** Set debt for the next minor collection, which will happen when -** memory grows 'genminormul'%. +** total number of objects grows 'genminormul'%. */ static void setminordebt (global_State *g) { - luaE_setdebt(g, -(cast(l_mem, (gettotalbytes(g) / 100)) * g->genminormul)); + luaE_setdebt(g, -(cast(l_mem, (gettotalobjs(g) / 100)) * g->genminormul)); } @@ -1326,14 +1316,12 @@ static void setminordebt (global_State *g) { ** are cleared. Then, turn all objects into old and finishes the ** collection. */ -static lu_mem entergen (lua_State *L, global_State *g) { - lu_mem numobjs; +static void entergen (lua_State *L, global_State *g) { luaC_runtilstate(L, bitmask(GCSpause)); /* prepare to start a new cycle */ luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */ - numobjs = atomic(L); /* propagates all and then do the atomic stuff */ + atomic(L); /* propagates all and then do the atomic stuff */ atomic2gen(L, g); setminordebt(g); /* set debt assuming next cycle will be minor */ - return numobjs; } @@ -1372,9 +1360,9 @@ void luaC_changemode (lua_State *L, int newmode) { /* ** Does a full collection in generational mode. */ -static lu_mem fullgen (lua_State *L, global_State *g) { +static void fullgen (lua_State *L, global_State *g) { enterinc(g); - return entergen(L, g); + entergen(L, g); } @@ -1400,22 +1388,22 @@ static lu_mem fullgen (lua_State *L, global_State *g) { ** ('g->lastatomic != 0' also means that the last collection was bad.) */ static void stepgenfull (lua_State *L, global_State *g) { - lu_mem newatomic; /* count of traversed objects */ lu_mem lastatomic = g->lastatomic; /* count from last collection */ if (g->gckind == KGC_GEN) /* still in generational mode? */ enterinc(g); /* enter incremental mode */ luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */ - newatomic = atomic(L); /* mark everybody */ - if (newatomic < lastatomic + (lastatomic >> 3)) { /* good collection? */ + g->marked = 0; + atomic(L); /* mark everybody */ + if (g->marked < lastatomic + (lastatomic >> 3)) { /* good collection? */ atomic2gen(L, g); /* return to generational mode */ setminordebt(g); } else { /* another bad collection; stay in incremental mode */ - g->GCestimate = gettotalbytes(g); /* first estimate */; + g->GCestimate = gettotalobjs(g); /* first estimate */; + g->lastatomic = g->marked; entersweep(L); luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */ setpause(g); - g->lastatomic = newatomic; } } @@ -1443,21 +1431,23 @@ static void genstep (lua_State *L, global_State *g) { if (g->lastatomic != 0) /* last collection was a bad one? */ stepgenfull(L, g); /* do a full step */ else { - lu_mem majorbase = g->GCestimate; /* memory after last major collection */ - lu_mem majorinc = (majorbase / 100) * getgcparam(g->genmajormul); - if (g->GCdebt > 0 && gettotalbytes(g) > majorbase + majorinc) { - lu_mem numobjs = fullgen(L, g); /* do a major collection */ - if (gettotalbytes(g) < majorbase + (majorinc / 2)) { - /* collected at least half of memory growth since last major + l_mem majorbase = g->GCestimate; /* objects after last major collection */ + l_mem majorinc = (majorbase / 100) * getgcparam(g->genmajormul); + if (g->GCdebt > 0 && gettotalobjs(g) > majorbase + majorinc) { + g->marked = 0; + fullgen(L, g); /* do a major collection */ + if (gettotalobjs(g) < majorbase + (majorinc / 2)) { + /* collected at least half of object growth since last major collection; keep doing minor collections. */ lua_assert(g->lastatomic == 0); } else { /* bad collection */ - g->lastatomic = numobjs; /* signal that last collection was bad */ + g->lastatomic = g->marked; /* signal that last collection was bad */ setpause(g); /* do a long wait for next (major) collection */ } } else { /* regular case; do a minor collection */ + g->marked = 0; youngcollection(L, g); setminordebt(g); g->GCestimate = majorbase; /* preserve base value */ @@ -1522,9 +1512,9 @@ void luaC_freeallobjects (lua_State *L) { } -static lu_mem atomic (lua_State *L) { +static l_mem atomic (lua_State *L) { + l_mem work = 0; global_State *g = G(L); - lu_mem work = 0; GCObject *origweak, *origall; GCObject *grayagain = g->grayagain; /* save original list */ g->grayagain = NULL; @@ -1541,50 +1531,47 @@ static lu_mem atomic (lua_State *L) { work += propagateall(g); /* propagate changes */ g->gray = grayagain; work += propagateall(g); /* traverse 'grayagain' list */ - convergeephemerons(g); + work += convergeephemerons(g); /* at this point, all strongly accessible objects are marked. */ /* Clear values from weak tables, before checking finalizers */ - clearbyvalues(g, g->weak, NULL); - clearbyvalues(g, g->allweak, NULL); + work += clearbyvalues(g, g->weak, NULL); + work += clearbyvalues(g, g->allweak, NULL); origweak = g->weak; origall = g->allweak; separatetobefnz(g, 0); /* separate objects to be finalized */ work += markbeingfnz(g); /* mark objects that will be finalized */ work += propagateall(g); /* remark, to propagate 'resurrection' */ - convergeephemerons(g); + work += convergeephemerons(g); /* at this point, all resurrected objects are marked. */ /* remove dead objects from weak tables */ - clearbykeys(g, g->ephemeron); /* clear keys from all ephemeron tables */ - clearbykeys(g, g->allweak); /* clear keys from all 'allweak' tables */ + work += clearbykeys(g, g->ephemeron); /* clear keys from all ephemeron */ + work += clearbykeys(g, g->allweak); /* clear keys from all 'allweak' */ /* clear values from resurrected weak tables */ - clearbyvalues(g, g->weak, origweak); - clearbyvalues(g, g->allweak, origall); + work += clearbyvalues(g, g->weak, origweak); + work += clearbyvalues(g, g->allweak, origall); luaS_clearcache(g); g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */ lua_assert(g->gray == NULL); - return work; /* estimate of slots marked by 'atomic' */ + return work; } -static int sweepstep (lua_State *L, global_State *g, - int nextstate, GCObject **nextlist) { +static void sweepstep (lua_State *L, global_State *g, + int nextstate, GCObject **nextlist) { if (g->sweepgc) { l_mem olddebt = g->GCdebt; - int count; - g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX, &count); + g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX); g->GCestimate += g->GCdebt - olddebt; /* update estimate */ - return count; } else { /* enter next state */ g->gcstate = nextstate; g->sweepgc = nextlist; - return 0; /* no work done */ } } -static lu_mem singlestep (lua_State *L) { +static l_mem singlestep (lua_State *L) { global_State *g = G(L); - lu_mem work; + l_mem work; lua_assert(!g->gcstopem); /* collector is not reentrant */ g->gcstopem = 1; /* no emergency collections while collecting */ switch (g->gcstate) { @@ -1599,26 +1586,30 @@ static lu_mem singlestep (lua_State *L) { g->gcstate = GCSenteratomic; /* finish propagate phase */ work = 0; } - else - work = propagatemark(g); /* traverse one gray object */ + else { + propagatemark(g); /* traverse one gray object */ + work = 1; + } break; } case GCSenteratomic: { - work = atomic(L); /* work is what was traversed by 'atomic' */ + work = atomic(L); entersweep(L); - g->GCestimate = gettotalbytes(g); /* first estimate */; break; } case GCSswpallgc: { /* sweep "regular" objects */ - work = sweepstep(L, g, GCSswpfinobj, &g->finobj); + sweepstep(L, g, GCSswpfinobj, &g->finobj); + work = GCSWEEPMAX; break; } case GCSswpfinobj: { /* sweep objects with finalizers */ - work = sweepstep(L, g, GCSswptobefnz, &g->tobefnz); + sweepstep(L, g, GCSswptobefnz, &g->tobefnz); + work = GCSWEEPMAX; break; } case GCSswptobefnz: { /* sweep objects to be finalized */ - work = sweepstep(L, g, GCSswpend, NULL); + sweepstep(L, g, GCSswpend, NULL); + work = GCSWEEPMAX; break; } case GCSswpend: { /* finish sweeps */ @@ -1630,7 +1621,8 @@ static lu_mem singlestep (lua_State *L) { case GCScallfin: { /* call remaining finalizers */ if (g->tobefnz && !g->gcemergency) { g->gcstopem = 0; /* ok collections during finalizers */ - work = runafewfinalizers(L, GCFINMAX) * GCFINALIZECOST; + runafewfinalizers(L, GCFINMAX); + work = GCFINMAX * GCFINALIZECOST; } else { /* emergency mode or no more finalizers */ g->gcstate = GCSpause; /* finish collection */ @@ -1666,18 +1658,16 @@ void luaC_runtilstate (lua_State *L, int statesmask) { */ static void incstep (lua_State *L, global_State *g) { int stepmul = (getgcparam(g->gcstepmul) | 1); /* avoid division by 0 */ - l_mem debt = (g->GCdebt / WORK2MEM) * stepmul; - l_mem stepsize = (g->gcstepsize <= log2maxs(l_mem)) - ? ((cast(l_mem, 1) << g->gcstepsize) / WORK2MEM) * stepmul - : MAX_LMEM; /* overflow; keep maximum value */ + l_mem debt = (g->GCdebt / 100) * stepmul; + l_mem stepsize = cast(l_mem, 1) << g->gcstepsize; do { /* repeat until pause or enough "credit" (negative debt) */ - lu_mem work = singlestep(L); /* perform one single step */ + l_mem work = singlestep(L); /* perform one single step */ debt -= work; } while (debt > -stepsize && g->gcstate != GCSpause); if (g->gcstate == GCSpause) setpause(g); /* pause until next cycle */ else { - debt = (debt / stepmul) * WORK2MEM; /* convert 'work units' to bytes */ + debt = (debt / stepmul) * 100; /* apply step multiplier */ luaE_setdebt(g, debt); } } @@ -1710,9 +1700,8 @@ static void fullinc (lua_State *L, global_State *g) { /* finish any pending sweep phase to start a new cycle */ luaC_runtilstate(L, bitmask(GCSpause)); luaC_runtilstate(L, bitmask(GCScallfin)); /* run up to finalizers */ - /* estimate must be correct after a full GC cycle */ - lua_assert(g->GCestimate == gettotalbytes(g)); luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */ + /* estimate must be correct after a full GC cycle */ setpause(g); } diff --git a/lgc.h b/lgc.h index c960e706..a4c54b47 100644 --- a/lgc.h +++ b/lgc.h @@ -135,10 +135,10 @@ #define getgcparam(p) ((p) * 4) #define setgcparam(p,v) ((p) = (v) / 4) -#define LUAI_GCMUL 100 +#define LUAI_GCMUL 300 -/* how much to allocate before next GC step (log2) */ -#define LUAI_GCSTEPSIZE 13 /* 8 KB */ +/* how many objects to allocate before next GC step (log2) */ +#define LUAI_GCSTEPSIZE 8 /* 256 objects */ /* diff --git a/llimits.h b/llimits.h index 52a32f92..ef108f5f 100644 --- a/llimits.h +++ b/llimits.h @@ -57,7 +57,7 @@ typedef signed char ls_byte; ** floor of the log2 of the maximum signed value for integral type 't'. ** (That is, maximum 'n' such that '2^n' fits in the given signed type.) */ -#define log2maxs(t) (sizeof(t) * 8 - 2) +#define log2maxs(t) cast_int(sizeof(t) * 8 - 2) /* diff --git a/lmem.c b/lmem.c index 9029d588..66e2b92b 100644 --- a/lmem.c +++ b/lmem.c @@ -133,7 +133,7 @@ void luaM_free_ (lua_State *L, void *block, size_t osize) { global_State *g = G(L); lua_assert((osize == 0) == (block == NULL)); (*g->frealloc)(g->ud, block, osize, 0); - g->GCdebt -= osize; + g->totalbytes -= osize; } @@ -167,10 +167,10 @@ void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) { if (l_unlikely(newblock == NULL && nsize > 0)) { newblock = tryagain(L, block, osize, nsize); if (newblock == NULL) /* still no memory? */ - return NULL; /* do not update 'GCdebt' */ + return NULL; /* do not update 'totalbytes' */ } lua_assert((nsize == 0) == (newblock == NULL)); - g->GCdebt = (g->GCdebt + nsize) - osize; + g->totalbytes += nsize - osize; return newblock; } @@ -195,7 +195,7 @@ void *luaM_malloc_ (lua_State *L, size_t size, int tag) { if (newblock == NULL) luaM_error(L); } - g->GCdebt += size; + g->totalbytes += size; return newblock; } } diff --git a/lstate.c b/lstate.c index 3091a00e..9f615342 100644 --- a/lstate.c +++ b/lstate.c @@ -83,15 +83,15 @@ static unsigned int luai_makeseed (lua_State *L) { /* -** set GCdebt to a new value keeping the value (totalbytes + GCdebt) -** invariant (and avoiding underflows in 'totalbytes') +** set GCdebt to a new value keeping the value (totalobjs + GCdebt) +** invariant (and avoiding underflows in 'totalobjs') */ void luaE_setdebt (global_State *g, l_mem debt) { - l_mem tb = gettotalbytes(g); + l_mem tb = gettotalobjs(g); lua_assert(tb > 0); if (debt < tb - MAX_LMEM) - debt = tb - MAX_LMEM; /* will make 'totalbytes == MAX_LMEM' */ - g->totalbytes = tb - debt; + debt = tb - MAX_LMEM; /* will make 'totalobjs == MAX_LMEM' */ + g->totalobjs = tb - debt; g->GCdebt = debt; } @@ -278,8 +278,8 @@ static void close_state (lua_State *L) { } luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size); freestack(L); - lua_assert(gettotalbytes(g) == sizeof(LG)); - lua_assert(g->totalobjs == 1); + lua_assert(g->totalbytes == sizeof(LG)); + lua_assert(gettotalobjs(g) == 1); (*g->frealloc)(g->ud, fromstate(L), sizeof(LG), 0); /* free main block */ } @@ -389,6 +389,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { g->twups = NULL; g->totalbytes = sizeof(LG); g->totalobjs = 1; + g->marked = 0; g->GCdebt = 0; g->lastatomic = 0; setivalue(&g->nilvalue, 0); /* to signal that state is not yet built */ diff --git a/lstate.h b/lstate.h index 62ad61c6..3ffd09b7 100644 --- a/lstate.h +++ b/lstate.h @@ -249,9 +249,10 @@ typedef struct CallInfo { typedef struct global_State { lua_Alloc frealloc; /* function to reallocate memory */ void *ud; /* auxiliary data to 'frealloc' */ - l_mem totalbytes; /* number of bytes currently allocated - GCdebt */ - l_mem totalobjs; /* total number of objects allocated */ + l_mem totalbytes; /* number of bytes currently allocated */ + l_mem totalobjs; /* total number of objects allocated - GCdebt */ l_mem GCdebt; /* bytes allocated not yet compensated by the collector */ + lu_mem marked; /* number of objects marked in a GC cycle */ lu_mem GCestimate; /* an estimate of the non-garbage memory in use */ lu_mem lastatomic; /* see function 'genstep' in file 'lgc.c' */ stringtable strt; /* hash table for strings */ @@ -386,8 +387,9 @@ union GCUnion { #define obj2gco(v) check_exp((v)->tt >= LUA_TSTRING, &(cast_u(v)->gc)) -/* actual number of total bytes allocated */ -#define gettotalbytes(g) cast(lu_mem, (g)->totalbytes + (g)->GCdebt) +/* actual number of total objects allocated */ +#define gettotalobjs(g) ((g)->totalobjs + (g)->GCdebt) + LUAI_FUNC void luaE_setdebt (global_State *g, l_mem debt); LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1); diff --git a/ltests.c b/ltests.c index 1caed04c..57530884 100644 --- a/ltests.c +++ b/ltests.c @@ -1027,6 +1027,16 @@ static int table_query (lua_State *L) { } +static int query_inc (lua_State *L) { + global_State *g = G(L); + lua_pushinteger(L, gettotalobjs(g)); + lua_pushinteger(L, g->GCdebt); + lua_pushinteger(L, getgcparam(g->gcpause)); + lua_pushinteger(L, getgcparam(g->gcstepmul)); + lua_pushinteger(L, cast(l_mem, 1) << g->gcstepsize); + return 5; +} + static int string_query (lua_State *L) { stringtable *tb = &G(L)->strt; int s = cast_int(luaL_optinteger(L, 1, 0)) - 1; @@ -1933,6 +1943,7 @@ static const struct luaL_Reg tests_funcs[] = { {"pushuserdata", pushuserdata}, {"querystr", string_query}, {"querytab", table_query}, + {"queryinc", query_inc}, {"ref", tref}, {"resume", coresume}, {"s2d", s2d}, diff --git a/lua.c b/lua.c index 7f7dc2b2..715430a0 100644 --- a/lua.c +++ b/lua.c @@ -633,7 +633,8 @@ static int pmain (lua_State *L) { } luaL_openlibs(L); /* open standard libraries */ createargtable(L, argv, argc, script); /* create table 'arg' */ - lua_gc(L, LUA_GCGEN, 0, 0); /* GC in generational mode */ + lua_gc(L, LUA_GCRESTART); /* start GC... */ + lua_gc(L, LUA_GCGEN, 0, 0); /* ...in generational mode */ if (!(args & has_E)) { /* no option '-E'? */ if (handle_luainit(L) != LUA_OK) /* run LUA_INIT */ return 0; /* error running LUA_INIT */ @@ -665,6 +666,7 @@ int main (int argc, char **argv) { l_message(argv[0], "cannot create state: not enough memory"); return EXIT_FAILURE; } + lua_gc(L, LUA_GCSTOP); /* stop GC while buidling state */ lua_pushcfunction(L, &pmain); /* to call 'pmain' in protected mode */ lua_pushinteger(L, argc); /* 1st argument */ lua_pushlightuserdata(L, argv); /* 2nd argument */