rlm@1: /* rlm@1: ** $Id: lgc.c,v 2.38.1.1 2007/12/27 13:02:25 roberto Exp $ rlm@1: ** Garbage Collector rlm@1: ** See Copyright Notice in lua.h rlm@1: */ rlm@1: rlm@1: #include rlm@1: rlm@1: #define lgc_c rlm@1: #define LUA_CORE rlm@1: rlm@1: #include "lua.h" rlm@1: rlm@1: #include "ldebug.h" rlm@1: #include "ldo.h" rlm@1: #include "lfunc.h" rlm@1: #include "lgc.h" rlm@1: #include "lmem.h" rlm@1: #include "lobject.h" rlm@1: #include "lstate.h" rlm@1: #include "lstring.h" rlm@1: #include "ltable.h" rlm@1: #include "ltm.h" rlm@1: rlm@1: rlm@1: #define GCSTEPSIZE 1024u rlm@1: #define GCSWEEPMAX 40 rlm@1: #define GCSWEEPCOST 10 rlm@1: #define GCFINALIZECOST 100 rlm@1: rlm@1: rlm@1: #define maskmarks cast_byte(~(bitmask(BLACKBIT)|WHITEBITS)) rlm@1: rlm@1: #define makewhite(g,x) \ rlm@1: ((x)->gch.marked = cast_byte(((x)->gch.marked & maskmarks) | luaC_white(g))) rlm@1: rlm@1: #define white2gray(x) reset2bits((x)->gch.marked, WHITE0BIT, WHITE1BIT) rlm@1: #define black2gray(x) resetbit((x)->gch.marked, BLACKBIT) rlm@1: rlm@1: #define stringmark(s) reset2bits((s)->tsv.marked, WHITE0BIT, WHITE1BIT) rlm@1: rlm@1: rlm@1: #define isfinalized(u) testbit((u)->marked, FINALIZEDBIT) rlm@1: #define markfinalized(u) l_setbit((u)->marked, FINALIZEDBIT) rlm@1: rlm@1: rlm@1: #define KEYWEAK bitmask(KEYWEAKBIT) rlm@1: #define VALUEWEAK bitmask(VALUEWEAKBIT) rlm@1: rlm@1: rlm@1: rlm@1: #define markvalue(g,o) { checkconsistency(o); \ rlm@1: if (iscollectable(o) && iswhite(gcvalue(o))) reallymarkobject(g,gcvalue(o)); } rlm@1: rlm@1: #define markobject(g,t) { if (iswhite(obj2gco(t))) \ rlm@1: reallymarkobject(g, obj2gco(t)); } rlm@1: rlm@1: rlm@1: #define setthreshold(g) (g->GCthreshold = (g->estimate/100) * g->gcpause) rlm@1: rlm@1: rlm@1: static void removeentry (Node *n) { rlm@1: lua_assert(ttisnil(gval(n))); rlm@1: if (iscollectable(gkey(n))) rlm@1: setttype(gkey(n), LUA_TDEADKEY); /* dead key; remove it */ rlm@1: } rlm@1: rlm@1: rlm@1: static void reallymarkobject (global_State *g, GCObject *o) { rlm@1: lua_assert(iswhite(o) && !isdead(g, o)); rlm@1: white2gray(o); rlm@1: switch (o->gch.tt) { rlm@1: case LUA_TSTRING: { rlm@1: return; rlm@1: } rlm@1: case LUA_TUSERDATA: { rlm@1: Table *mt = gco2u(o)->metatable; rlm@1: gray2black(o); /* udata are never gray */ rlm@1: if (mt) markobject(g, mt); rlm@1: markobject(g, gco2u(o)->env); rlm@1: return; rlm@1: } rlm@1: case LUA_TUPVAL: { rlm@1: UpVal *uv = gco2uv(o); rlm@1: markvalue(g, uv->v); rlm@1: if (uv->v == &uv->u.value) /* closed? */ rlm@1: gray2black(o); /* open upvalues are never black */ rlm@1: return; rlm@1: } rlm@1: case LUA_TFUNCTION: { rlm@1: gco2cl(o)->c.gclist = g->gray; rlm@1: g->gray = o; rlm@1: break; rlm@1: } rlm@1: case LUA_TTABLE: { rlm@1: gco2h(o)->gclist = g->gray; rlm@1: g->gray = o; rlm@1: break; rlm@1: } rlm@1: case LUA_TTHREAD: { rlm@1: gco2th(o)->gclist = g->gray; rlm@1: g->gray = o; rlm@1: break; rlm@1: } rlm@1: case LUA_TPROTO: { rlm@1: gco2p(o)->gclist = g->gray; rlm@1: g->gray = o; rlm@1: break; rlm@1: } rlm@1: default: lua_assert(0); rlm@1: } rlm@1: } rlm@1: rlm@1: rlm@1: static void marktmu (global_State *g) { rlm@1: GCObject *u = g->tmudata; rlm@1: if (u) { rlm@1: do { rlm@1: u = u->gch.next; rlm@1: makewhite(g, u); /* may be marked, if left from previous GC */ rlm@1: reallymarkobject(g, u); rlm@1: } while (u != g->tmudata); rlm@1: } rlm@1: } rlm@1: rlm@1: rlm@1: /* move `dead' udata that need finalization to list `tmudata' */ rlm@1: size_t luaC_separateudata (lua_State *L, int all) { rlm@1: global_State *g = G(L); rlm@1: size_t deadmem = 0; rlm@1: GCObject **p = &g->mainthread->next; rlm@1: GCObject *curr; rlm@1: while ((curr = *p) != NULL) { rlm@1: if (!(iswhite(curr) || all) || isfinalized(gco2u(curr))) rlm@1: p = &curr->gch.next; /* don't bother with them */ rlm@1: else if (fasttm(L, gco2u(curr)->metatable, TM_GC) == NULL) { rlm@1: markfinalized(gco2u(curr)); /* don't need finalization */ rlm@1: p = &curr->gch.next; rlm@1: } rlm@1: else { /* must call its gc method */ rlm@1: deadmem += sizeudata(gco2u(curr)); rlm@1: markfinalized(gco2u(curr)); rlm@1: *p = curr->gch.next; rlm@1: /* link `curr' at the end of `tmudata' list */ rlm@1: if (g->tmudata == NULL) /* list is empty? */ rlm@1: g->tmudata = curr->gch.next = curr; /* creates a circular list */ rlm@1: else { rlm@1: curr->gch.next = g->tmudata->gch.next; rlm@1: g->tmudata->gch.next = curr; rlm@1: g->tmudata = curr; rlm@1: } rlm@1: } rlm@1: } rlm@1: return deadmem; rlm@1: } rlm@1: rlm@1: rlm@1: static int traversetable (global_State *g, Table *h) { rlm@1: int i; rlm@1: int weakkey = 0; rlm@1: int weakvalue = 0; rlm@1: const TValue *mode; rlm@1: if (h->metatable) rlm@1: markobject(g, h->metatable); rlm@1: mode = gfasttm(g, h->metatable, TM_MODE); rlm@1: if (mode && ttisstring(mode)) { /* is there a weak mode? */ rlm@1: weakkey = (strchr(svalue(mode), 'k') != NULL); rlm@1: weakvalue = (strchr(svalue(mode), 'v') != NULL); rlm@1: if (weakkey || weakvalue) { /* is really weak? */ rlm@1: h->marked &= ~(KEYWEAK | VALUEWEAK); /* clear bits */ rlm@1: h->marked |= cast_byte((weakkey << KEYWEAKBIT) | rlm@1: (weakvalue << VALUEWEAKBIT)); rlm@1: h->gclist = g->weak; /* must be cleared after GC, ... */ rlm@1: g->weak = obj2gco(h); /* ... so put in the appropriate list */ rlm@1: } rlm@1: } rlm@1: if (weakkey && weakvalue) return 1; rlm@1: if (!weakvalue) { rlm@1: i = h->sizearray; rlm@1: while (i--) rlm@1: markvalue(g, &h->array[i]); rlm@1: } rlm@1: i = sizenode(h); rlm@1: while (i--) { rlm@1: Node *n = gnode(h, i); rlm@1: lua_assert(ttype(gkey(n)) != LUA_TDEADKEY || ttisnil(gval(n))); rlm@1: if (ttisnil(gval(n))) rlm@1: removeentry(n); /* remove empty entries */ rlm@1: else { rlm@1: lua_assert(!ttisnil(gkey(n))); rlm@1: if (!weakkey) markvalue(g, gkey(n)); rlm@1: if (!weakvalue) markvalue(g, gval(n)); rlm@1: } rlm@1: } rlm@1: return weakkey || weakvalue; rlm@1: } rlm@1: rlm@1: rlm@1: /* rlm@1: ** All marks are conditional because a GC may happen while the rlm@1: ** prototype is still being created rlm@1: */ rlm@1: static void traverseproto (global_State *g, Proto *f) { rlm@1: int i; rlm@1: if (f->source) stringmark(f->source); rlm@1: for (i=0; isizek; i++) /* mark literals */ rlm@1: markvalue(g, &f->k[i]); rlm@1: for (i=0; isizeupvalues; i++) { /* mark upvalue names */ rlm@1: if (f->upvalues[i]) rlm@1: stringmark(f->upvalues[i]); rlm@1: } rlm@1: for (i=0; isizep; i++) { /* mark nested protos */ rlm@1: if (f->p[i]) rlm@1: markobject(g, f->p[i]); rlm@1: } rlm@1: for (i=0; isizelocvars; i++) { /* mark local-variable names */ rlm@1: if (f->locvars[i].varname) rlm@1: stringmark(f->locvars[i].varname); rlm@1: } rlm@1: } rlm@1: rlm@1: rlm@1: rlm@1: static void traverseclosure (global_State *g, Closure *cl) { rlm@1: markobject(g, cl->c.env); rlm@1: if (cl->c.isC) { rlm@1: int i; rlm@1: for (i=0; ic.nupvalues; i++) /* mark its upvalues */ rlm@1: markvalue(g, &cl->c.upvalue[i]); rlm@1: } rlm@1: else { rlm@1: int i; rlm@1: lua_assert(cl->l.nupvalues == cl->l.p->nups); rlm@1: markobject(g, cl->l.p); rlm@1: for (i=0; il.nupvalues; i++) /* mark its upvalues */ rlm@1: markobject(g, cl->l.upvals[i]); rlm@1: } rlm@1: } rlm@1: rlm@1: rlm@1: static void checkstacksizes (lua_State *L, StkId max) { rlm@1: int ci_used = cast_int(L->ci - L->base_ci); /* number of `ci' in use */ rlm@1: int s_used = cast_int(max - L->stack); /* part of stack in use */ rlm@1: if (L->size_ci > LUAI_MAXCALLS) /* handling overflow? */ rlm@1: return; /* do not touch the stacks */ rlm@1: if (4*ci_used < L->size_ci && 2*BASIC_CI_SIZE < L->size_ci) rlm@1: luaD_reallocCI(L, L->size_ci/2); /* still big enough... */ rlm@1: condhardstacktests(luaD_reallocCI(L, ci_used + 1)); rlm@1: if (4*s_used < L->stacksize && rlm@1: 2*(BASIC_STACK_SIZE+EXTRA_STACK) < L->stacksize) rlm@1: luaD_reallocstack(L, L->stacksize/2); /* still big enough... */ rlm@1: condhardstacktests(luaD_reallocstack(L, s_used)); rlm@1: } rlm@1: rlm@1: rlm@1: static void traversestack (global_State *g, lua_State *l) { rlm@1: StkId o, lim; rlm@1: CallInfo *ci; rlm@1: markvalue(g, gt(l)); rlm@1: lim = l->top; rlm@1: for (ci = l->base_ci; ci <= l->ci; ci++) { rlm@1: lua_assert(ci->top <= l->stack_last); rlm@1: if (lim < ci->top) lim = ci->top; rlm@1: } rlm@1: for (o = l->stack; o < l->top; o++) rlm@1: markvalue(g, o); rlm@1: for (; o <= lim; o++) rlm@1: setnilvalue(o); rlm@1: checkstacksizes(l, lim); rlm@1: } rlm@1: rlm@1: rlm@1: /* rlm@1: ** traverse one gray object, turning it to black. rlm@1: ** Returns `quantity' traversed. rlm@1: */ rlm@1: static l_mem propagatemark (global_State *g) { rlm@1: GCObject *o = g->gray; rlm@1: lua_assert(isgray(o)); rlm@1: gray2black(o); rlm@1: switch (o->gch.tt) { rlm@1: case LUA_TTABLE: { rlm@1: Table *h = gco2h(o); rlm@1: g->gray = h->gclist; rlm@1: if (traversetable(g, h)) /* table is weak? */ rlm@1: black2gray(o); /* keep it gray */ rlm@1: return sizeof(Table) + sizeof(TValue) * h->sizearray + rlm@1: sizeof(Node) * sizenode(h); rlm@1: } rlm@1: case LUA_TFUNCTION: { rlm@1: Closure *cl = gco2cl(o); rlm@1: g->gray = cl->c.gclist; rlm@1: traverseclosure(g, cl); rlm@1: return (cl->c.isC) ? sizeCclosure(cl->c.nupvalues) : rlm@1: sizeLclosure(cl->l.nupvalues); rlm@1: } rlm@1: case LUA_TTHREAD: { rlm@1: lua_State *th = gco2th(o); rlm@1: g->gray = th->gclist; rlm@1: th->gclist = g->grayagain; rlm@1: g->grayagain = o; rlm@1: black2gray(o); rlm@1: traversestack(g, th); rlm@1: return sizeof(lua_State) + sizeof(TValue) * th->stacksize + rlm@1: sizeof(CallInfo) * th->size_ci; rlm@1: } rlm@1: case LUA_TPROTO: { rlm@1: Proto *p = gco2p(o); rlm@1: g->gray = p->gclist; rlm@1: traverseproto(g, p); rlm@1: return sizeof(Proto) + sizeof(Instruction) * p->sizecode + rlm@1: sizeof(Proto *) * p->sizep + rlm@1: sizeof(TValue) * p->sizek + rlm@1: sizeof(int) * p->sizelineinfo + rlm@1: sizeof(LocVar) * p->sizelocvars + rlm@1: sizeof(TString *) * p->sizeupvalues; rlm@1: } rlm@1: default: lua_assert(0); return 0; rlm@1: } rlm@1: } rlm@1: rlm@1: rlm@1: static size_t propagateall (global_State *g) { rlm@1: size_t m = 0; rlm@1: while (g->gray) m += propagatemark(g); rlm@1: return m; rlm@1: } rlm@1: rlm@1: rlm@1: /* rlm@1: ** The next function tells whether a key or value can be cleared from rlm@1: ** a weak table. Non-collectable objects are never removed from weak rlm@1: ** tables. Strings behave as `values', so are never removed too. for rlm@1: ** other objects: if really collected, cannot keep them; for userdata rlm@1: ** being finalized, keep them in keys, but not in values rlm@1: */ rlm@1: static int iscleared (const TValue *o, int iskey) { rlm@1: if (!iscollectable(o)) return 0; rlm@1: if (ttisstring(o)) { rlm@1: stringmark(rawtsvalue(o)); /* strings are `values', so are never weak */ rlm@1: return 0; rlm@1: } rlm@1: return iswhite(gcvalue(o)) || rlm@1: (ttisuserdata(o) && (!iskey && isfinalized(uvalue(o)))); rlm@1: } rlm@1: rlm@1: rlm@1: /* rlm@1: ** clear collected entries from weaktables rlm@1: */ rlm@1: static void cleartable (GCObject *l) { rlm@1: while (l) { rlm@1: Table *h = gco2h(l); rlm@1: int i = h->sizearray; rlm@1: lua_assert(testbit(h->marked, VALUEWEAKBIT) || rlm@1: testbit(h->marked, KEYWEAKBIT)); rlm@1: if (testbit(h->marked, VALUEWEAKBIT)) { rlm@1: while (i--) { rlm@1: TValue *o = &h->array[i]; rlm@1: if (iscleared(o, 0)) /* value was collected? */ rlm@1: setnilvalue(o); /* remove value */ rlm@1: } rlm@1: } rlm@1: i = sizenode(h); rlm@1: while (i--) { rlm@1: Node *n = gnode(h, i); rlm@1: if (!ttisnil(gval(n)) && /* non-empty entry? */ rlm@1: (iscleared(key2tval(n), 1) || iscleared(gval(n), 0))) { rlm@1: setnilvalue(gval(n)); /* remove value ... */ rlm@1: removeentry(n); /* remove entry from table */ rlm@1: } rlm@1: } rlm@1: l = h->gclist; rlm@1: } rlm@1: } rlm@1: rlm@1: rlm@1: static void freeobj (lua_State *L, GCObject *o) { rlm@1: switch (o->gch.tt) { rlm@1: case LUA_TPROTO: luaF_freeproto(L, gco2p(o)); break; rlm@1: case LUA_TFUNCTION: luaF_freeclosure(L, gco2cl(o)); break; rlm@1: case LUA_TUPVAL: luaF_freeupval(L, gco2uv(o)); break; rlm@1: case LUA_TTABLE: luaH_free(L, gco2h(o)); break; rlm@1: case LUA_TTHREAD: { rlm@1: lua_assert(gco2th(o) != L && gco2th(o) != G(L)->mainthread); rlm@1: luaE_freethread(L, gco2th(o)); rlm@1: break; rlm@1: } rlm@1: case LUA_TSTRING: { rlm@1: G(L)->strt.nuse--; rlm@1: luaM_freemem(L, o, sizestring(gco2ts(o))); rlm@1: break; rlm@1: } rlm@1: case LUA_TUSERDATA: { rlm@1: luaM_freemem(L, o, sizeudata(gco2u(o))); rlm@1: break; rlm@1: } rlm@1: default: lua_assert(0); rlm@1: } rlm@1: } rlm@1: rlm@1: rlm@1: rlm@1: #define sweepwholelist(L,p) sweeplist(L,p,MAX_LUMEM) rlm@1: rlm@1: rlm@1: static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count) { rlm@1: GCObject *curr; rlm@1: global_State *g = G(L); rlm@1: int deadmask = otherwhite(g); rlm@1: while ((curr = *p) != NULL && count-- > 0) { rlm@1: if (curr->gch.tt == LUA_TTHREAD) /* sweep open upvalues of each thread */ rlm@1: sweepwholelist(L, &gco2th(curr)->openupval); rlm@1: if ((curr->gch.marked ^ WHITEBITS) & deadmask) { /* not dead? */ rlm@1: lua_assert(!isdead(g, curr) || testbit(curr->gch.marked, FIXEDBIT)); rlm@1: makewhite(g, curr); /* make it white (for next cycle) */ rlm@1: p = &curr->gch.next; rlm@1: } rlm@1: else { /* must erase `curr' */ rlm@1: lua_assert(isdead(g, curr) || deadmask == bitmask(SFIXEDBIT)); rlm@1: *p = curr->gch.next; rlm@1: if (curr == g->rootgc) /* is the first element of the list? */ rlm@1: g->rootgc = curr->gch.next; /* adjust first */ rlm@1: freeobj(L, curr); rlm@1: } rlm@1: } rlm@1: return p; rlm@1: } rlm@1: rlm@1: rlm@1: static void checkSizes (lua_State *L) { rlm@1: global_State *g = G(L); rlm@1: /* check size of string hash */ rlm@1: if (g->strt.nuse < cast(lu_int32, g->strt.size/4) && rlm@1: g->strt.size > MINSTRTABSIZE*2) rlm@1: luaS_resize(L, g->strt.size/2); /* table is too big */ rlm@1: /* check size of buffer */ rlm@1: if (luaZ_sizebuffer(&g->buff) > LUA_MINBUFFER*2) { /* buffer too big? */ rlm@1: size_t newsize = luaZ_sizebuffer(&g->buff) / 2; rlm@1: luaZ_resizebuffer(L, &g->buff, newsize); rlm@1: } rlm@1: } rlm@1: rlm@1: rlm@1: static void GCTM (lua_State *L) { rlm@1: global_State *g = G(L); rlm@1: GCObject *o = g->tmudata->gch.next; /* get first element */ rlm@1: Udata *udata = rawgco2u(o); rlm@1: const TValue *tm; rlm@1: /* remove udata from `tmudata' */ rlm@1: if (o == g->tmudata) /* last element? */ rlm@1: g->tmudata = NULL; rlm@1: else rlm@1: g->tmudata->gch.next = udata->uv.next; rlm@1: udata->uv.next = g->mainthread->next; /* return it to `root' list */ rlm@1: g->mainthread->next = o; rlm@1: makewhite(g, o); rlm@1: tm = fasttm(L, udata->uv.metatable, TM_GC); rlm@1: if (tm != NULL) { rlm@1: lu_byte oldah = L->allowhook; rlm@1: lu_mem oldt = g->GCthreshold; rlm@1: L->allowhook = 0; /* stop debug hooks during GC tag method */ rlm@1: g->GCthreshold = 2*g->totalbytes; /* avoid GC steps */ rlm@1: setobj2s(L, L->top, tm); rlm@1: setuvalue(L, L->top+1, udata); rlm@1: L->top += 2; rlm@1: luaD_call(L, L->top - 2, 0); rlm@1: L->allowhook = oldah; /* restore hooks */ rlm@1: g->GCthreshold = oldt; /* restore threshold */ rlm@1: } rlm@1: } rlm@1: rlm@1: rlm@1: /* rlm@1: ** Call all GC tag methods rlm@1: */ rlm@1: void luaC_callGCTM (lua_State *L) { rlm@1: while (G(L)->tmudata) rlm@1: GCTM(L); rlm@1: } rlm@1: rlm@1: rlm@1: void luaC_freeall (lua_State *L) { rlm@1: global_State *g = G(L); rlm@1: int i; rlm@1: g->currentwhite = WHITEBITS | bitmask(SFIXEDBIT); /* mask to collect all elements */ rlm@1: sweepwholelist(L, &g->rootgc); rlm@1: for (i = 0; i < g->strt.size; i++) /* free all string lists */ rlm@1: sweepwholelist(L, &g->strt.hash[i]); rlm@1: } rlm@1: rlm@1: rlm@1: static void markmt (global_State *g) { rlm@1: int i; rlm@1: for (i=0; imt[i]) markobject(g, g->mt[i]); rlm@1: } rlm@1: rlm@1: rlm@1: /* mark root set */ rlm@1: static void markroot (lua_State *L) { rlm@1: global_State *g = G(L); rlm@1: g->gray = NULL; rlm@1: g->grayagain = NULL; rlm@1: g->weak = NULL; rlm@1: markobject(g, g->mainthread); rlm@1: /* make global table be traversed before main stack */ rlm@1: markvalue(g, gt(g->mainthread)); rlm@1: markvalue(g, registry(L)); rlm@1: markmt(g); rlm@1: g->gcstate = GCSpropagate; rlm@1: } rlm@1: rlm@1: rlm@1: static void remarkupvals (global_State *g) { rlm@1: UpVal *uv; rlm@1: for (uv = g->uvhead.u.l.next; uv != &g->uvhead; uv = uv->u.l.next) { rlm@1: lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv); rlm@1: if (isgray(obj2gco(uv))) rlm@1: markvalue(g, uv->v); rlm@1: } rlm@1: } rlm@1: rlm@1: rlm@1: static void atomic (lua_State *L) { rlm@1: global_State *g = G(L); rlm@1: size_t udsize; /* total size of userdata to be finalized */ rlm@1: /* remark occasional upvalues of (maybe) dead threads */ rlm@1: remarkupvals(g); rlm@1: /* traverse objects cautch by write barrier and by 'remarkupvals' */ rlm@1: propagateall(g); rlm@1: /* remark weak tables */ rlm@1: g->gray = g->weak; rlm@1: g->weak = NULL; rlm@1: lua_assert(!iswhite(obj2gco(g->mainthread))); rlm@1: markobject(g, L); /* mark running thread */ rlm@1: markmt(g); /* mark basic metatables (again) */ rlm@1: propagateall(g); rlm@1: /* remark gray again */ rlm@1: g->gray = g->grayagain; rlm@1: g->grayagain = NULL; rlm@1: propagateall(g); rlm@1: udsize = luaC_separateudata(L, 0); /* separate userdata to be finalized */ rlm@1: marktmu(g); /* mark `preserved' userdata */ rlm@1: udsize += propagateall(g); /* remark, to propagate `preserveness' */ rlm@1: cleartable(g->weak); /* remove collected objects from weak tables */ rlm@1: /* flip current white */ rlm@1: g->currentwhite = cast_byte(otherwhite(g)); rlm@1: g->sweepstrgc = 0; rlm@1: g->sweepgc = &g->rootgc; rlm@1: g->gcstate = GCSsweepstring; rlm@1: g->estimate = g->totalbytes - udsize; /* first estimate */ rlm@1: } rlm@1: rlm@1: rlm@1: static l_mem singlestep (lua_State *L) { rlm@1: global_State *g = G(L); rlm@1: /*lua_checkmemory(L);*/ rlm@1: switch (g->gcstate) { rlm@1: case GCSpause: { rlm@1: markroot(L); /* start a new collection */ rlm@1: return 0; rlm@1: } rlm@1: case GCSpropagate: { rlm@1: if (g->gray) rlm@1: return propagatemark(g); rlm@1: else { /* no more `gray' objects */ rlm@1: atomic(L); /* finish mark phase */ rlm@1: return 0; rlm@1: } rlm@1: } rlm@1: case GCSsweepstring: { rlm@1: lu_mem old = g->totalbytes; rlm@1: sweepwholelist(L, &g->strt.hash[g->sweepstrgc++]); rlm@1: if (g->sweepstrgc >= g->strt.size) /* nothing more to sweep? */ rlm@1: g->gcstate = GCSsweep; /* end sweep-string phase */ rlm@1: lua_assert(old >= g->totalbytes); rlm@1: g->estimate -= old - g->totalbytes; rlm@1: return GCSWEEPCOST; rlm@1: } rlm@1: case GCSsweep: { rlm@1: lu_mem old = g->totalbytes; rlm@1: g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX); rlm@1: if (*g->sweepgc == NULL) { /* nothing more to sweep? */ rlm@1: checkSizes(L); rlm@1: g->gcstate = GCSfinalize; /* end sweep phase */ rlm@1: } rlm@1: lua_assert(old >= g->totalbytes); rlm@1: g->estimate -= old - g->totalbytes; rlm@1: return GCSWEEPMAX*GCSWEEPCOST; rlm@1: } rlm@1: case GCSfinalize: { rlm@1: if (g->tmudata) { rlm@1: GCTM(L); rlm@1: if (g->estimate > GCFINALIZECOST) rlm@1: g->estimate -= GCFINALIZECOST; rlm@1: return GCFINALIZECOST; rlm@1: } rlm@1: else { rlm@1: g->gcstate = GCSpause; /* end collection */ rlm@1: g->gcdept = 0; rlm@1: return 0; rlm@1: } rlm@1: } rlm@1: default: lua_assert(0); return 0; rlm@1: } rlm@1: } rlm@1: rlm@1: rlm@1: void luaC_step (lua_State *L) { rlm@1: global_State *g = G(L); rlm@1: l_mem lim = (GCSTEPSIZE/100) * g->gcstepmul; rlm@1: if (lim == 0) rlm@1: lim = (MAX_LUMEM-1)/2; /* no limit */ rlm@1: g->gcdept += g->totalbytes - g->GCthreshold; rlm@1: do { rlm@1: lim -= singlestep(L); rlm@1: if (g->gcstate == GCSpause) rlm@1: break; rlm@1: } while (lim > 0); rlm@1: if (g->gcstate != GCSpause) { rlm@1: if (g->gcdept < GCSTEPSIZE) rlm@1: g->GCthreshold = g->totalbytes + GCSTEPSIZE; /* - lim/g->gcstepmul;*/ rlm@1: else { rlm@1: g->gcdept -= GCSTEPSIZE; rlm@1: g->GCthreshold = g->totalbytes; rlm@1: } rlm@1: } rlm@1: else { rlm@1: lua_assert(g->totalbytes >= g->estimate); rlm@1: setthreshold(g); rlm@1: } rlm@1: } rlm@1: rlm@1: rlm@1: void luaC_fullgc (lua_State *L) { rlm@1: global_State *g = G(L); rlm@1: if (g->gcstate <= GCSpropagate) { rlm@1: /* reset sweep marks to sweep all elements (returning them to white) */ rlm@1: g->sweepstrgc = 0; rlm@1: g->sweepgc = &g->rootgc; rlm@1: /* reset other collector lists */ rlm@1: g->gray = NULL; rlm@1: g->grayagain = NULL; rlm@1: g->weak = NULL; rlm@1: g->gcstate = GCSsweepstring; rlm@1: } rlm@1: lua_assert(g->gcstate != GCSpause && g->gcstate != GCSpropagate); rlm@1: /* finish any pending sweep phase */ rlm@1: while (g->gcstate != GCSfinalize) { rlm@1: lua_assert(g->gcstate == GCSsweepstring || g->gcstate == GCSsweep); rlm@1: singlestep(L); rlm@1: } rlm@1: markroot(L); rlm@1: while (g->gcstate != GCSpause) { rlm@1: singlestep(L); rlm@1: } rlm@1: setthreshold(g); rlm@1: } rlm@1: rlm@1: rlm@1: void luaC_barrierf (lua_State *L, GCObject *o, GCObject *v) { rlm@1: global_State *g = G(L); rlm@1: lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o)); rlm@1: lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause); rlm@1: lua_assert(ttype(&o->gch) != LUA_TTABLE); rlm@1: /* must keep invariant? */ rlm@1: if (g->gcstate == GCSpropagate) rlm@1: reallymarkobject(g, v); /* restore invariant */ rlm@1: else /* don't mind */ rlm@1: makewhite(g, o); /* mark as white just to avoid other barriers */ rlm@1: } rlm@1: rlm@1: rlm@1: void luaC_barrierback (lua_State *L, Table *t) { rlm@1: global_State *g = G(L); rlm@1: GCObject *o = obj2gco(t); rlm@1: lua_assert(isblack(o) && !isdead(g, o)); rlm@1: lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause); rlm@1: black2gray(o); /* make table gray (again) */ rlm@1: t->gclist = g->grayagain; rlm@1: g->grayagain = o; rlm@1: } rlm@1: rlm@1: rlm@1: void luaC_link (lua_State *L, GCObject *o, lu_byte tt) { rlm@1: global_State *g = G(L); rlm@1: o->gch.next = g->rootgc; rlm@1: g->rootgc = o; rlm@1: o->gch.marked = luaC_white(g); rlm@1: o->gch.tt = tt; rlm@1: } rlm@1: rlm@1: rlm@1: void luaC_linkupval (lua_State *L, UpVal *uv) { rlm@1: global_State *g = G(L); rlm@1: GCObject *o = obj2gco(uv); rlm@1: o->gch.next = g->rootgc; /* link upvalue into `rootgc' list */ rlm@1: g->rootgc = o; rlm@1: if (isgray(o)) { rlm@1: if (g->gcstate == GCSpropagate) { rlm@1: gray2black(o); /* closed upvalues need barrier */ rlm@1: luaC_barrier(L, uv, uv->v); rlm@1: } rlm@1: else { /* sweep phase: sweep it (turning it into white) */ rlm@1: makewhite(g, o); rlm@1: lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause); rlm@1: } rlm@1: } rlm@1: } rlm@1: