From f356d5acdd9d8e8f7e9d1d7632c4657f945ff4f4 Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Wed, 23 Nov 2022 17:17:20 -0300 Subject: First version of GC counting objects for control Still needs to review generational mode. --- lgc.c | 253 ++++++++++++++++++++++++++++++++---------------------------------- 1 file changed, 121 insertions(+), 132 deletions(-) (limited to 'lgc.c') 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); } -- cgit v1.2.3-55-g6feb