/* ** mark root set and reset all gray lists, to start a new ** incremental (or full) collection */ static void markroot (global_State *g) { g->gray = g->grayagain = NULL; g->weak = g->allweak = g->ephemeron = NULL; markobject(g, g->mainthread); markvalue(g, &g->l_registry); markmt(g); markbeingfnz(g); /* mark any finalizing object left from previous cycle */ }
static void remarkupvals(global_State *g) { UpVal *uv; for (uv = g->uvhead.u.l.next; uv != &g->uvhead; uv = uv->u.l.next) { lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv); if (isgray(obj2gco(uv))) markvalue(g, uv->v); } }
/* mark root set */ static void markroot (GCState *st, lua_State *L) { global_State *g = st->g; markobject(st, defaultmeta(L)); markobject(st, registry(L)); traversestack(st, g->mainthread); if (L != g->mainthread) /* another thread is running? */ markvalue(st, L); /* cannot collect it */ }
static void traversestack (global_State *g, lua_State *l) { StkId o, lim; CallInfo *ci; markvalue(g, gt(l)); lim = l->top; if(l->stack == NULL) return; /* no stack to traverse */ for (ci = l->base_ci; ci <= l->ci; ci++) { lua_assert(ci->top <= l->stack_last); if (lim < ci->top) lim = ci->top; } for (o = l->stack; o < l->top; o++) markvalue(g, o); for (; o <= lim; o++) setnilvalue(o); if (!isfixedstack(l)) /* if stack size is fixed, can't resize it. */ checkstacksizes(l, lim); }
static int traversetable(global_State *g, Table *h) { int i; int weakkey = 0; int weakvalue = 0; const TValue *mode; if (h->metatable) markobject(g, h->metatable); mode = gfasttm(g, h->metatable, TM_MODE); if (mode && ttisstring(mode)) { /* is there a weak mode? */ weakkey = (strchr(svalue(mode), 'k') != NULL); weakvalue = (strchr(svalue(mode), 'v') != NULL); if (weakkey || weakvalue) { /* is really weak? */ h->marked &= ~(KEYWEAK | VALUEWEAK); /* clear bits */ h->marked |= cast_byte((weakkey << KEYWEAKBIT) | (weakvalue << VALUEWEAKBIT)); h->gclist = g->weak; /* must be cleared after GC, ... */ g->weak = obj2gco(h); /* ... so put in the appropriate list */ } } if (weakkey && weakvalue) return 1; if (!weakvalue) { i = h->sizearray; while (i--) markvalue(g, &h->array[i]); } i = sizenode(h); while (i--) { Node *n = gnode(h, i); lua_assert(ttype(gkey(n)) != LUA_TDEADKEY || ttisnil(gval(n))); if (ttisnil(gval(n))) removeentry(n); /* remove empty entries */ else { lua_assert(!ttisnil(gkey(n))); if (!weakkey) markvalue(g, gkey(n)); if (!weakvalue) markvalue(g, gval(n)); } } return weakkey || weakvalue; }
static void traverseclosure (GCState *st, Closure *cl) { if (cl->c.isC) { int i; for (i=0; i<cl->c.nupvalues; i++) /* mark its upvalues */ markobject(st, &cl->c.upvalue[i]); } else { int i; lua_assert(cl->l.nupvalues == cl->l.p->nups); markvalue(st, hvalue(&cl->l.g)); markvalue(st, cl->l.p); for (i=0; i<cl->l.nupvalues; i++) { /* mark its upvalues */ UpVal *u = cl->l.upvals[i]; if (!u->marked) { markobject(st, &u->value); u->marked = 1; } } } }
/* ** Traverse a prototype. (While a prototype is being build, its ** arrays can be larger than needed; the extra slots are filled with ** NULL, so the use of 'markobjectN') */ static int traverseproto (global_State *g, Proto *f) { int i; if (f->cache && iswhite(f->cache)) f->cache = NULL; /* allow cache to be collected */ for (i = 0; i < f->sp->sizek; i++) /* mark literals */ markvalue(g, &f->k[i]); for (i = 0; i < f->sp->sizep; i++) /* mark nested protos */ markobjectN(g, f->p[i]); return sizeof(Proto) + sizeof(Proto *) * f->sp->sizep + sizeof(TValue) * f->sp->sizek + marksharedproto(g, f->sp); }
/* ** mark an object. Userdata, strings, and closed upvalues are visited ** and turned black here. Other objects are marked gray and added ** to appropriate list to be visited (and turned black) later. (Open ** upvalues are already linked in 'headuv' list.) */ static void reallymarkobject (global_State *g, GCObject *o) { lu_mem size; white2gray(o); switch (gch(o)->tt) { case LUA_TSHRSTR: case LUA_TLNGSTR: { size = sizestring(gco2ts(o)); break; /* nothing else to mark; make it black */ } case LUA_TUSERDATA: { Table *mt = gco2u(o)->metatable; markobject(g, mt); markobject(g, gco2u(o)->env); size = sizeudata(gco2u(o)); break; } case LUA_TUPVAL: { UpVal *uv = gco2uv(o); markvalue(g, uv->v); if (uv->v != &uv->u.value) /* open? */ return; /* open upvalues remain gray */ size = sizeof(UpVal); break; } case LUA_TLCL: { gco2lcl(o)->gclist = g->gray; g->gray = o; return; } case LUA_TCCL: { gco2ccl(o)->gclist = g->gray; g->gray = o; return; } case LUA_TTABLE: { linktable(gco2t(o), &g->gray); return; } case LUA_TTHREAD: { gco2th(o)->gclist = g->gray; g->gray = o; return; } case LUA_TPROTO: { gco2p(o)->gclist = g->gray; g->gray = o; return; } default: lua_assert(0); return; } gray2black(o); g->GCmemtrav += size; }
void reallymarkobject (global_State *g, GCObject *o) { #else static void reallymarkobject (global_State *g, GCObject *o) { #endif /* LUAPLUS_EXTENSIONS */ lua_assert(iswhite(o) && !isdead(g, o)); white2gray(o); switch (o->gch.tt) { case LUA_TSTRING: { return; } #if LUA_WIDESTRING case LUA_TWSTRING: { return; } #endif /* LUA_WIDESTRING */ case LUA_TUSERDATA: { Table *mt = gco2u(o)->metatable; gray2black(o); /* udata are never gray */ if (mt) markobject(g, mt); markobject(g, gco2u(o)->env); return; } case LUA_TUPVAL: { UpVal *uv = gco2uv(o); markvalue(g, uv->v); if (uv->v == &uv->u.value) /* closed? */ gray2black(o); /* open upvalues are never black */ return; } case LUA_TFUNCTION: { gco2cl(o)->c.gclist = g->gray; g->gray = o; break; } case LUA_TTABLE: { gco2h(o)->gclist = g->gray; g->gray = o; break; } case LUA_TTHREAD: { gco2th(o)->gclist = g->gray; g->gray = o; break; } case LUA_TPROTO: { gco2p(o)->gclist = g->gray; g->gray = o; break; } default: lua_assert(0); } }
static lu_mem traversestack (global_State *g, lua_State *th) { StkId o = th->stack; if (o == NULL) return 1; /* stack not completely built yet */ for (; o < th->top; o++) markvalue(g, o); if (g->gcstate == GCSatomic) { /* final traversal? */ StkId lim = th->stack + th->stacksize; /* real end of stack */ for (; o < lim; o++) /* clear not-marked stack slice */ setnilvalue(o); } return sizeof(lua_State) + sizeof(TValue) * th->stacksize; }
static int traversestack (global_State *g, lua_State *L) { StkId o = L->stack; if (o == NULL) return 1; /* stack not completely built yet */ for (; o < L->top; o++) markvalue(g, o); if (g->gcstate == GCSatomic) { /* final traversal? */ StkId lim = L->stack + L->stacksize; /* real end of stack */ for (; o < lim; o++) /* clear not-marked stack slice */ setnilvalue(o); } return TRAVCOST + cast_int(o - L->stack); }
/* ** open upvalues point to values in a thread, so those values should ** be marked when the thread is traversed except in the atomic phase ** (because then the value cannot be changed by the thread and the ** thread may not be traversed again) */ static lu_mem traverseLclosure (global_State *g, LClosure *cl) { int i; markobjectN(g, cl->p); /* mark its prototype */ for (i = 0; i < cl->nupvalues; i++) { /* mark its upvalues */ UpVal *uv = cl->upvals[i]; if (uv != NULL) { if (upisopen(uv) && g->gcstate != GCSinsideatomic) uv->u.open.touched = 1; /* can be marked in 'remarkupvals' */ else markvalue(g, uv->v); } } return sizeLclosure(cl->nupvalues); }
static void traversestack (global_State *g, lua_State *l) { StkId o, lim; CallInfo *ci; markvalue(g, gt(l)); lim = l->top; for (ci = l->base_ci; ci <= l->ci; ci++) { lua_assert(ci->top <= l->stack_last); if (lim < ci->top) lim = ci->top; } for (o = l->stack; o < l->top; o++) markvalue(g, o); #if LUA_REFCOUNT for (; o <= lim; o++) { if (iscollectable(o)) o->value.gc->gch.ref--; setnilvalue2n(l, o); } #else for (; o <= lim; o++) setnilvalue(o); #endif /* LUA_REFCOUNT */ checkstacksizes(l, lim); }
static void traverseclosure(global_State *g, Closure *cl) { markobject(g, cl->c.env); if (cl->c.isC) { int i; for (i = 0; i < cl->c.nupvalues; i++) /* mark its upvalues */ markvalue(g, &cl->c.upvalue[i]); } else { int i; lua_assert(cl->l.nupvalues == cl->l.p->nups); markobject(g, cl->l.p); for (i = 0; i < cl->l.nupvalues; i++) /* mark its upvalues */ markobject(g, cl->l.upvals[i]); } }
static int traverseproto (global_State *g, Proto *f) { int i; if (f->cache && iswhite(obj2gco(f->cache))) f->cache = NULL; /* allow cache to be collected */ stringmark(f->source); for (i = 0; i < f->sizek; i++) /* mark literals */ markvalue(g, &f->k[i]); for (i = 0; i < f->sizeupvalues; i++) /* mark upvalue names */ stringmark(f->upvalues[i].name); for (i = 0; i < f->sizep; i++) /* mark nested protos */ markobject(g, f->p[i]); for (i = 0; i < f->sizelocvars; i++) /* mark local-variable names */ stringmark(f->locvars[i].varname); return TRAVCOST + f->sizek + f->sizeupvalues + f->sizep + f->sizelocvars; }
static void traverseproto (GCState *st, Proto *f) { int i; stringmark(f->source); for (i=0; i<f->sizek; i++) { /* mark literal strings */ if (ttisstring(f->k+i) || ttiswstring(f->k+i)) stringmark(tsvalue(f->k+i)); } for (i=0; i<f->sizeupvalues; i++) /* mark upvalue names */ stringmark(f->upvalues[i]); for (i=0; i<f->sizep; i++) /* mark nested protos */ markvalue(st, f->p[i]); for (i=0; i<f->sizelocvars; i++) /* mark local-variable names */ stringmark(f->locvars[i].varname); lua_assert(luaG_checkcode(f)); }
/* ** 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); } } }
static int traverseclosure (global_State *g, Closure *cl) { if (cl->c.isC) { int i; for (i=0; i<cl->c.nupvalues; i++) /* mark its upvalues */ markvalue(g, &cl->c.upvalue[i]); } else { int i; lua_assert(cl->l.nupvalues == cl->l.p->sizeupvalues); markobject(g, cl->l.p); /* mark its prototype */ for (i=0; i<cl->l.nupvalues; i++) /* mark its upvalues */ markobject(g, cl->l.upvals[i]); } return TRAVCOST + cl->c.nupvalues; }
void LuaState::LuaPlusGCFunction(void* s) { lua_State* L = (lua_State*)s; LuaState* state = (LuaState*)lua_getstateuserdata(L); if (!state) return; global_State* g = G(L); LuaObject* curObj = state->GetHeadObject()->m_next; while (curObj != (LuaObject*)state->GetTailObject()) { markvalue(g, curObj->GetTObject()); curObj = ((MiniLuaObject*)curObj)->m_next; } }
void LuaPlusGCFunction(void* s) { lua_State* L = (lua_State*)s; LuaPlus::LuaState* state = lua_State_To_LuaState(L); if (!state) return; global_State* g = G(L); LuaPlus::LuaObject* curObj = (LuaPlus::LuaObject*)G(L)->gchead_next; while (curObj != (LuaPlus::LuaObject*)&G(L)->gctail_next) { markvalue(g, curObj->GetTObject()); curObj = *(LuaPlus::LuaObject**)curObj; } }
static l_mem atomic (lua_State *L) { global_State *g = G(L); l_mem work; GCObject *origweak, *origall; GCObject *grayagain = g->grayagain; /* save original list */ lua_assert(g->ephemeron == NULL && g->weak == NULL); lua_assert(!iswhite(g->mainthread)); g->gcstate = GCSinsideatomic; g->GCmemtrav = 0; /* start counting work */ markobject(g, L); /* mark running thread */ /* registry and global metatables may be changed by API */ markvalue(g, &g->l_registry); markmt(g); /* mark global metatables */ /* remark occasional upvalues of (maybe) dead threads */ remarkupvals(g); propagateall(g); /* propagate changes */ work = g->GCmemtrav; /* stop counting (do not recount 'grayagain') */ g->gray = grayagain; propagateall(g); /* traverse 'grayagain' list */ g->GCmemtrav = 0; /* restart counting */ convergeephemerons(g); /* at this point, all strongly accessible objects are marked. */ /* Clear values from weak tables, before checking finalizers */ clearvalues(g, g->weak, NULL); clearvalues(g, g->allweak, NULL); origweak = g->weak; origall = g->allweak; work += g->GCmemtrav; /* stop counting (objects being finalized) */ separatetobefnz(g, 0); /* separate objects to be finalized */ g->gcfinnum = 1; /* there may be objects to be finalized */ markbeingfnz(g); /* mark objects that will be finalized */ propagateall(g); /* remark, to propagate 'resurrection' */ g->GCmemtrav = 0; /* restart counting */ convergeephemerons(g); /* at this point, all resurrected objects are marked. */ /* remove dead objects from weak tables */ clearkeys(g, g->ephemeron, NULL); /* clear keys from all ephemeron tables */ clearkeys(g, g->allweak, NULL); /* clear keys from all 'allweak' tables */ /* clear values from resurrected weak tables */ clearvalues(g, g->weak, origweak); clearvalues(g, g->allweak, origall); luaS_clearcache(g); g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */ work += g->GCmemtrav; /* complete counting */ return work; /* estimate of memory marked by 'atomic' */ }
/* ** All marks are conditional because a GC may happen while the ** prototype is still being created */ static void traverseproto (global_State *g, Proto *f) { int i; if (f->source) stringmark(f->source); for (i=0; i<f->sizek; i++) /* mark literals */ markvalue(g, &f->k[i]); for (i=0; i<f->sizeupvalues; i++) { /* mark upvalue names */ if (f->upvalues[i]) stringmark(f->upvalues[i]); } for (i=0; i<f->sizep; i++) { /* mark nested protos */ if (f->p[i]) markobject(g, f->p[i]); } for (i=0; i<f->sizelocvars; i++) { /* mark local-variable names */ if (f->locvars[i].varname) stringmark(f->locvars[i].varname); } }
static void reallymarkobject (global_State *g, GCObject *o) { lua_assert(iswhite(o) && !isdead(g, o)); white2gray(o);// 先将白变灰 switch (o->gch.tt) {// 根据不同类型进行不同的操作 case LUA_TSTRING: { return; // 不通过gc管理 } case LUA_TUSERDATA: { Table *mt = gco2u(o)->metatable; gray2black(o); /* udata are never gray */ if (mt) markobject(g, mt); markobject(g, gco2u(o)->env); return; } case LUA_TUPVAL: { UpVal *uv = gco2uv(o); markvalue(g, uv->v); if (uv->v == &uv->u.value) /* closed? */ gray2black(o); /* open upvalues are never black */ return; } case LUA_TFUNCTION: { gco2cl(o)->c.gclist = g->gray; g->gray = o; break; } case LUA_TTABLE: { gco2h(o)->gclist = g->gray; g->gray = o; break; } case LUA_TTHREAD: { gco2th(o)->gclist = g->gray; g->gray = o; break; } case LUA_TPROTO: { gco2p(o)->gclist = g->gray; g->gray = o; break; } default: lua_assert(0); } }
/* ** mark an object. Userdata and closed upvalues are visited and turned ** black here. Strings remain gray (it is the same as making them ** black). Other objects are marked gray and added to appropriate list ** to be visited (and turned black) later. (Open upvalues are already ** linked in 'headuv' list.) */ static void reallymarkobject (global_State *g, GCObject *o) { lua_assert(iswhite(o) && !isdead(g, o)); white2gray(o); switch (gch(o)->tt) { case LUA_TSTRING: { return; /* for strings, gray is as good as black */ } case LUA_TUSERDATA: { Table *mt = gco2u(o)->metatable; markobject(g, mt); markobject(g, gco2u(o)->env); gray2black(o); /* all pointers marked */ return; } case LUA_TUPVAL: { UpVal *uv = gco2uv(o); markvalue(g, uv->v); if (uv->v == &uv->u.value) /* closed? (open upvalues remain gray) */ gray2black(o); /* make it black */ return; } case LUA_TFUNCTION: { gco2cl(o)->c.gclist = g->gray; g->gray = o; break; } case LUA_TTABLE: { linktable(gco2t(o), &g->gray); break; } case LUA_TTHREAD: { gco2th(o)->gclist = g->gray; g->gray = o; break; } case LUA_TPROTO: { gco2p(o)->gclist = g->gray; g->gray = o; break; } default: lua_assert(0); } }
/* ** Mark all values stored in marked open upvalues from non-marked threads. ** (Values from marked threads were already marked when traversing the ** thread.) Remove from the list threads that no longer have upvalues and ** not-marked threads. */ static void remarkupvals (global_State *g) { lua_State *thread; lua_State **p = &g->twups; while ((thread = *p) != NULL) { lua_assert(!isblack(thread)); /* threads are never black */ if (isgray(thread) && thread->openupval != NULL) p = &thread->twups; /* keep marked thread with upvalues in the list */ else { /* thread is not marked or without upvalues */ UpVal *uv; *p = thread->twups; /* remove thread from the list */ thread->twups = thread; /* mark that it is out of list */ for (uv = thread->openupval; uv != NULL; uv = uv->u.open.next) { if (uv->u.open.touched) { markvalue(g, uv->v); /* remark upvalue's value */ uv->u.open.touched = 0; } } } } }
/* ** Traverse a prototype. (While a prototype is being build, its ** arrays can be larger than needed; the extra slots are filled with ** NULL, so the use of 'markobjectN') */ static int traverseproto (global_State *g, Proto *f) { int i; if (f->cache && iswhite(f->cache)) f->cache = NULL; /* allow cache to be collected */ markobjectN(g, f->source); for (i = 0; i < f->sizek; i++) /* mark literals */ markvalue(g, &f->k[i]); for (i = 0; i < f->sizeupvalues; i++) /* mark upvalue names */ markobjectN(g, f->upvalues[i].name); for (i = 0; i < f->sizep; i++) /* mark nested protos */ markobjectN(g, f->p[i]); for (i = 0; i < f->sizelocvars; i++) /* mark local-variable names */ markobjectN(g, f->locvars[i].varname); return sizeof(Proto) + sizeof(Instruction) * f->sizecode + sizeof(Proto *) * f->sizep + sizeof(TValue) * f->sizek + sizeof(int) * f->sizelineinfo + sizeof(LocVar) * f->sizelocvars + sizeof(Upvaldesc) * f->sizeupvalues; }
static lu_mem traversestack (global_State *g, lua_State *th) { int n = 0; StkId o = th->stack; if (o == NULL) return 1; /* stack not completely built yet */ for (; o < th->top; o++) /* mark live elements in the stack */ markvalue(g, o); if (g->gcstate == GCSatomic) { /* final traversal? */ StkId lim = th->stack + th->stacksize; /* real end of stack */ for (; o < lim; o++) /* clear not-marked stack slice */ setnilvalue(o); } else { /* count call infos to compute size */ CallInfo *ci; for (ci = &th->base_ci; ci != th->ci; ci = ci->next) n++; } return sizeof(lua_State) + sizeof(TValue) * th->stacksize + sizeof(CallInfo) * n; }
/* ** Traverse a table with weak values and link it to proper list. During ** propagate phase, keep it in 'grayagain' list, to be revisited in the ** atomic phase. In the atomic phase, if table has any white value, ** put it in 'weak' list, to be cleared. */ static void traverseweakvalue (global_State *g, Table *h) { Node *n, *limit = gnodelast(h); /* if there is array part, assume it may have white values (it is not worth traversing it now just to check) */ int hasclears = (h->sizearray > 0); for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */ checkdeadkey(n); if (ttisnil(gval(n))) /* entry is empty? */ removeentry(n); /* remove it */ else { lua_assert(!ttisnil(gkey(n))); markvalue(g, gkey(n)); /* mark key */ if (!hasclears && iscleared(g, gval(n))) /* is there a white value? */ hasclears = 1; /* table will have to be cleared */ } } if (g->gcstate == GCSpropagate) linkgclist(h, g->grayagain); /* must retraverse it in atomic phase */ else if (hasclears) linkgclist(h, g->weak); /* has to be cleared later */ }
static void traverseweakvalue (global_State *g, Table *h) { Node *n, *limit = gnodelast(h); /* if there is array part, assume it may have white values (do not traverse it just to check) */ int hasclears = (h->sizearray > 0); for (n = gnode(h, 0); n < limit; n++) { checkdeadkey(n); if (ttisnil(gval(n))) /* entry is empty? */ removeentry(n); /* remove it */ else { lua_assert(!ttisnil(gkey(n))); markvalue(g, gkey(n)); /* mark key */ if (!hasclears && iscleared(g, gval(n))) /* is there a white value? */ hasclears = 1; /* table will have to be cleared */ } } if (hasclears) linktable(h, &g->weak); /* has to be cleared later */ else /* no white values */ linktable(h, &g->grayagain); /* no need to clean */ }
static lu_mem traversethread (global_State *g, lua_State *th) { StkId o = th->stack; if (o == NULL) return 1; /* stack not completely built yet */ lua_assert(g->gcstate == GCSinsideatomic || th->openupval == NULL || isintwups(th)); for (; o < th->top; o++) /* mark live elements in the stack */ markvalue(g, o); if (g->gcstate == GCSinsideatomic) { /* final traversal? */ StkId lim = th->stack + th->stacksize; /* real end of stack */ for (; o < lim; o++) /* clear not-marked stack slice */ setnilvalue(o); /* 'remarkupvals' may have removed thread from 'twups' list */ if (!isintwups(th) && th->openupval != NULL) { th->twups = g->twups; /* link it back to the list */ g->twups = th; } } else if (g->gckind != KGC_EMERGENCY) luaD_shrinkstack(th); /* do not change stack in emergency cycle */ return (sizeof(lua_State) + sizeof(TValue) * th->stacksize); }