diff --git a/lapi.c b/lapi.c index 4f4e3021..c493609a 100644 --- a/lapi.c +++ b/lapi.c @@ -53,16 +53,6 @@ const char lua_ident[] = #define isupvalue(i) ((i) < LUA_REGISTRYINDEX) -/* Advance the garbage collector when creating large objects */ -static void advancegc (lua_State *L, size_t delta) { - delta >>= 5; /* one object for each 32 bytes (empirical) */ - if (delta > 0) { - global_State *g = G(L); - luaE_setdebt(g, g->GCdebt - cast(l_obj, delta)); - } -} - - /* ** Convert an acceptable index to a pointer to its respective value. ** Non-valid indices return the special nil value 'G(L)->nilvalue'. @@ -540,7 +530,6 @@ LUA_API const char *lua_pushlstring (lua_State *L, const char *s, size_t len) { ts = (len == 0) ? luaS_new(L, "") : luaS_newlstr(L, s, len); setsvalue2s(L, L->top.p, ts); api_incr_top(L); - advancegc(L, len); luaC_checkGC(L); lua_unlock(L); return getstr(ts); @@ -557,7 +546,6 @@ LUA_API const char *lua_pushextlstring (lua_State *L, setsvalue2s(L, L->top.p, ts); api_incr_top(L); if (falloc != NULL) /* non-static string? */ - advancegc(L, len); /* count its memory */ luaC_checkGC(L); lua_unlock(L); return getstr(ts); @@ -1190,16 +1178,16 @@ 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(g->GCtotalbytes >> 10); + res = cast_int(gettotalbytes(g) >> 10); break; } case LUA_GCCOUNTB: { - res = cast_int(g->GCtotalbytes & 0x3ff); + res = cast_int(gettotalbytes(g) & 0x3ff); break; } case LUA_GCSTEP: { lu_byte oldstp = g->gcstp; - l_obj n = cast(l_obj, va_arg(argp, size_t)); + l_mem n = cast(l_mem, va_arg(argp, size_t)); int work = 0; /* true if GC did some work */ g->gcstp = 0; /* allow GC to run (other bits must be zero here) */ if (n <= 0) @@ -1356,7 +1344,6 @@ LUA_API void *lua_newuserdatauv (lua_State *L, size_t size, int nuvalue) { u = luaS_newudata(L, size, cast(unsigned short, nuvalue)); setuvalue(L, s2v(L->top.p), u); api_incr_top(L); - advancegc(L, size); luaC_checkGC(L); lua_unlock(L); return getudatamem(u); diff --git a/lauxlib.c b/lauxlib.c index b70b7ae6..defd4d57 100644 --- a/lauxlib.c +++ b/lauxlib.c @@ -618,6 +618,7 @@ LUALIB_API void luaL_pushresult (luaL_Buffer *B) { box->bsize = 0; box->box = NULL; lua_pushextlstring(L, s, len, allocf, ud); lua_closeslot(L, -2); /* close the box */ + lua_gc(L, LUA_GCSTEP, len); } lua_remove(L, -2); /* remove box or placeholder from the stack */ } diff --git a/lgc.c b/lgc.c index 92034635..a38d11b2 100644 --- a/lgc.c +++ b/lgc.c @@ -18,7 +18,6 @@ #include "ldo.h" #include "lfunc.h" #include "lgc.h" -#include "llex.h" #include "lmem.h" #include "lobject.h" #include "lstate.h" @@ -27,13 +26,6 @@ #include "ltm.h" -/* -** Number of fixed (luaC_fix) objects in a Lua state: metafield names, -** plus reserved words, plus "_ENV", plus the memory-error message. -*/ -#define NFIXED (TM_N + NUM_RESERVED + 2) - - /* ** Maximum number of elements to sweep in each single step. ** (Large enough to dissipate fixed overheads but small enough @@ -42,6 +34,12 @@ #define GCSWEEPMAX 20 +/* +** Cost (in work units) of running one finalizer. +*/ +#define CWUFIN 10 + + /* mask with all color bits */ #define maskcolors (bitmask(BLACKBIT) | WHITEBITS) @@ -95,7 +93,7 @@ static void reallymarkobject (global_State *g, GCObject *o); -static l_obj atomic (lua_State *L); +static void atomic (lua_State *L); static void entersweep (lua_State *L); @@ -112,6 +110,66 @@ static void entersweep (lua_State *L); #define gnodelast(h) gnode(h, cast_sizet(sizenode(h))) +static size_t objsize (GCObject *o) { + switch (o->tt) { + case LUA_VTABLE: { + /* Fow now, table size does not consider 'haslastfree' */ + Table *t = gco2t(o); + size_t sz = sizeof(Table) + + luaH_realasize(t) * (sizeof(Value) + 1); + if (!isdummy(t)) + sz += sizenode(t) * sizeof(Node); + return sz; + } + case LUA_VLCL: { + LClosure *cl = gco2lcl(o); + return sizeLclosure(cl->nupvalues); + } + case LUA_VCCL: { + CClosure *cl = gco2ccl(o); + return sizeCclosure(cl->nupvalues); + break; + } + case LUA_VUSERDATA: { + Udata *u = gco2u(o); + return sizeudata(u->nuvalue, u->len); + } + case LUA_VPROTO: { + Proto *p = gco2p(o); + size_t sz = sizeof(Proto) + + cast_uint(p->sizep) * sizeof(Proto*) + + cast_uint(p->sizek) * sizeof(TValue) + + cast_uint(p->sizelocvars) * sizeof(LocVar) + + cast_uint(p->sizeupvalues) * sizeof(Upvaldesc); + if (!(p->flag & PF_FIXED)) { + sz += cast_uint(p->sizecode) * sizeof(Instruction) + + cast_uint(p->sizelineinfo) * sizeof(lu_byte) + + cast_uint(p->sizeabslineinfo) * sizeof(AbsLineInfo); + } + return sz; + } + case LUA_VTHREAD: { + lua_State *L1 = gco2th(o); + size_t sz = sizeof(lua_State) + LUA_EXTRASPACE + + cast_uint(L1->nci) * sizeof(CallInfo); + if (L1->stack.p != NULL) + sz += cast_uint(stacksize(L1) + EXTRA_STACK) * sizeof(StackValue); + return sz; + } + case LUA_VSHRSTR: { + TString *ts = gco2ts(o); + return sizestrshr(cast_uint(ts->shrlen)); + } + case LUA_VLNGSTR: { + TString *ts = gco2ts(o); + return luaS_sizelngstr(ts->u.lnglen, ts->shrlen); + } + case LUA_VUPVAL: return sizeof(UpVal); + default: lua_assert(0); return 0; + } +} + + static GCObject **getgclist (GCObject *o) { switch (o->tt) { case LUA_VTABLE: return &gco2t(o)->gclist; @@ -250,7 +308,6 @@ GCObject *luaC_newobjdt (lua_State *L, lu_byte 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->GCdebt--; o->marked = luaC_white(g); o->tt = tt; o->next = g->allgc; @@ -290,7 +347,7 @@ GCObject *luaC_newobj (lua_State *L, lu_byte 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->GCmarked++; + g->GCmarked += cast(l_mem, objsize(o)); switch (o->tt) { case LUA_VSHRSTR: case LUA_VLNGSTR: { @@ -338,14 +395,10 @@ static void markmt (global_State *g) { /* ** mark all objects in list of being-finalized */ -static l_obj markbeingfnz (global_State *g) { +static void markbeingfnz (global_State *g) { GCObject *o; - l_obj count = 0; - for (o = g->tobefnz; o != NULL; o = o->next) { - count++; + for (o = g->tobefnz; o != NULL; o = o->next) markobject(g, o); - } - return count; } @@ -360,8 +413,7 @@ static l_obj 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 l_obj remarkupvals (global_State *g) { - l_obj work = 0; +static void remarkupvals (global_State *g) { lua_State *thread; lua_State **p = &g->twups; while ((thread = *p) != NULL) { @@ -380,9 +432,7 @@ static l_obj remarkupvals (global_State *g) { } } } - work++; } - return work; } @@ -401,7 +451,7 @@ static void cleargraylists (global_State *g) { */ static void restartcollection (global_State *g) { cleargraylists(g); - g->GCmarked = NFIXED; + g->GCmarked = 0; markobject(g, g->mainthread); markvalue(g, &g->l_registry); markmt(g); @@ -546,7 +596,7 @@ static void traversestrongtable (global_State *g, Table *h) { } -static void traversetable (global_State *g, Table *h) { +static l_mem traversetable (global_State *g, Table *h) { const char *weakkey, *weakvalue; const TValue *mode = gfasttm(g, h->metatable, TM_MODE); TString *smode; @@ -565,15 +615,17 @@ static void traversetable (global_State *g, Table *h) { } else /* not weak */ traversestrongtable(g, h); + return 1 + sizenode(h) + h->alimit; } -static void traverseudata (global_State *g, Udata *u) { +static l_mem 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; } @@ -582,7 +634,7 @@ static void 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 void traverseproto (global_State *g, Proto *f) { +static l_mem traverseproto (global_State *g, Proto *f) { int i; markobjectN(g, f->source); for (i = 0; i < f->sizek; i++) /* mark literals */ @@ -593,26 +645,29 @@ static void 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 void traverseCclosure (global_State *g, CClosure *cl) { +static l_mem 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 void traverseLclosure (global_State *g, LClosure *cl) { +static l_mem 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; } @@ -628,13 +683,13 @@ static void 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 void traversethread (global_State *g, lua_State *th) { +static l_mem 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; /* stack not completely built yet */ + return 0; /* 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 */ @@ -652,35 +707,33 @@ static void traversethread (global_State *g, lua_State *th) { g->twups = th; } } + return 1 + (th->top.p - th->stack.p); } /* -** traverse one gray object, turning it to black. +** traverse one gray object, turning it to black. Return an estimate +** of the number of slots traversed. */ -static void propagatemark (global_State *g) { +static l_mem propagatemark (global_State *g) { GCObject *o = g->gray; nw2black(o); g->gray = *getgclist(o); /* remove from 'gray' list */ switch (o->tt) { - 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); + 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; } } -static l_obj propagateall (global_State *g) { - l_obj work = 0; - while (g->gray) { +static void propagateall (global_State *g) { + while (g->gray) propagatemark(g); - work++; - } - return work; } @@ -690,9 +743,8 @@ static l_obj propagateall (global_State *g) { ** inverts the direction of the traversals, trying to speed up ** convergence on chains in the same table. */ -static l_obj convergeephemerons (global_State *g) { +static void convergeephemerons (global_State *g) { int changed; - l_obj work = 0; int dir = 0; do { GCObject *w; @@ -707,11 +759,9 @@ static l_obj 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; } /* }====================================================== */ @@ -727,8 +777,7 @@ static l_obj convergeephemerons (global_State *g) { /* ** clear entries with unmarked keys from all weaktables in list 'l' */ -static l_obj clearbykeys (global_State *g, GCObject *l) { - l_obj work = 0; +static void clearbykeys (global_State *g, GCObject *l) { for (; l; l = gco2t(l)->gclist) { Table *h = gco2t(l); Node *limit = gnodelast(h); @@ -739,9 +788,7 @@ static l_obj clearbykeys (global_State *g, GCObject *l) { if (isempty(gval(n))) /* is entry empty? */ clearkey(n); /* clear its key */ } - work++; } - return work; } @@ -749,8 +796,7 @@ static l_obj clearbykeys (global_State *g, GCObject *l) { ** clear entries with unmarked values from all weaktables in list 'l' up ** to element 'f' */ -static l_obj clearbyvalues (global_State *g, GCObject *l, GCObject *f) { - l_obj work = 0; +static void clearbyvalues (global_State *g, GCObject *l, GCObject *f) { for (; l != f; l = gco2t(l)->gclist) { Table *h = gco2t(l); Node *n, *limit = gnodelast(h); @@ -767,9 +813,7 @@ static l_obj clearbyvalues (global_State *g, GCObject *l, GCObject *f) { if (isempty(gval(n))) /* is entry empty? */ clearkey(n); /* clear its key */ } - work++; } - return work; } @@ -781,7 +825,6 @@ static void freeupval (lua_State *L, UpVal *uv) { static void freeobj (lua_State *L, GCObject *o) { - G(L)->GCtotalobjs--; switch (o->tt) { case LUA_VPROTO: luaF_freeproto(L, gco2p(o)); @@ -835,12 +878,11 @@ static void freeobj (lua_State *L, GCObject *o) { ** for next collection cycle. Return where to continue the traversal or ** NULL if list is finished. */ -static GCObject **sweeplist (lua_State *L, GCObject **p, l_obj countin) { +static GCObject **sweeplist (lua_State *L, GCObject **p, l_mem countin) { global_State *g = G(L); int ow = otherwhite(g); - l_obj i; int white = luaC_white(g); /* current white */ - for (i = 0; *p != NULL && i < countin; i++) { + while (*p != NULL && countin-- > 0) { GCObject *curr = *p; int marked = curr->marked; if (isdeadm(ow, marked)) { /* is 'curr' dead? */ @@ -1052,8 +1094,8 @@ void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) { ** approximately (marked * pause / 100). */ static void setpause (global_State *g) { - l_obj threshold = applygcparam(g, PAUSE, g->GCmarked); - l_obj debt = threshold - gettotalobjs(g); + l_mem threshold = applygcparam(g, PAUSE, g->GCmarked); + l_mem debt = threshold - gettotalbytes(g); if (debt < 0) debt = 0; luaE_setdebt(g, debt); } @@ -1103,7 +1145,7 @@ static void sweep2old (lua_State *L, GCObject **p) { */ static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p, GCObject *limit, GCObject **pfirstold1, - l_obj *paddedold) { + l_mem *paddedold) { static const lu_byte nextage[] = { G_SURVIVAL, /* from G_NEW */ G_OLD1, /* from G_SURVIVAL */ @@ -1113,7 +1155,7 @@ static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p, G_TOUCHED1, /* from G_TOUCHED1 (do not change) */ G_TOUCHED2 /* from G_TOUCHED2 (do not change) */ }; - l_obj addedold = 0; + l_mem addedold = 0; int white = luaC_white(g); GCObject *curr; while ((curr = *p) != limit) { @@ -1132,7 +1174,7 @@ static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p, lua_assert(age != G_OLD1); /* advanced in 'markold' */ setage(curr, nextage[age]); if (getage(curr) == G_OLD1) { - addedold++; /* one more object becoming old */ + addedold += cast(l_mem, objsize(curr)); /* bytes becoming old */ if (*pfirstold1 == NULL) *pfirstold1 = curr; /* first OLD1 object in the list */ } @@ -1257,9 +1299,9 @@ static void minor2inc (lua_State *L, global_State *g, lu_byte kind) { ** than 'minormajor'% of the number of lived objects after the last ** major collection. (That percentage is computed in 'limit'.) */ -static int checkminormajor (global_State *g, l_obj addedold1) { - l_obj step = applygcparam(g, MINORMUL, g->GCmajorminor); - l_obj limit = applygcparam(g, MINORMAJOR, g->GCmajorminor); +static int checkminormajor (global_State *g, l_mem addedold1) { + l_mem step = applygcparam(g, MINORMUL, g->GCmajorminor); + l_mem limit = applygcparam(g, MINORMAJOR, g->GCmajorminor); return (addedold1 >= (step >> 1) || g->GCmarked >= limit); } @@ -1269,8 +1311,8 @@ static int checkminormajor (global_State *g, l_obj addedold1) { ** sweep all lists and advance pointers. Finally, finish the collection. */ static void youngcollection (lua_State *L, global_State *g) { - l_obj addedold1 = 0; - l_obj marked = g->GCmarked; /* preserve 'g->GCmarked' */ + l_mem addedold1 = 0; + l_mem marked = g->GCmarked; /* preserve 'g->GCmarked' */ GCObject **psurvival; /* to point to first non-dead survival object */ GCObject *dummy; /* dummy out parameter to 'sweepgen' */ lua_assert(g->gcstate == GCSpropagate); @@ -1346,7 +1388,9 @@ static void atomic2gen (lua_State *L, global_State *g) { /* ** Set debt for the next minor collection, which will happen when -** total number of objects grows 'genminormul'%. +** total number of bytes grows 'genminormul'% in relation to +** the base, GCmajorminor, which is the number of bytes being used +** after the last major collection. */ static void setminordebt (global_State *g) { luaE_setdebt(g, applygcparam(g, MINORMUL, g->GCmajorminor)); @@ -1404,18 +1448,18 @@ static void fullgen (lua_State *L, global_State *g) { */ static int checkmajorminor (lua_State *L, global_State *g) { if (g->gckind == KGC_GENMAJOR) { /* generational mode? */ - l_obj numobjs = gettotalobjs(g); - l_obj addedobjs = numobjs - g->GCmajorminor; - l_obj limit = applygcparam(g, MAJORMINOR, addedobjs); - l_obj tobecollected = numobjs - g->GCmarked; + l_mem numbytes = gettotalbytes(g); + l_mem addedobjs = numbytes - g->GCmajorminor; + l_mem limit = applygcparam(g, MAJORMINOR, addedobjs); + l_mem tobecollected = numbytes - g->GCmarked; if (tobecollected > limit) { atomic2gen(L, g); /* return to generational mode */ setminordebt(g); - return 0; /* exit incremental collection */ + return 1; /* exit incremental collection */ } } g->GCmajorminor = g->GCmarked; /* prepare for next collection */ - return 1; /* stay doing incremental collections */ + return 0; /* stay doing incremental collections */ } /* }====================================================== */ @@ -1474,8 +1518,7 @@ void luaC_freeallobjects (lua_State *L) { } -static l_obj atomic (lua_State *L) { - l_obj work = 0; +static void atomic (lua_State *L) { global_State *g = G(L); GCObject *origweak, *origall; GCObject *grayagain = g->grayagain; /* save original list */ @@ -1487,33 +1530,32 @@ static l_obj atomic (lua_State *L) { /* registry and global metatables may be changed by API */ markvalue(g, &g->l_registry); markmt(g); /* mark global metatables */ - work += propagateall(g); /* empties 'gray' list */ + propagateall(g); /* empties 'gray' list */ /* remark occasional upvalues of (maybe) dead threads */ - work += remarkupvals(g); - work += propagateall(g); /* propagate changes */ + remarkupvals(g); + propagateall(g); /* propagate changes */ g->gray = grayagain; - work += propagateall(g); /* traverse 'grayagain' list */ - work += convergeephemerons(g); + propagateall(g); /* traverse 'grayagain' list */ + convergeephemerons(g); /* at this point, all strongly accessible objects are marked. */ /* Clear values from weak tables, before checking finalizers */ - work += clearbyvalues(g, g->weak, NULL); - work += clearbyvalues(g, g->allweak, NULL); + clearbyvalues(g, g->weak, NULL); + 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' */ - work += convergeephemerons(g); + markbeingfnz(g); /* mark objects that will be finalized */ + propagateall(g); /* remark, to propagate 'resurrection' */ + convergeephemerons(g); /* at this point, all resurrected objects are marked. */ /* remove dead objects from weak tables */ - work += clearbykeys(g, g->ephemeron); /* clear keys from all ephemeron */ - work += clearbykeys(g, g->allweak); /* clear keys from all 'allweak' */ + clearbykeys(g, g->ephemeron); /* clear keys from all ephemeron */ + clearbykeys(g, g->allweak); /* clear keys from all 'allweak' */ /* clear values from resurrected weak tables */ - work += clearbyvalues(g, g->weak, origweak); - work += clearbyvalues(g, g->allweak, origall); + clearbyvalues(g, g->weak, origweak); + clearbyvalues(g, g->allweak, origall); luaS_clearcache(g); g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */ lua_assert(g->gray == NULL); - return work; } @@ -1524,7 +1566,7 @@ static l_obj atomic (lua_State *L) { static void sweepstep (lua_State *L, global_State *g, lu_byte nextstate, GCObject **nextlist, int fast) { if (g->sweepgc) - g->sweepgc = sweeplist(L, g->sweepgc, fast ? MAX_LOBJ : GCSWEEPMAX); + g->sweepgc = sweeplist(L, g->sweepgc, fast ? MAX_LMEM : GCSWEEPMAX); else { /* enter next state */ g->gcstate = nextstate; g->sweepgc = nextlist; @@ -1544,72 +1586,80 @@ static void sweepstep (lua_State *L, global_State *g, ** That avoids traversing twice some objects, such as threads and ** weak tables. */ -static l_obj singlestep (lua_State *L, int fast) { + +#define step2pause -3 /* finished collection; entered pause state */ +#define atomicstep -2 /* atomic step */ +#define step2minor -1 /* moved to minor collections */ + + +static l_mem singlestep (lua_State *L, int fast) { global_State *g = G(L); - l_obj work; + l_mem stepresult; lua_assert(!g->gcstopem); /* collector is not reentrant */ g->gcstopem = 1; /* no emergency collections while collecting */ switch (g->gcstate) { case GCSpause: { restartcollection(g); g->gcstate = GCSpropagate; - work = 1; + stepresult = 1; break; } case GCSpropagate: { if (fast || g->gray == NULL) { g->gcstate = GCSenteratomic; /* finish propagate phase */ - work = 0; - } - else { - propagatemark(g); /* traverse one gray object */ - work = 1; + stepresult = 1; } + else + stepresult = propagatemark(g); /* traverse one gray object */ break; } case GCSenteratomic: { - work = atomic(L); + atomic(L); if (checkmajorminor(L, g)) + stepresult = step2minor; + else { entersweep(L); + stepresult = atomicstep; + } break; } case GCSswpallgc: { /* sweep "regular" objects */ sweepstep(L, g, GCSswpfinobj, &g->finobj, fast); - work = GCSWEEPMAX; + stepresult = GCSWEEPMAX; break; } case GCSswpfinobj: { /* sweep objects with finalizers */ sweepstep(L, g, GCSswptobefnz, &g->tobefnz, fast); - work = GCSWEEPMAX; + stepresult = GCSWEEPMAX; break; } case GCSswptobefnz: { /* sweep objects to be finalized */ sweepstep(L, g, GCSswpend, NULL, fast); - work = GCSWEEPMAX; + stepresult = GCSWEEPMAX; break; } case GCSswpend: { /* finish sweeps */ checkSizes(L, g); g->gcstate = GCScallfin; - work = 0; + stepresult = GCSWEEPMAX; break; } case GCScallfin: { /* call finalizers */ if (g->tobefnz && !g->gcemergency) { g->gcstopem = 0; /* ok collections during finalizers */ GCTM(L); /* call one finalizer */ - work = 1; + stepresult = CWUFIN; } else { /* emergency mode or no more finalizers */ g->gcstate = GCSpause; /* finish collection */ - work = 0; + stepresult = step2pause; } break; } default: lua_assert(0); return 0; } g->gcstopem = 0; - return work; + return stepresult; } @@ -1635,25 +1685,26 @@ void luaC_runtilstate (lua_State *L, int state, int fast) { ** controls when next step will be performed. */ static void incstep (lua_State *L, global_State *g) { - l_obj stepsize = applygcparam(g, STEPSIZE, 100); - l_obj work2do = applygcparam(g, STEPMUL, stepsize); - int fast = 0; - if (work2do == 0) { /* special case: do a full collection */ - work2do = MAX_LOBJ; /* do unlimited work */ - fast = 1; - } - do { /* repeat until pause or enough work */ - l_obj work = singlestep(L, fast); /* perform one single step */ - if (g->gckind == KGC_GENMINOR) /* returned to minor collections? */ + l_mem stepsize = applygcparam(g, STEPSIZE, 100); + l_mem work2do = applygcparam(g, STEPMUL, stepsize); + l_mem stres; + int fast = (work2do == 0); /* special case: do a full collection */ + do { /* repeat until enough work */ + stres = singlestep(L, fast); /* perform one single step */ + if (stres == step2minor) /* returned to minor collections? */ return; /* nothing else to be done here */ - work2do -= work; - } while (work2do > 0 && g->gcstate != GCSpause); + else if (stres == step2pause || (stres == atomicstep && !fast)) + break; /* end of cycle or atomic */ + else + work2do -= stres; + } while (fast || work2do > 0); if (g->gcstate == GCSpause) setpause(g); /* pause until next cycle */ else luaE_setdebt(g, stepsize); } + /* ** Performs a basic GC step if collector is running. (If collector is ** not running, set a reasonable debt to avoid it being called at @@ -1663,17 +1714,23 @@ void luaC_step (lua_State *L) { global_State *g = G(L); lua_assert(!g->gcemergency); if (!gcrunning(g)) /* not running? */ - luaE_setdebt(g, 2000); + luaE_setdebt(g, 20000); else { +// printf("mem: %ld kind: %s ", gettotalbytes(g), +// g->gckind == KGC_INC ? "inc" : g->gckind == KGC_GENMAJOR ? "genmajor" : +// "genminor"); switch (g->gckind) { case KGC_INC: case KGC_GENMAJOR: +// printf("(%d -> ", g->gcstate); incstep(L, g); +// printf("%d) ", g->gcstate); break; case KGC_GENMINOR: youngcollection(L, g); setminordebt(g); break; } +// printf("-> mem: %ld debt: %ld\n", gettotalbytes(g), g->GCdebt); } } @@ -1692,7 +1749,7 @@ static void fullinc (lua_State *L, global_State *g) { luaC_runtilstate(L, GCSpause, 1); luaC_runtilstate(L, GCScallfin, 1); /* run up to finalizers */ /* 'marked' must be correct after a full GC cycle */ - lua_assert(g->GCmarked == gettotalobjs(g)); + /* lua_assert(g->GCmarked == gettotalobjs(g)); ??? */ luaC_runtilstate(L, GCSpause, 1); /* finish collection */ setpause(g); } diff --git a/lgc.h b/lgc.h index a30755d0..0b16ac7f 100644 --- a/lgc.h +++ b/lgc.h @@ -23,8 +23,9 @@ ** never point to a white one. Moreover, any gray object must be in a ** "gray list" (gray, grayagain, weak, allweak, ephemeron) so that it ** can be visited again before finishing the collection cycle. (Open -** upvalues are an exception to this rule.) These lists have no meaning -** when the invariant is not being enforced (e.g., sweep phase). +** upvalues are an exception to this rule, as they are attached to +** a corresponding thread.) These lists have no meaning when the +** invariant is not being enforced (e.g., sweep phase). */ @@ -48,10 +49,10 @@ /* ** macro to tell when main invariant (white objects cannot point to black -** ones) must be kept. During a collection, the sweep -** phase may break the invariant, as objects turned white may point to -** still-black objects. The invariant is restored when sweep ends and -** all objects are white again. +** ones) must be kept. During a collection, the sweep phase may break +** the invariant, as objects turned white may point to still-black +** objects. The invariant is restored when sweep ends and all objects +** are white again. */ #define keepinvariant(g) ((g)->gcstate <= GCSatomic) @@ -163,34 +164,37 @@ /* ** Minor collections will shift to major ones after LUAI_MINORMAJOR% -** objects become old. +** bytes become old. */ #define LUAI_MINORMAJOR 100 /* ** Major collections will shift to minor ones after a collection -** collects at least LUAI_MAJORMINOR% of the new objects. +** collects at least LUAI_MAJORMINOR% of the new bytes. */ #define LUAI_MAJORMINOR 50 /* ** A young (minor) collection will run after creating LUAI_GENMINORMUL% -** new objects. +** new bytes. */ #define LUAI_GENMINORMUL 25 /* incremental */ -/* Number of objects must be LUAI_GCPAUSE% before starting new cycle */ +/* Number of bytes must be LUAI_GCPAUSE% before starting new cycle */ #define LUAI_GCPAUSE 200 -/* Step multiplier. (Roughly, the collector handles LUAI_GCMUL% objects - for each new allocated object.) */ -#define LUAI_GCMUL 200 +/* +** Step multiplier: The collector handles LUAI_GCMUL% work units for +** each new allocated byte. (Each "work unit" corresponds roughly to +** sweeping or marking one object.) +*/ +#define LUAI_GCMUL 20 /* ??? */ -/* How many objects to allocate before next GC step */ -#define LUAI_GCSTEPSIZE 250 +/* How many bytes to allocate before next GC step */ +#define LUAI_GCSTEPSIZE (250 * sizeof(void*)) #define setgcparam(g,p,v) (g->gcparams[LUA_GCP##p] = luaO_codeparam(v)) diff --git a/llimits.h b/llimits.h index d7ae065b..f189048c 100644 --- a/llimits.h +++ b/llimits.h @@ -16,25 +16,24 @@ /* -** 'lu_mem' is an unsigned integer big enough to count the total memory -** used by Lua (in bytes). 'l_obj' is a signed integer big enough to -** count the total number of objects used by Lua. (It is signed due -** to the use of debt in several computations.) Usually, 'size_t' and -** 'ptrdiff_t' should work, but we use 'long' for 16-bit machines. +** 'l_mem' is a signed integer big enough to count the total memory +** used by Lua. (It is signed due to the use of debt in several +** computations.) Usually, 'ptrdiff_t' should work, but we use 'long' +** for 16-bit machines. */ #if defined(LUAI_MEM) /* { external definitions? */ +typedef LUAI_MEM l_mem; typedef LUAI_UMEM lu_mem; -typedef LUAI_MEM l_obj; #elif LUAI_IS32INT /* }{ */ +typedef ptrdiff_t l_mem; typedef size_t lu_mem; -typedef ptrdiff_t l_obj; #else /* 16-bit ints */ /* }{ */ +typedef long l_mem; typedef unsigned long lu_mem; -typedef long l_obj; #endif /* } */ -#define MAX_LOBJ \ - cast(l_obj, (cast(lu_mem, 1) << (sizeof(l_obj) * CHAR_BIT - 1)) - 1) +#define MAX_LMEM \ + cast(l_mem, (cast(lu_mem, 1) << (sizeof(l_mem) * 8 - 1)) - 1) /* chars used as small naturals (so that 'char' is reserved for characters) */ diff --git a/lmem.c b/lmem.c index f18ea172..de8503d9 100644 --- a/lmem.c +++ b/lmem.c @@ -151,7 +151,7 @@ void luaM_free_ (lua_State *L, void *block, size_t osize) { global_State *g = G(L); lua_assert((osize == 0) == (block == NULL)); callfrealloc(g, block, osize, 0); - g->GCtotalbytes -= osize; + g->GCdebt += cast(l_mem, osize); } @@ -181,10 +181,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 'GCtotalbytes' */ + return NULL; /* do not update 'GCdebt' */ } lua_assert((nsize == 0) == (newblock == NULL)); - g->GCtotalbytes += nsize - osize; + g->GCdebt -= cast(l_mem, nsize) - cast(l_mem, osize); return newblock; } @@ -209,7 +209,7 @@ void *luaM_malloc_ (lua_State *L, size_t size, int tag) { if (newblock == NULL) luaM_error(L); } - g->GCtotalbytes += size; + g->GCdebt -= cast(l_mem, size); return newblock; } } diff --git a/lobject.c b/lobject.c index f7159547..1ca03e76 100644 --- a/lobject.c +++ b/lobject.c @@ -85,7 +85,7 @@ lu_byte luaO_codeparam (unsigned int p) { ** more significant bits, as long as the multiplication does not ** overflow, so we check which order is best. */ -l_obj luaO_applyparam (lu_byte p, l_obj x) { +l_mem luaO_applyparam (lu_byte p, l_mem x) { unsigned int m = p & 0xF; /* mantissa */ int e = (p >> 4); /* exponent */ if (e > 0) { /* normalized? */ @@ -94,19 +94,19 @@ l_obj luaO_applyparam (lu_byte p, l_obj x) { } e -= 7; /* correct excess-7 */ if (e >= 0) { - if (x < (MAX_LOBJ / 0x1F) >> e) /* no overflow? */ + if (x < (MAX_LMEM / 0x1F) >> e) /* no overflow? */ return (x * m) << e; /* order doesn't matter here */ else /* real overflow */ - return MAX_LOBJ; + return MAX_LMEM; } else { /* negative exponent */ e = -e; - if (x < MAX_LOBJ / 0x1F) /* multiplication cannot overflow? */ + if (x < MAX_LMEM / 0x1F) /* multiplication cannot overflow? */ return (x * m) >> e; /* multiplying first gives more precision */ - else if ((x >> e) < MAX_LOBJ / 0x1F) /* cannot overflow after shift? */ + else if ((x >> e) < MAX_LMEM / 0x1F) /* cannot overflow after shift? */ return (x >> e) * m; else /* real overflow */ - return MAX_LOBJ; + return MAX_LMEM; } } diff --git a/lobject.h b/lobject.h index fb66dff7..2411410b 100644 --- a/lobject.h +++ b/lobject.h @@ -838,7 +838,7 @@ typedef struct Table { LUAI_FUNC int luaO_utf8esc (char *buff, unsigned long x); LUAI_FUNC lu_byte luaO_ceillog2 (unsigned int x); LUAI_FUNC lu_byte luaO_codeparam (unsigned int p); -LUAI_FUNC l_obj luaO_applyparam (lu_byte p, l_obj x); +LUAI_FUNC l_mem luaO_applyparam (lu_byte p, l_mem x); LUAI_FUNC int luaO_rawarith (lua_State *L, int op, const TValue *p1, const TValue *p2, TValue *res); diff --git a/lstate.c b/lstate.c index f4c9081d..8e7c8b86 100644 --- a/lstate.c +++ b/lstate.c @@ -77,12 +77,12 @@ typedef struct LG { ** objects (GCtotalobjs - GCdebt) invariant and avoiding overflows in ** 'GCtotalobjs'. */ -void luaE_setdebt (global_State *g, l_obj debt) { - l_obj tb = gettotalobjs(g); +void luaE_setdebt (global_State *g, l_mem debt) { + l_mem tb = gettotalbytes(g); lua_assert(tb > 0); - if (debt > MAX_LOBJ - tb) - debt = MAX_LOBJ - tb; /* will make GCtotalobjs == MAX_LOBJ */ - g->GCtotalobjs = tb + debt; + if (debt > MAX_LMEM - tb) + debt = MAX_LMEM - tb; /* will make GCtotalbytes == MAX_LMEM */ + g->GCtotalbytes = tb + debt; g->GCdebt = debt; } @@ -269,8 +269,7 @@ static void close_state (lua_State *L) { } luaM_freearray(L, G(L)->strt.hash, cast_sizet(G(L)->strt.size)); freestack(L); - lua_assert(g->GCtotalbytes == sizeof(LG)); - lua_assert(gettotalobjs(g) == 1); + lua_assert(gettotalbytes(g) == sizeof(LG)); (*g->frealloc)(g->ud, fromstate(L), sizeof(LG), 0); /* free main block */ } @@ -379,7 +378,6 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud, unsigned seed) { g->weak = g->ephemeron = g->allweak = NULL; g->twups = NULL; g->GCtotalbytes = sizeof(LG); - g->GCtotalobjs = 1; g->GCmarked = 0; g->GCdebt = 0; setivalue(&g->nilvalue, 0); /* to signal that state is not yet built */ diff --git a/lstate.h b/lstate.h index 6aa02889..2a03576d 100644 --- a/lstate.h +++ b/lstate.h @@ -274,11 +274,10 @@ struct CallInfo { typedef struct global_State { lua_Alloc frealloc; /* function to reallocate memory */ void *ud; /* auxiliary data to 'frealloc' */ - lu_mem GCtotalbytes; /* number of bytes currently allocated */ - l_obj GCtotalobjs; /* total number of objects allocated + GCdebt */ - l_obj GCdebt; /* objects counted but not yet allocated */ - l_obj GCmarked; /* number of objects marked in a GC cycle */ - l_obj GCmajorminor; /* auxiliary counter to control major-minor shifts */ + l_mem GCtotalbytes; /* number of bytes currently allocated + debt */ + l_mem GCdebt; /* bytes counted but not yet allocated */ + l_mem GCmarked; /* number of objects marked in a GC cycle */ + l_mem GCmajorminor; /* auxiliary counter to control major-minor shifts */ stringtable strt; /* hash table for strings */ TValue l_registry; TValue nilvalue; /* a nil value */ @@ -411,11 +410,11 @@ union GCUnion { #define obj2gco(v) check_exp((v)->tt >= LUA_TSTRING, &(cast_u(v)->gc)) -/* actual number of total objects allocated */ -#define gettotalobjs(g) ((g)->GCtotalobjs - (g)->GCdebt) +/* actual number of total memory allocated */ +#define gettotalbytes(g) ((g)->GCtotalbytes - (g)->GCdebt) -LUAI_FUNC void luaE_setdebt (global_State *g, l_obj debt); +LUAI_FUNC void luaE_setdebt (global_State *g, l_mem debt); LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1); LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L); LUAI_FUNC void luaE_shrinkCI (lua_State *L); diff --git a/ltests.c b/ltests.c index 7d134e2d..91bce2a1 100644 --- a/ltests.c +++ b/ltests.c @@ -537,7 +537,7 @@ static void checkobject (global_State *g, GCObject *o, int maybedead, } -static l_obj checkgraylist (global_State *g, GCObject *o) { +static l_mem checkgraylist (global_State *g, GCObject *o) { int total = 0; /* count number of elements in the list */ cast_void(g); /* better to keep it if we need to print an object */ while (o) { @@ -566,8 +566,8 @@ static l_obj checkgraylist (global_State *g, GCObject *o) { /* ** Check objects in gray lists. */ -static l_obj checkgrays (global_State *g) { - l_obj total = 0; /* count number of elements in all lists */ +static l_mem checkgrays (global_State *g) { + l_mem total = 0; /* count number of elements in all lists */ if (!keepinvariant(g)) return total; total += checkgraylist(g, g->gray); total += checkgraylist(g, g->grayagain); @@ -583,7 +583,7 @@ static l_obj checkgrays (global_State *g) { ** 'count' and check its TESTBIT. (It must have been previously set by ** 'checkgraylist'.) */ -static void incifingray (global_State *g, GCObject *o, l_obj *count) { +static void incifingray (global_State *g, GCObject *o, l_mem *count) { if (!keepinvariant(g)) return; /* gray lists not being kept in these phases */ if (o->tt == LUA_VUPVAL) { @@ -600,10 +600,10 @@ static void incifingray (global_State *g, GCObject *o, l_obj *count) { } -static l_obj checklist (global_State *g, int maybedead, int tof, +static l_mem checklist (global_State *g, int maybedead, int tof, GCObject *newl, GCObject *survival, GCObject *old, GCObject *reallyold) { GCObject *o; - l_obj total = 0; /* number of object that should be in gray lists */ + l_mem total = 0; /* number of object that should be in gray lists */ for (o = newl; o != survival; o = o->next) { checkobject(g, o, maybedead, G_NEW); incifingray(g, o, &total); @@ -632,8 +632,8 @@ int lua_checkmemory (lua_State *L) { global_State *g = G(L); GCObject *o; int maybedead; - l_obj totalin; /* total of objects that are in gray lists */ - l_obj totalshould; /* total of objects that should be in gray lists */ + l_mem totalin; /* total of objects that are in gray lists */ + l_mem totalshould; /* total of objects that should be in gray lists */ if (keepinvariant(g)) { assert(!iswhite(g->mainthread)); assert(!iswhite(gcvalue(&g->l_registry))); @@ -1040,7 +1040,7 @@ static int table_query (lua_State *L) { static int query_GCparams (lua_State *L) { global_State *g = G(L); - lua_pushinteger(L, cast(lua_Integer, gettotalobjs(g))); + lua_pushinteger(L, cast(lua_Integer, gettotalbytes(g))); lua_pushinteger(L, cast(lua_Integer, g->GCdebt)); lua_pushinteger(L, cast(lua_Integer, applygcparam(g, MINORMUL, 100))); lua_pushinteger(L, cast(lua_Integer, applygcparam(g, MAJORMINOR, 100)));