/* ** mark all objects in list of being-finalized */ static void markbeingfnz (global_State *g) { GCObject *o; for (o = g->tobefnz; o != NULL; o = gch(o)->next) { makewhite(g, o); reallymarkobject(g, o); } }
static void GCTM (lua_State *L) { global_State *g = G(L); GCObject *o = g->tmudata->gch.next; /* get first element */ Udata *udata = rawgco2u(o); const TValue *tm; /* remove udata from `tmudata' */ if (o == g->tmudata) /* last element? */ g->tmudata = NULL; else g->tmudata->gch.next = udata->uv.next; udata->uv.next = g->mainthread->next; /* return it to `root' list */ g->mainthread->next = o; makewhite(g, o); tm = fasttm(L, udata->uv.metatable, TM_GC); if (tm != NULL) { lu_byte oldah = L->allowhook; lu_mem oldt = g->GCthreshold; L->allowhook = 0; /* stop debug hooks during GC tag method */ g->GCthreshold = 2*g->totalbytes; /* avoid GC steps */ setobj2s(L, L->top, tm); setuvalue(L, L->top+1, udata); L->top += 2; luaD_call(L, L->top - 2, 0); L->allowhook = oldah; /* restore hooks */ g->GCthreshold = oldt; /* restore threshold */ } }
/* ** if object 'o' has a finalizer, remove it from 'allgc' list (must ** search the list to find it) and link it in 'finobj' list. */ void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) { global_State *g = G(L); if (testbit(gch(o)->marked, SEPARATED) || /* obj. is already separated... */ isfinalized(o) || /* ... or is finalized... */ gfasttm(g, mt, TM_GC) == NULL) /* or has no finalizer? */ return; /* nothing to be done */ else { /* move 'o' to 'finobj' list */ GCObject **p; GCheader *ho = gch(o); if (g->sweepgc == &ho->next) { /* avoid removing current sweep object */ lua_assert(issweepphase(g)); g->sweepgc = sweeptolive(L, g->sweepgc, NULL); } /* search for pointer pointing to 'o' */ for (p = &g->allgc; *p != o; p = &gch(*p)->next) { /* empty */ } *p = ho->next; /* remove 'o' from root list */ ho->next = g->finobj; /* link it in list 'finobj' */ g->finobj = o; l_setbit(ho->marked, SEPARATED); /* mark it as such */ if (!keepinvariantout(g)) /* not keeping invariant? */ makewhite(g, o); /* "sweep" object */ else resetoldbit(o); /* see MOVE OLD rule */ } }
static lu_mem singlestep (lua_State *L) { global_State *g = G(L); switch (g->gcstate) { case GCSpause: { g->GCmemtrav = g->strt.size * sizeof(GCObject*); restartcollection(g); g->gcstate = GCSpropagate; return g->GCmemtrav; } case GCSpropagate: { g->GCmemtrav = 0; lua_assert(g->gray); propagatemark(g); if (g->gray == NULL) /* no more gray objects? */ g->gcstate = GCSatomic; /* finish propagate phase */ return g->GCmemtrav; /* memory traversed in this step */ } case GCSatomic: { lu_mem work; int sw; propagateall(g); /* make sure gray list is empty */ work = atomic(L); /* work is what was traversed by 'atomic' */ sw = entersweep(L); g->GCestimate = gettotalbytes(g); /* first estimate */; return work + sw * GCSWEEPCOST; } case GCSswpallgc: { /* sweep "regular" objects */ return sweepstep(L, g, GCSswpfinobj, &g->finobj); } case GCSswpfinobj: { /* sweep objects with finalizers */ return sweepstep(L, g, GCSswptobefnz, &g->tobefnz); } case GCSswptobefnz: { /* sweep objects to be finalized */ return sweepstep(L, g, GCSswpend, NULL); } case GCSswpend: { /* finish sweeps */ makewhite(g, g->mainthread); /* sweep main thread */ checkSizes(L, g); g->gcstate = GCScallfin; return 0; } case GCScallfin: { /* call remaining finalizers */ if (g->tobefnz && g->gckind != KGC_EMERGENCY) { int n = runafewfinalizers(L); return (n * GCFINALIZECOST); } else { /* emergency mode or no more finalizers */ g->gcstate = GCSpause; /* finish collection */ return 0; } } default: lua_assert(0); return 0; } }
static void marktmu (global_State *g) { GCObject *u = g->tmudata; if (u) { do { u = u->gch.next; makewhite(g, u); /* may be marked, if left from previous GC */ reallymarkobject(g, u); } while (u != g->tmudata); } }
/* ** barrier that moves collector forward, that is, mark the white object ** being pointed by a black object. (If in sweep phase, clear the black ** object to white [sweep it] to avoid other barrier calls for this ** same object.) */ void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) { global_State *g = G(L); lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o)); if (keepinvariant(g)) /* must keep invariant? */ reallymarkobject(g, v); /* restore invariant */ else { /* sweep phase */ lua_assert(issweepphase(g)); makewhite(g, o); /* mark main obj. as white to avoid other barriers */ } }
void luaC_barrierf (lua_State *L, GCObject *o, GCObject *v) { global_State *g = G(L); lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o)); lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause); lua_assert(ttype(&o->gch) != LUA_TTABLE); /* must keep invariant? */ if (g->gcstate == GCSpropagate) reallymarkobject(g, v); /* restore invariant */ else /* don't mind */ makewhite(g, o); /* mark as white just to avoid other barriers */ }
static GCObject *udata2finalize (global_State *g) { GCObject *o = g->tobefnz; /* get first element */ lua_assert(tofinalize(o)); g->tobefnz = o->next; /* remove it from 'tobefnz' list */ o->next = g->allgc; /* return it to 'allgc' list */ g->allgc = o; resetbit(o->marked, FINALIZEDBIT); /* object is "normal" again */ if (issweepphase(g)) makewhite(g, o); /* "sweep" object */ return o; }
void GCTM (lua_State *L) { #else static void GCTM (lua_State *L) { #endif /* LUA_REFCOUNT */ global_State *g = G(L); GCObject *o = g->tmudata->gch.next; /* get first element */ Udata *udata = rawgco2u(o); const TValue *tm; /* remove udata from `tmudata' */ if (o == g->tmudata) /* last element? */ g->tmudata = NULL; else g->tmudata->gch.next = udata->uv.next; #if LUA_REFCOUNT udata->uv.prev = (GCObject*)&G(L)->mainthread->next; #endif /* LUA_REFCOUNT */ udata->uv.next = g->mainthread->next; /* return it to `root' list */ #if LUA_REFCOUNT if (udata->uv.next) udata->uv.next->uv.prev = obj2gco(udata); #endif /* LUA_REFCOUNT */ g->mainthread->next = o; makewhite(g, o); tm = fasttm(L, udata->uv.metatable, TM_GC); if (tm != NULL) { lu_byte oldah = L->allowhook; lu_mem oldt = g->GCthreshold; L->allowhook = 0; /* stop debug hooks during GC tag method */ g->GCthreshold = 2*g->totalbytes; /* avoid GC steps */ #if LUA_REFCOUNT /* We could be in the middle of a stack set, so call the __gc metamethod on one stack level past the current one. */ L->top++; /* we're freeing this object, but fake ref count it, because GCTM actually uses it */ udata->uv.ref++; L->top += 2; setobj2s(L, L->top - 2, tm); setuvalue(L, L->top - 1, udata); luaD_call(L, L->top - 2, 0); udata->uv.ref--; L->top--; setnilvalue2n(L, L->top - 1); #else setobj2s(L, L->top, tm); setuvalue(L, L->top+1, udata); L->top += 2; luaD_call(L, L->top - 2, 0); #endif /* LUA_REFCOUNT */ L->allowhook = oldah; /* restore hooks */ g->GCthreshold = oldt; /* restore threshold */ } }
static GCObject *udata2finalize (global_State *g) { GCObject *o = g->tobefnz; /* get first element */ lua_assert(isfinalized(o)); g->tobefnz = gch(o)->next; /* remove it from 'tobefnz' list */ gch(o)->next = g->allgc; /* return it to 'allgc' list */ g->allgc = o; resetbit(gch(o)->marked, SEPARATED); /* mark that it is not in 'tobefnz' */ lua_assert(!isold(o)); /* see MOVE OLD rule */ if (!keepinvariantout(g)) /* not keeping invariant? */ makewhite(g, o); /* "sweep" object */ return o; }
/* ** barrier that moves collector forward, that is, mark the white object ** being pointed by a black object. */ void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) { global_State *g = G(L); lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o)); lua_assert(isgenerational(g) || g->gcstate != GCSpause); lua_assert(gch(o)->tt != LUA_TTABLE); if (keepinvariant(g)) /* must keep invariant? */ reallymarkobject(g, v); /* restore invariant */ else { /* sweep phase */ lua_assert(issweepphase(g)); makewhite(g, o); /* mark main obj. as white to avoid other barriers */ } }
/* ** check color (and invariants) for an upvalue that was closed, ** i.e., moved into the 'allgc' list */ void luaC_checkupvalcolor (global_State *g, UpVal *uv) { GCObject *o = obj2gco(uv); lua_assert(!isblack(o)); /* open upvalues are never black */ if (isgray(o)) { if (keepinvariant(g)) { resetoldbit(o); /* see MOVE OLD rule */ gray2black(o); /* it is being visited now */ markvalue(g, uv->v); } else { lua_assert(issweepphase(g)); makewhite(g, o); } } }
void luaC_linkupval(lua_State *L, UpVal *uv) { global_State *g = G(L); GCObject *o = obj2gco(uv); o->gch.next = g->rootgc; /* link upvalue into `rootgc' list */ g->rootgc = o; if (isgray(o)) { if (g->gcstate == GCSpropagate) { gray2black(o); /* closed upvalues need barrier */ luaC_barrier(L, uv, uv->v); } else { /* sweep phase: sweep it (turning it into white) */ makewhite(g, o); lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause); } } }
/* ** if object 'o' has a finalizer, remove it from 'allgc' list (must ** search the list to find it) and link it in 'finobj' list. */ void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) { global_State *g = G(L); if (tofinalize(o) || /* obj. is already marked... */ gfasttm(g, mt, TM_GC) == NULL) /* or has no finalizer? */ return; /* nothing to be done */ else { /* move 'o' to 'finobj' list */ GCObject **p; if (issweepphase(g)) { makewhite(g, o); /* "sweep" object 'o' */ if (g->sweepgc == &o->next) /* should not remove 'sweepgc' object */ g->sweepgc = sweeptolive(L, g->sweepgc, NULL); /* change 'sweepgc' */ } /* search for pointer pointing to 'o' */ for (p = &g->allgc; *p != o; p = &(*p)->next) { /* empty */ } *p = o->next; /* remove 'o' from 'allgc' list */ o->next = g->finobj; /* link it in 'finobj' list */ g->finobj = o; l_setbit(o->marked, FINALIZEDBIT); /* mark it as such */ } }
/* Free a C data object. */ void LJ_FASTCALL lj_cdata_free(global_State *g, GCcdata *cd) { if (LJ_UNLIKELY(cd->marked & LJ_GC_CDATA_FIN)) { GCobj *root; makewhite(g, obj2gco(cd)); markfinalized(obj2gco(cd)); if ((root = gcref(g->gc.mmudata)) != NULL) { setgcrefr(cd->nextgc, root->gch.nextgc); setgcref(root->gch.nextgc, obj2gco(cd)); setgcref(g->gc.mmudata, obj2gco(cd)); } else { setgcref(cd->nextgc, obj2gco(cd)); setgcref(g->gc.mmudata, obj2gco(cd)); } } else if (LJ_LIKELY(!cdataisv(cd))) { CType *ct = ctype_raw(ctype_ctsG(g), cd->ctypeid); CTSize sz = ctype_hassize(ct->info) ? ct->size : CTSIZE_PTR; lua_assert(ctype_hassize(ct->info) || ctype_isfunc(ct->info) || ctype_isextern(ct->info)); lj_mem_free(g, cd, sizeof(GCcdata) + sz); } else { lj_mem_free(g, memcdatav(cd), sizecdatav(cd)); } }
static void cleartable (lua_State *L, GCObject *l) { #else static void cleartable (GCObject *l) { #endif /* LUA_REFCOUNT */ while (l) { Table *h = gco2h(l); int i = h->sizearray; lua_assert(testbit(h->marked, VALUEWEAKBIT) || testbit(h->marked, KEYWEAKBIT)); if (testbit(h->marked, VALUEWEAKBIT)) { while (i--) { TValue *o = &h->array[i]; #if LUA_REFCOUNT if (iscleared(o, 0)) { /* value was collected? */ if (iscollectable(o)) o->value.gc->gch.ref--; setnilvalue2n(l, o); /* remove value */ } #else if (iscleared(o, 0)) /* value was collected? */ setnilvalue(o); /* remove value */ #endif /* LUA_REFCOUNT */ } } i = sizenode(h); while (i--) { Node *n = gnode(h, i); if (!ttisnil(gval(n)) && /* non-empty entry? */ (iscleared(key2tval(n), 1) || iscleared(gval(n), 0))) { #if LUA_REFCOUNT if (iscollectable(gval(n))) gval(n)->value.gc->gch.ref--; setnilvalue2n(L, gval(n)); /* remove value ... */ #else setnilvalue(gval(n)); /* remove value ... */ #endif /* LUA_REFCOUNT */ removeentry(n); /* remove entry from table */ } } l = h->gclist; } } static void freeobj (lua_State *L, GCObject *o) { switch (o->gch.tt) { case LUA_TPROTO: luaF_freeproto(L, gco2p(o)); break; case LUA_TFUNCTION: luaF_freeclosure(L, gco2cl(o)); break; case LUA_TUPVAL: luaF_freeupval(L, gco2uv(o)); break; case LUA_TTABLE: luaH_free(L, gco2h(o)); break; case LUA_TTHREAD: { lua_assert(gco2th(o) != L && gco2th(o) != G(L)->mainthread); luaE_freethread(L, gco2th(o)); break; } case LUA_TSTRING: { G(L)->strt.nuse--; luaM_freemem(L, o, sizestring(gco2ts(o))); break; } #if LUA_WIDESTRING case LUA_TWSTRING: { G(L)->strt.nuse--; luaM_freemem(L, o, sizestring(gco2ts(o))); break; } #endif /* LUA_WIDESTRING */ case LUA_TUSERDATA: { luaM_freemem(L, o, sizeudata(gco2u(o))); break; } default: lua_assert(0); } } #define sweepwholelist(L,p) sweeplist(L,p,MAX_LUMEM) static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count) { GCObject *curr; global_State *g = G(L); int deadmask = otherwhite(g); while ((curr = *p) != NULL && count-- > 0) { if (curr->gch.tt == LUA_TTHREAD) /* sweep open upvalues of each thread */ sweepwholelist(L, &gco2th(curr)->openupval); if ((curr->gch.marked ^ WHITEBITS) & deadmask) { /* not dead? */ lua_assert(!isdead(g, curr) || testbit(curr->gch.marked, FIXEDBIT)); makewhite(g, curr); /* make it white (for next cycle) */ p = &curr->gch.next; } else { /* must erase `curr' */ lua_assert(isdead(g, curr) || deadmask == bitmask(SFIXEDBIT)); #if LUA_REFCOUNT if (curr->gch.prev) curr->gch.prev->gch.next = curr->gch.next; if (curr->gch.next) curr->gch.next->gch.prev = (GCObject*)p; #endif /* LUA_REFCOUNT */ *p = curr->gch.next; if (curr == g->rootgc) /* is the first element of the list? */ g->rootgc = curr->gch.next; /* adjust first */ freeobj(L, curr); } } return p; }