GC back to controling pace counting bytes

Memory is the resource we want to save. Still to be reviewed again.
This commit is contained in:
Roberto Ierusalimschy 2024-09-19 19:02:14 -03:00
parent b443145ff3
commit ddfa1fbccf
11 changed files with 247 additions and 202 deletions

19
lapi.c
View File

@ -53,16 +53,6 @@ const char lua_ident[] =
#define isupvalue(i) ((i) < LUA_REGISTRYINDEX) #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. ** Convert an acceptable index to a pointer to its respective value.
** Non-valid indices return the special nil value 'G(L)->nilvalue'. ** 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); ts = (len == 0) ? luaS_new(L, "") : luaS_newlstr(L, s, len);
setsvalue2s(L, L->top.p, ts); setsvalue2s(L, L->top.p, ts);
api_incr_top(L); api_incr_top(L);
advancegc(L, len);
luaC_checkGC(L); luaC_checkGC(L);
lua_unlock(L); lua_unlock(L);
return getstr(ts); return getstr(ts);
@ -557,7 +546,6 @@ LUA_API const char *lua_pushextlstring (lua_State *L,
setsvalue2s(L, L->top.p, ts); setsvalue2s(L, L->top.p, ts);
api_incr_top(L); api_incr_top(L);
if (falloc != NULL) /* non-static string? */ if (falloc != NULL) /* non-static string? */
advancegc(L, len); /* count its memory */
luaC_checkGC(L); luaC_checkGC(L);
lua_unlock(L); lua_unlock(L);
return getstr(ts); return getstr(ts);
@ -1190,16 +1178,16 @@ LUA_API int lua_gc (lua_State *L, int what, ...) {
} }
case LUA_GCCOUNT: { case LUA_GCCOUNT: {
/* GC values are expressed in Kbytes: #bytes/2^10 */ /* GC values are expressed in Kbytes: #bytes/2^10 */
res = cast_int(g->GCtotalbytes >> 10); res = cast_int(gettotalbytes(g) >> 10);
break; break;
} }
case LUA_GCCOUNTB: { case LUA_GCCOUNTB: {
res = cast_int(g->GCtotalbytes & 0x3ff); res = cast_int(gettotalbytes(g) & 0x3ff);
break; break;
} }
case LUA_GCSTEP: { case LUA_GCSTEP: {
lu_byte oldstp = g->gcstp; 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 */ int work = 0; /* true if GC did some work */
g->gcstp = 0; /* allow GC to run (other bits must be zero here) */ g->gcstp = 0; /* allow GC to run (other bits must be zero here) */
if (n <= 0) 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)); u = luaS_newudata(L, size, cast(unsigned short, nuvalue));
setuvalue(L, s2v(L->top.p), u); setuvalue(L, s2v(L->top.p), u);
api_incr_top(L); api_incr_top(L);
advancegc(L, size);
luaC_checkGC(L); luaC_checkGC(L);
lua_unlock(L); lua_unlock(L);
return getudatamem(u); return getudatamem(u);

View File

@ -618,6 +618,7 @@ LUALIB_API void luaL_pushresult (luaL_Buffer *B) {
box->bsize = 0; box->box = NULL; box->bsize = 0; box->box = NULL;
lua_pushextlstring(L, s, len, allocf, ud); lua_pushextlstring(L, s, len, allocf, ud);
lua_closeslot(L, -2); /* close the box */ lua_closeslot(L, -2); /* close the box */
lua_gc(L, LUA_GCSTEP, len);
} }
lua_remove(L, -2); /* remove box or placeholder from the stack */ lua_remove(L, -2); /* remove box or placeholder from the stack */
} }

307
lgc.c
View File

@ -18,7 +18,6 @@
#include "ldo.h" #include "ldo.h"
#include "lfunc.h" #include "lfunc.h"
#include "lgc.h" #include "lgc.h"
#include "llex.h"
#include "lmem.h" #include "lmem.h"
#include "lobject.h" #include "lobject.h"
#include "lstate.h" #include "lstate.h"
@ -27,13 +26,6 @@
#include "ltm.h" #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. ** Maximum number of elements to sweep in each single step.
** (Large enough to dissipate fixed overheads but small enough ** (Large enough to dissipate fixed overheads but small enough
@ -42,6 +34,12 @@
#define GCSWEEPMAX 20 #define GCSWEEPMAX 20
/*
** Cost (in work units) of running one finalizer.
*/
#define CWUFIN 10
/* mask with all color bits */ /* mask with all color bits */
#define maskcolors (bitmask(BLACKBIT) | WHITEBITS) #define maskcolors (bitmask(BLACKBIT) | WHITEBITS)
@ -95,7 +93,7 @@
static void reallymarkobject (global_State *g, GCObject *o); 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); 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))) #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) { static GCObject **getgclist (GCObject *o) {
switch (o->tt) { switch (o->tt) {
case LUA_VTABLE: return &gco2t(o)->gclist; 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); global_State *g = G(L);
char *p = cast_charp(luaM_newobject(L, novariant(tt), sz)); char *p = cast_charp(luaM_newobject(L, novariant(tt), sz));
GCObject *o = cast(GCObject *, p + offset); GCObject *o = cast(GCObject *, p + offset);
g->GCdebt--;
o->marked = luaC_white(g); o->marked = luaC_white(g);
o->tt = tt; o->tt = tt;
o->next = g->allgc; 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. ** (only closures can), and a userdata's metatable must be a table.
*/ */
static void reallymarkobject (global_State *g, GCObject *o) { static void reallymarkobject (global_State *g, GCObject *o) {
g->GCmarked++; g->GCmarked += cast(l_mem, objsize(o));
switch (o->tt) { switch (o->tt) {
case LUA_VSHRSTR: case LUA_VSHRSTR:
case LUA_VLNGSTR: { case LUA_VLNGSTR: {
@ -338,14 +395,10 @@ static void markmt (global_State *g) {
/* /*
** mark all objects in list of being-finalized ** mark all objects in list of being-finalized
*/ */
static l_obj markbeingfnz (global_State *g) { static void markbeingfnz (global_State *g) {
GCObject *o; GCObject *o;
l_obj count = 0; for (o = g->tobefnz; o != NULL; o = o->next)
for (o = g->tobefnz; o != NULL; o = o->next) {
count++;
markobject(g, o); 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 ** upvalues, as they have nothing to be checked. (If the thread gets an
** upvalue later, it will be linked in the list again.) ** upvalue later, it will be linked in the list again.)
*/ */
static l_obj remarkupvals (global_State *g) { static void remarkupvals (global_State *g) {
l_obj work = 0;
lua_State *thread; lua_State *thread;
lua_State **p = &g->twups; lua_State **p = &g->twups;
while ((thread = *p) != NULL) { 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) { static void restartcollection (global_State *g) {
cleargraylists(g); cleargraylists(g);
g->GCmarked = NFIXED; g->GCmarked = 0;
markobject(g, g->mainthread); markobject(g, g->mainthread);
markvalue(g, &g->l_registry); markvalue(g, &g->l_registry);
markmt(g); 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 char *weakkey, *weakvalue;
const TValue *mode = gfasttm(g, h->metatable, TM_MODE); const TValue *mode = gfasttm(g, h->metatable, TM_MODE);
TString *smode; TString *smode;
@ -565,15 +615,17 @@ static void traversetable (global_State *g, Table *h) {
} }
else /* not weak */ else /* not weak */
traversestrongtable(g, h); 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; int i;
markobjectN(g, u->metatable); /* mark its metatable */ markobjectN(g, u->metatable); /* mark its metatable */
for (i = 0; i < u->nuvalue; i++) for (i = 0; i < u->nuvalue; i++)
markvalue(g, &u->uv[i].uv); markvalue(g, &u->uv[i].uv);
genlink(g, obj2gco(u)); 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 ** arrays can be larger than needed; the extra slots are filled with
** NULL, so the use of 'markobjectN') ** 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; int i;
markobjectN(g, f->source); markobjectN(g, f->source);
for (i = 0; i < f->sizek; i++) /* mark literals */ 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]); markobjectN(g, f->p[i]);
for (i = 0; i < f->sizelocvars; i++) /* mark local-variable names */ for (i = 0; i < f->sizelocvars; i++) /* mark local-variable names */
markobjectN(g, f->locvars[i].varname); 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; int i;
for (i = 0; i < cl->nupvalues; i++) /* mark its upvalues */ for (i = 0; i < cl->nupvalues; i++) /* mark its upvalues */
markvalue(g, &cl->upvalue[i]); markvalue(g, &cl->upvalue[i]);
return 1 + cl->nupvalues;
} }
/* /*
** Traverse a Lua closure, marking its prototype and its upvalues. ** Traverse a Lua closure, marking its prototype and its upvalues.
** (Both can be NULL while closure is being created.) ** (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; int i;
markobjectN(g, cl->p); /* mark its prototype */ markobjectN(g, cl->p); /* mark its prototype */
for (i = 0; i < cl->nupvalues; i++) { /* visit its upvalues */ for (i = 0; i < cl->nupvalues; i++) { /* visit its upvalues */
UpVal *uv = cl->upvals[i]; UpVal *uv = cl->upvals[i];
markobjectN(g, uv); /* mark upvalue */ 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 ** (which can only happen in generational mode) or if the traverse is in
** the propagate phase (which can only happen in incremental mode). ** 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; UpVal *uv;
StkId o = th->stack.p; StkId o = th->stack.p;
if (isold(th) || g->gcstate == GCSpropagate) if (isold(th) || g->gcstate == GCSpropagate)
linkgclist(th, g->grayagain); /* insert into 'grayagain' list */ linkgclist(th, g->grayagain); /* insert into 'grayagain' list */
if (o == NULL) if (o == NULL)
return; /* stack not completely built yet */ return 0; /* stack not completely built yet */
lua_assert(g->gcstate == GCSatomic || lua_assert(g->gcstate == GCSatomic ||
th->openupval == NULL || isintwups(th)); th->openupval == NULL || isintwups(th));
for (; o < th->top.p; o++) /* mark live elements in the stack */ 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; 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; GCObject *o = g->gray;
nw2black(o); nw2black(o);
g->gray = *getgclist(o); /* remove from 'gray' list */ g->gray = *getgclist(o); /* remove from 'gray' list */
switch (o->tt) { switch (o->tt) {
case LUA_VTABLE: traversetable(g, gco2t(o)); break; case LUA_VTABLE: return traversetable(g, gco2t(o));
case LUA_VUSERDATA: traverseudata(g, gco2u(o)); break; case LUA_VUSERDATA: return traverseudata(g, gco2u(o));
case LUA_VLCL: traverseLclosure(g, gco2lcl(o)); break; case LUA_VLCL: return traverseLclosure(g, gco2lcl(o));
case LUA_VCCL: traverseCclosure(g, gco2ccl(o)); break; case LUA_VCCL: return traverseCclosure(g, gco2ccl(o));
case LUA_VPROTO: traverseproto(g, gco2p(o)); break; case LUA_VPROTO: return traverseproto(g, gco2p(o));
case LUA_VTHREAD: traversethread(g, gco2th(o)); break; case LUA_VTHREAD: return traversethread(g, gco2th(o));
default: lua_assert(0); default: lua_assert(0); return 0;
} }
} }
static l_obj propagateall (global_State *g) { static void propagateall (global_State *g) {
l_obj work = 0; while (g->gray)
while (g->gray) {
propagatemark(g); 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 ** inverts the direction of the traversals, trying to speed up
** convergence on chains in the same table. ** convergence on chains in the same table.
*/ */
static l_obj convergeephemerons (global_State *g) { static void convergeephemerons (global_State *g) {
int changed; int changed;
l_obj work = 0;
int dir = 0; int dir = 0;
do { do {
GCObject *w; GCObject *w;
@ -707,11 +759,9 @@ static l_obj convergeephemerons (global_State *g) {
propagateall(g); /* propagate changes */ propagateall(g); /* propagate changes */
changed = 1; /* will have to revisit all ephemeron tables */ changed = 1; /* will have to revisit all ephemeron tables */
} }
work++;
} }
dir = !dir; /* invert direction next time */ dir = !dir; /* invert direction next time */
} while (changed); /* repeat until no more changes */ } 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' ** clear entries with unmarked keys from all weaktables in list 'l'
*/ */
static l_obj clearbykeys (global_State *g, GCObject *l) { static void clearbykeys (global_State *g, GCObject *l) {
l_obj work = 0;
for (; l; l = gco2t(l)->gclist) { for (; l; l = gco2t(l)->gclist) {
Table *h = gco2t(l); Table *h = gco2t(l);
Node *limit = gnodelast(h); Node *limit = gnodelast(h);
@ -739,9 +788,7 @@ static l_obj clearbykeys (global_State *g, GCObject *l) {
if (isempty(gval(n))) /* is entry empty? */ if (isempty(gval(n))) /* is entry empty? */
clearkey(n); /* clear its key */ 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 ** clear entries with unmarked values from all weaktables in list 'l' up
** to element 'f' ** to element 'f'
*/ */
static l_obj clearbyvalues (global_State *g, GCObject *l, GCObject *f) { static void clearbyvalues (global_State *g, GCObject *l, GCObject *f) {
l_obj work = 0;
for (; l != f; l = gco2t(l)->gclist) { for (; l != f; l = gco2t(l)->gclist) {
Table *h = gco2t(l); Table *h = gco2t(l);
Node *n, *limit = gnodelast(h); 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? */ if (isempty(gval(n))) /* is entry empty? */
clearkey(n); /* clear its key */ 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) { static void freeobj (lua_State *L, GCObject *o) {
G(L)->GCtotalobjs--;
switch (o->tt) { switch (o->tt) {
case LUA_VPROTO: case LUA_VPROTO:
luaF_freeproto(L, gco2p(o)); 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 ** for next collection cycle. Return where to continue the traversal or
** NULL if list is finished. ** 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); global_State *g = G(L);
int ow = otherwhite(g); int ow = otherwhite(g);
l_obj i;
int white = luaC_white(g); /* current white */ int white = luaC_white(g); /* current white */
for (i = 0; *p != NULL && i < countin; i++) { while (*p != NULL && countin-- > 0) {
GCObject *curr = *p; GCObject *curr = *p;
int marked = curr->marked; int marked = curr->marked;
if (isdeadm(ow, marked)) { /* is 'curr' dead? */ 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). ** approximately (marked * pause / 100).
*/ */
static void setpause (global_State *g) { static void setpause (global_State *g) {
l_obj threshold = applygcparam(g, PAUSE, g->GCmarked); l_mem threshold = applygcparam(g, PAUSE, g->GCmarked);
l_obj debt = threshold - gettotalobjs(g); l_mem debt = threshold - gettotalbytes(g);
if (debt < 0) debt = 0; if (debt < 0) debt = 0;
luaE_setdebt(g, debt); 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, static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p,
GCObject *limit, GCObject **pfirstold1, GCObject *limit, GCObject **pfirstold1,
l_obj *paddedold) { l_mem *paddedold) {
static const lu_byte nextage[] = { static const lu_byte nextage[] = {
G_SURVIVAL, /* from G_NEW */ G_SURVIVAL, /* from G_NEW */
G_OLD1, /* from G_SURVIVAL */ 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_TOUCHED1, /* from G_TOUCHED1 (do not change) */
G_TOUCHED2 /* from G_TOUCHED2 (do not change) */ G_TOUCHED2 /* from G_TOUCHED2 (do not change) */
}; };
l_obj addedold = 0; l_mem addedold = 0;
int white = luaC_white(g); int white = luaC_white(g);
GCObject *curr; GCObject *curr;
while ((curr = *p) != limit) { 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' */ lua_assert(age != G_OLD1); /* advanced in 'markold' */
setage(curr, nextage[age]); setage(curr, nextage[age]);
if (getage(curr) == G_OLD1) { if (getage(curr) == G_OLD1) {
addedold++; /* one more object becoming old */ addedold += cast(l_mem, objsize(curr)); /* bytes becoming old */
if (*pfirstold1 == NULL) if (*pfirstold1 == NULL)
*pfirstold1 = curr; /* first OLD1 object in the list */ *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 ** than 'minormajor'% of the number of lived objects after the last
** major collection. (That percentage is computed in 'limit'.) ** major collection. (That percentage is computed in 'limit'.)
*/ */
static int checkminormajor (global_State *g, l_obj addedold1) { static int checkminormajor (global_State *g, l_mem addedold1) {
l_obj step = applygcparam(g, MINORMUL, g->GCmajorminor); l_mem step = applygcparam(g, MINORMUL, g->GCmajorminor);
l_obj limit = applygcparam(g, MINORMAJOR, g->GCmajorminor); l_mem limit = applygcparam(g, MINORMAJOR, g->GCmajorminor);
return (addedold1 >= (step >> 1) || g->GCmarked >= limit); 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. ** sweep all lists and advance pointers. Finally, finish the collection.
*/ */
static void youngcollection (lua_State *L, global_State *g) { static void youngcollection (lua_State *L, global_State *g) {
l_obj addedold1 = 0; l_mem addedold1 = 0;
l_obj marked = g->GCmarked; /* preserve 'g->GCmarked' */ l_mem marked = g->GCmarked; /* preserve 'g->GCmarked' */
GCObject **psurvival; /* to point to first non-dead survival object */ GCObject **psurvival; /* to point to first non-dead survival object */
GCObject *dummy; /* dummy out parameter to 'sweepgen' */ GCObject *dummy; /* dummy out parameter to 'sweepgen' */
lua_assert(g->gcstate == GCSpropagate); 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 ** 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) { static void setminordebt (global_State *g) {
luaE_setdebt(g, applygcparam(g, MINORMUL, g->GCmajorminor)); 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) { static int checkmajorminor (lua_State *L, global_State *g) {
if (g->gckind == KGC_GENMAJOR) { /* generational mode? */ if (g->gckind == KGC_GENMAJOR) { /* generational mode? */
l_obj numobjs = gettotalobjs(g); l_mem numbytes = gettotalbytes(g);
l_obj addedobjs = numobjs - g->GCmajorminor; l_mem addedobjs = numbytes - g->GCmajorminor;
l_obj limit = applygcparam(g, MAJORMINOR, addedobjs); l_mem limit = applygcparam(g, MAJORMINOR, addedobjs);
l_obj tobecollected = numobjs - g->GCmarked; l_mem tobecollected = numbytes - g->GCmarked;
if (tobecollected > limit) { if (tobecollected > limit) {
atomic2gen(L, g); /* return to generational mode */ atomic2gen(L, g); /* return to generational mode */
setminordebt(g); setminordebt(g);
return 0; /* exit incremental collection */ return 1; /* exit incremental collection */
} }
} }
g->GCmajorminor = g->GCmarked; /* prepare for next 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) { static void atomic (lua_State *L) {
l_obj work = 0;
global_State *g = G(L); global_State *g = G(L);
GCObject *origweak, *origall; GCObject *origweak, *origall;
GCObject *grayagain = g->grayagain; /* save original list */ 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 */ /* registry and global metatables may be changed by API */
markvalue(g, &g->l_registry); markvalue(g, &g->l_registry);
markmt(g); /* mark global metatables */ markmt(g); /* mark global metatables */
work += propagateall(g); /* empties 'gray' list */ propagateall(g); /* empties 'gray' list */
/* remark occasional upvalues of (maybe) dead threads */ /* remark occasional upvalues of (maybe) dead threads */
work += remarkupvals(g); remarkupvals(g);
work += propagateall(g); /* propagate changes */ propagateall(g); /* propagate changes */
g->gray = grayagain; g->gray = grayagain;
work += propagateall(g); /* traverse 'grayagain' list */ propagateall(g); /* traverse 'grayagain' list */
work += convergeephemerons(g); convergeephemerons(g);
/* at this point, all strongly accessible objects are marked. */ /* at this point, all strongly accessible objects are marked. */
/* Clear values from weak tables, before checking finalizers */ /* Clear values from weak tables, before checking finalizers */
work += clearbyvalues(g, g->weak, NULL); clearbyvalues(g, g->weak, NULL);
work += clearbyvalues(g, g->allweak, NULL); clearbyvalues(g, g->allweak, NULL);
origweak = g->weak; origall = g->allweak; origweak = g->weak; origall = g->allweak;
separatetobefnz(g, 0); /* separate objects to be finalized */ separatetobefnz(g, 0); /* separate objects to be finalized */
work += markbeingfnz(g); /* mark objects that will be finalized */ markbeingfnz(g); /* mark objects that will be finalized */
work += propagateall(g); /* remark, to propagate 'resurrection' */ propagateall(g); /* remark, to propagate 'resurrection' */
work += convergeephemerons(g); convergeephemerons(g);
/* at this point, all resurrected objects are marked. */ /* at this point, all resurrected objects are marked. */
/* remove dead objects from weak tables */ /* remove dead objects from weak tables */
work += clearbykeys(g, g->ephemeron); /* clear keys from all ephemeron */ clearbykeys(g, g->ephemeron); /* clear keys from all ephemeron */
work += clearbykeys(g, g->allweak); /* clear keys from all 'allweak' */ clearbykeys(g, g->allweak); /* clear keys from all 'allweak' */
/* clear values from resurrected weak tables */ /* clear values from resurrected weak tables */
work += clearbyvalues(g, g->weak, origweak); clearbyvalues(g, g->weak, origweak);
work += clearbyvalues(g, g->allweak, origall); clearbyvalues(g, g->allweak, origall);
luaS_clearcache(g); luaS_clearcache(g);
g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */ g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */
lua_assert(g->gray == NULL); 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, static void sweepstep (lua_State *L, global_State *g,
lu_byte nextstate, GCObject **nextlist, int fast) { lu_byte nextstate, GCObject **nextlist, int fast) {
if (g->sweepgc) 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 */ else { /* enter next state */
g->gcstate = nextstate; g->gcstate = nextstate;
g->sweepgc = nextlist; 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 ** That avoids traversing twice some objects, such as threads and
** weak tables. ** 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); global_State *g = G(L);
l_obj work; l_mem stepresult;
lua_assert(!g->gcstopem); /* collector is not reentrant */ lua_assert(!g->gcstopem); /* collector is not reentrant */
g->gcstopem = 1; /* no emergency collections while collecting */ g->gcstopem = 1; /* no emergency collections while collecting */
switch (g->gcstate) { switch (g->gcstate) {
case GCSpause: { case GCSpause: {
restartcollection(g); restartcollection(g);
g->gcstate = GCSpropagate; g->gcstate = GCSpropagate;
work = 1; stepresult = 1;
break; break;
} }
case GCSpropagate: { case GCSpropagate: {
if (fast || g->gray == NULL) { if (fast || g->gray == NULL) {
g->gcstate = GCSenteratomic; /* finish propagate phase */ g->gcstate = GCSenteratomic; /* finish propagate phase */
work = 0; stepresult = 1;
}
else {
propagatemark(g); /* traverse one gray object */
work = 1;
} }
else
stepresult = propagatemark(g); /* traverse one gray object */
break; break;
} }
case GCSenteratomic: { case GCSenteratomic: {
work = atomic(L); atomic(L);
if (checkmajorminor(L, g)) if (checkmajorminor(L, g))
stepresult = step2minor;
else {
entersweep(L); entersweep(L);
stepresult = atomicstep;
}
break; break;
} }
case GCSswpallgc: { /* sweep "regular" objects */ case GCSswpallgc: { /* sweep "regular" objects */
sweepstep(L, g, GCSswpfinobj, &g->finobj, fast); sweepstep(L, g, GCSswpfinobj, &g->finobj, fast);
work = GCSWEEPMAX; stepresult = GCSWEEPMAX;
break; break;
} }
case GCSswpfinobj: { /* sweep objects with finalizers */ case GCSswpfinobj: { /* sweep objects with finalizers */
sweepstep(L, g, GCSswptobefnz, &g->tobefnz, fast); sweepstep(L, g, GCSswptobefnz, &g->tobefnz, fast);
work = GCSWEEPMAX; stepresult = GCSWEEPMAX;
break; break;
} }
case GCSswptobefnz: { /* sweep objects to be finalized */ case GCSswptobefnz: { /* sweep objects to be finalized */
sweepstep(L, g, GCSswpend, NULL, fast); sweepstep(L, g, GCSswpend, NULL, fast);
work = GCSWEEPMAX; stepresult = GCSWEEPMAX;
break; break;
} }
case GCSswpend: { /* finish sweeps */ case GCSswpend: { /* finish sweeps */
checkSizes(L, g); checkSizes(L, g);
g->gcstate = GCScallfin; g->gcstate = GCScallfin;
work = 0; stepresult = GCSWEEPMAX;
break; break;
} }
case GCScallfin: { /* call finalizers */ case GCScallfin: { /* call finalizers */
if (g->tobefnz && !g->gcemergency) { if (g->tobefnz && !g->gcemergency) {
g->gcstopem = 0; /* ok collections during finalizers */ g->gcstopem = 0; /* ok collections during finalizers */
GCTM(L); /* call one finalizer */ GCTM(L); /* call one finalizer */
work = 1; stepresult = CWUFIN;
} }
else { /* emergency mode or no more finalizers */ else { /* emergency mode or no more finalizers */
g->gcstate = GCSpause; /* finish collection */ g->gcstate = GCSpause; /* finish collection */
work = 0; stepresult = step2pause;
} }
break; break;
} }
default: lua_assert(0); return 0; default: lua_assert(0); return 0;
} }
g->gcstopem = 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. ** controls when next step will be performed.
*/ */
static void incstep (lua_State *L, global_State *g) { static void incstep (lua_State *L, global_State *g) {
l_obj stepsize = applygcparam(g, STEPSIZE, 100); l_mem stepsize = applygcparam(g, STEPSIZE, 100);
l_obj work2do = applygcparam(g, STEPMUL, stepsize); l_mem work2do = applygcparam(g, STEPMUL, stepsize);
int fast = 0; l_mem stres;
if (work2do == 0) { /* special case: do a full collection */ int fast = (work2do == 0); /* special case: do a full collection */
work2do = MAX_LOBJ; /* do unlimited work */ do { /* repeat until enough work */
fast = 1; stres = singlestep(L, fast); /* perform one single step */
} if (stres == step2minor) /* returned to minor collections? */
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? */
return; /* nothing else to be done here */ return; /* nothing else to be done here */
work2do -= work; else if (stres == step2pause || (stres == atomicstep && !fast))
} while (work2do > 0 && g->gcstate != GCSpause); break; /* end of cycle or atomic */
else
work2do -= stres;
} while (fast || work2do > 0);
if (g->gcstate == GCSpause) if (g->gcstate == GCSpause)
setpause(g); /* pause until next cycle */ setpause(g); /* pause until next cycle */
else else
luaE_setdebt(g, stepsize); luaE_setdebt(g, stepsize);
} }
/* /*
** Performs a basic GC step if collector is running. (If collector is ** Performs a basic GC step if collector is running. (If collector is
** not running, set a reasonable debt to avoid it being called at ** 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); global_State *g = G(L);
lua_assert(!g->gcemergency); lua_assert(!g->gcemergency);
if (!gcrunning(g)) /* not running? */ if (!gcrunning(g)) /* not running? */
luaE_setdebt(g, 2000); luaE_setdebt(g, 20000);
else { else {
// printf("mem: %ld kind: %s ", gettotalbytes(g),
// g->gckind == KGC_INC ? "inc" : g->gckind == KGC_GENMAJOR ? "genmajor" :
// "genminor");
switch (g->gckind) { switch (g->gckind) {
case KGC_INC: case KGC_GENMAJOR: case KGC_INC: case KGC_GENMAJOR:
// printf("(%d -> ", g->gcstate);
incstep(L, g); incstep(L, g);
// printf("%d) ", g->gcstate);
break; break;
case KGC_GENMINOR: case KGC_GENMINOR:
youngcollection(L, g); youngcollection(L, g);
setminordebt(g); setminordebt(g);
break; 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, GCSpause, 1);
luaC_runtilstate(L, GCScallfin, 1); /* run up to finalizers */ luaC_runtilstate(L, GCScallfin, 1); /* run up to finalizers */
/* 'marked' must be correct after a full GC cycle */ /* '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 */ luaC_runtilstate(L, GCSpause, 1); /* finish collection */
setpause(g); setpause(g);
} }

34
lgc.h
View File

@ -23,8 +23,9 @@
** never point to a white one. Moreover, any gray object must be in a ** never point to a white one. Moreover, any gray object must be in a
** "gray list" (gray, grayagain, weak, allweak, ephemeron) so that it ** "gray list" (gray, grayagain, weak, allweak, ephemeron) so that it
** can be visited again before finishing the collection cycle. (Open ** can be visited again before finishing the collection cycle. (Open
** upvalues are an exception to this rule.) These lists have no meaning ** upvalues are an exception to this rule, as they are attached to
** when the invariant is not being enforced (e.g., sweep phase). ** 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 ** macro to tell when main invariant (white objects cannot point to black
** ones) must be kept. During a collection, the sweep ** ones) must be kept. During a collection, the sweep phase may break
** phase may break the invariant, as objects turned white may point to ** the invariant, as objects turned white may point to still-black
** still-black objects. The invariant is restored when sweep ends and ** objects. The invariant is restored when sweep ends and all objects
** all objects are white again. ** are white again.
*/ */
#define keepinvariant(g) ((g)->gcstate <= GCSatomic) #define keepinvariant(g) ((g)->gcstate <= GCSatomic)
@ -163,34 +164,37 @@
/* /*
** Minor collections will shift to major ones after LUAI_MINORMAJOR% ** Minor collections will shift to major ones after LUAI_MINORMAJOR%
** objects become old. ** bytes become old.
*/ */
#define LUAI_MINORMAJOR 100 #define LUAI_MINORMAJOR 100
/* /*
** Major collections will shift to minor ones after a collection ** 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 #define LUAI_MAJORMINOR 50
/* /*
** A young (minor) collection will run after creating LUAI_GENMINORMUL% ** A young (minor) collection will run after creating LUAI_GENMINORMUL%
** new objects. ** new bytes.
*/ */
#define LUAI_GENMINORMUL 25 #define LUAI_GENMINORMUL 25
/* incremental */ /* 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 #define LUAI_GCPAUSE 200
/* Step multiplier. (Roughly, the collector handles LUAI_GCMUL% objects /*
for each new allocated object.) */ ** Step multiplier: The collector handles LUAI_GCMUL% work units for
#define LUAI_GCMUL 200 ** 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 */ /* How many bytes to allocate before next GC step */
#define LUAI_GCSTEPSIZE 250 #define LUAI_GCSTEPSIZE (250 * sizeof(void*))
#define setgcparam(g,p,v) (g->gcparams[LUA_GCP##p] = luaO_codeparam(v)) #define setgcparam(g,p,v) (g->gcparams[LUA_GCP##p] = luaO_codeparam(v))

View File

@ -16,25 +16,24 @@
/* /*
** 'lu_mem' is an unsigned integer big enough to count the total memory ** 'l_mem' is a signed integer big enough to count the total memory
** used by Lua (in bytes). 'l_obj' is a signed integer big enough to ** used by Lua. (It is signed due to the use of debt in several
** count the total number of objects used by Lua. (It is signed due ** computations.) Usually, 'ptrdiff_t' should work, but we use 'long'
** to the use of debt in several computations.) Usually, 'size_t' and ** for 16-bit machines.
** 'ptrdiff_t' should work, but we use 'long' for 16-bit machines.
*/ */
#if defined(LUAI_MEM) /* { external definitions? */ #if defined(LUAI_MEM) /* { external definitions? */
typedef LUAI_MEM l_mem;
typedef LUAI_UMEM lu_mem; typedef LUAI_UMEM lu_mem;
typedef LUAI_MEM l_obj;
#elif LUAI_IS32INT /* }{ */ #elif LUAI_IS32INT /* }{ */
typedef ptrdiff_t l_mem;
typedef size_t lu_mem; typedef size_t lu_mem;
typedef ptrdiff_t l_obj;
#else /* 16-bit ints */ /* }{ */ #else /* 16-bit ints */ /* }{ */
typedef long l_mem;
typedef unsigned long lu_mem; typedef unsigned long lu_mem;
typedef long l_obj;
#endif /* } */ #endif /* } */
#define MAX_LOBJ \ #define MAX_LMEM \
cast(l_obj, (cast(lu_mem, 1) << (sizeof(l_obj) * CHAR_BIT - 1)) - 1) 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) */ /* chars used as small naturals (so that 'char' is reserved for characters) */

8
lmem.c
View File

@ -151,7 +151,7 @@ void luaM_free_ (lua_State *L, void *block, size_t osize) {
global_State *g = G(L); global_State *g = G(L);
lua_assert((osize == 0) == (block == NULL)); lua_assert((osize == 0) == (block == NULL));
callfrealloc(g, block, osize, 0); 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)) { if (l_unlikely(newblock == NULL && nsize > 0)) {
newblock = tryagain(L, block, osize, nsize); newblock = tryagain(L, block, osize, nsize);
if (newblock == NULL) /* still no memory? */ if (newblock == NULL) /* still no memory? */
return NULL; /* do not update 'GCtotalbytes' */ return NULL; /* do not update 'GCdebt' */
} }
lua_assert((nsize == 0) == (newblock == NULL)); lua_assert((nsize == 0) == (newblock == NULL));
g->GCtotalbytes += nsize - osize; g->GCdebt -= cast(l_mem, nsize) - cast(l_mem, osize);
return newblock; return newblock;
} }
@ -209,7 +209,7 @@ void *luaM_malloc_ (lua_State *L, size_t size, int tag) {
if (newblock == NULL) if (newblock == NULL)
luaM_error(L); luaM_error(L);
} }
g->GCtotalbytes += size; g->GCdebt -= cast(l_mem, size);
return newblock; return newblock;
} }
} }

View File

@ -85,7 +85,7 @@ lu_byte luaO_codeparam (unsigned int p) {
** more significant bits, as long as the multiplication does not ** more significant bits, as long as the multiplication does not
** overflow, so we check which order is best. ** 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 */ unsigned int m = p & 0xF; /* mantissa */
int e = (p >> 4); /* exponent */ int e = (p >> 4); /* exponent */
if (e > 0) { /* normalized? */ if (e > 0) { /* normalized? */
@ -94,19 +94,19 @@ l_obj luaO_applyparam (lu_byte p, l_obj x) {
} }
e -= 7; /* correct excess-7 */ e -= 7; /* correct excess-7 */
if (e >= 0) { 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 */ return (x * m) << e; /* order doesn't matter here */
else /* real overflow */ else /* real overflow */
return MAX_LOBJ; return MAX_LMEM;
} }
else { /* negative exponent */ else { /* negative exponent */
e = -e; 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 */ 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; return (x >> e) * m;
else /* real overflow */ else /* real overflow */
return MAX_LOBJ; return MAX_LMEM;
} }
} }

View File

@ -838,7 +838,7 @@ typedef struct Table {
LUAI_FUNC int luaO_utf8esc (char *buff, unsigned long x); LUAI_FUNC int luaO_utf8esc (char *buff, unsigned long x);
LUAI_FUNC lu_byte luaO_ceillog2 (unsigned int x); LUAI_FUNC lu_byte luaO_ceillog2 (unsigned int x);
LUAI_FUNC lu_byte luaO_codeparam (unsigned int p); 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, LUAI_FUNC int luaO_rawarith (lua_State *L, int op, const TValue *p1,
const TValue *p2, TValue *res); const TValue *p2, TValue *res);

View File

@ -77,12 +77,12 @@ typedef struct LG {
** objects (GCtotalobjs - GCdebt) invariant and avoiding overflows in ** objects (GCtotalobjs - GCdebt) invariant and avoiding overflows in
** 'GCtotalobjs'. ** 'GCtotalobjs'.
*/ */
void luaE_setdebt (global_State *g, l_obj debt) { void luaE_setdebt (global_State *g, l_mem debt) {
l_obj tb = gettotalobjs(g); l_mem tb = gettotalbytes(g);
lua_assert(tb > 0); lua_assert(tb > 0);
if (debt > MAX_LOBJ - tb) if (debt > MAX_LMEM - tb)
debt = MAX_LOBJ - tb; /* will make GCtotalobjs == MAX_LOBJ */ debt = MAX_LMEM - tb; /* will make GCtotalbytes == MAX_LMEM */
g->GCtotalobjs = tb + debt; g->GCtotalbytes = tb + debt;
g->GCdebt = 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)); luaM_freearray(L, G(L)->strt.hash, cast_sizet(G(L)->strt.size));
freestack(L); freestack(L);
lua_assert(g->GCtotalbytes == sizeof(LG)); lua_assert(gettotalbytes(g) == sizeof(LG));
lua_assert(gettotalobjs(g) == 1);
(*g->frealloc)(g->ud, fromstate(L), sizeof(LG), 0); /* free main block */ (*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->weak = g->ephemeron = g->allweak = NULL;
g->twups = NULL; g->twups = NULL;
g->GCtotalbytes = sizeof(LG); g->GCtotalbytes = sizeof(LG);
g->GCtotalobjs = 1;
g->GCmarked = 0; g->GCmarked = 0;
g->GCdebt = 0; g->GCdebt = 0;
setivalue(&g->nilvalue, 0); /* to signal that state is not yet built */ setivalue(&g->nilvalue, 0); /* to signal that state is not yet built */

View File

@ -274,11 +274,10 @@ struct CallInfo {
typedef struct global_State { typedef struct global_State {
lua_Alloc frealloc; /* function to reallocate memory */ lua_Alloc frealloc; /* function to reallocate memory */
void *ud; /* auxiliary data to 'frealloc' */ void *ud; /* auxiliary data to 'frealloc' */
lu_mem GCtotalbytes; /* number of bytes currently allocated */ l_mem GCtotalbytes; /* number of bytes currently allocated + debt */
l_obj GCtotalobjs; /* total number of objects allocated + GCdebt */ l_mem GCdebt; /* bytes counted but not yet allocated */
l_obj GCdebt; /* objects counted but not yet allocated */ l_mem GCmarked; /* number of objects marked in a GC cycle */
l_obj GCmarked; /* number of objects marked in a GC cycle */ l_mem GCmajorminor; /* auxiliary counter to control major-minor shifts */
l_obj GCmajorminor; /* auxiliary counter to control major-minor shifts */
stringtable strt; /* hash table for strings */ stringtable strt; /* hash table for strings */
TValue l_registry; TValue l_registry;
TValue nilvalue; /* a nil value */ TValue nilvalue; /* a nil value */
@ -411,11 +410,11 @@ union GCUnion {
#define obj2gco(v) check_exp((v)->tt >= LUA_TSTRING, &(cast_u(v)->gc)) #define obj2gco(v) check_exp((v)->tt >= LUA_TSTRING, &(cast_u(v)->gc))
/* actual number of total objects allocated */ /* actual number of total memory allocated */
#define gettotalobjs(g) ((g)->GCtotalobjs - (g)->GCdebt) #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 void luaE_freethread (lua_State *L, lua_State *L1);
LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L); LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L);
LUAI_FUNC void luaE_shrinkCI (lua_State *L); LUAI_FUNC void luaE_shrinkCI (lua_State *L);

View File

@ -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 */ int total = 0; /* count number of elements in the list */
cast_void(g); /* better to keep it if we need to print an object */ cast_void(g); /* better to keep it if we need to print an object */
while (o) { while (o) {
@ -566,8 +566,8 @@ static l_obj checkgraylist (global_State *g, GCObject *o) {
/* /*
** Check objects in gray lists. ** Check objects in gray lists.
*/ */
static l_obj checkgrays (global_State *g) { static l_mem checkgrays (global_State *g) {
l_obj total = 0; /* count number of elements in all lists */ l_mem total = 0; /* count number of elements in all lists */
if (!keepinvariant(g)) return total; if (!keepinvariant(g)) return total;
total += checkgraylist(g, g->gray); total += checkgraylist(g, g->gray);
total += checkgraylist(g, g->grayagain); 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 ** 'count' and check its TESTBIT. (It must have been previously set by
** 'checkgraylist'.) ** '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)) if (!keepinvariant(g))
return; /* gray lists not being kept in these phases */ return; /* gray lists not being kept in these phases */
if (o->tt == LUA_VUPVAL) { 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 *newl, GCObject *survival, GCObject *old, GCObject *reallyold) {
GCObject *o; 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) { for (o = newl; o != survival; o = o->next) {
checkobject(g, o, maybedead, G_NEW); checkobject(g, o, maybedead, G_NEW);
incifingray(g, o, &total); incifingray(g, o, &total);
@ -632,8 +632,8 @@ int lua_checkmemory (lua_State *L) {
global_State *g = G(L); global_State *g = G(L);
GCObject *o; GCObject *o;
int maybedead; int maybedead;
l_obj totalin; /* total of objects that are in gray lists */ l_mem totalin; /* total of objects that are in gray lists */
l_obj totalshould; /* total of objects that should be in gray lists */ l_mem totalshould; /* total of objects that should be in gray lists */
if (keepinvariant(g)) { if (keepinvariant(g)) {
assert(!iswhite(g->mainthread)); assert(!iswhite(g->mainthread));
assert(!iswhite(gcvalue(&g->l_registry))); assert(!iswhite(gcvalue(&g->l_registry)));
@ -1040,7 +1040,7 @@ static int table_query (lua_State *L) {
static int query_GCparams (lua_State *L) { static int query_GCparams (lua_State *L) {
global_State *g = G(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, g->GCdebt));
lua_pushinteger(L, cast(lua_Integer, applygcparam(g, MINORMUL, 100))); lua_pushinteger(L, cast(lua_Integer, applygcparam(g, MINORMUL, 100)));
lua_pushinteger(L, cast(lua_Integer, applygcparam(g, MAJORMINOR, 100))); lua_pushinteger(L, cast(lua_Integer, applygcparam(g, MAJORMINOR, 100)));