18e3e3a7aSWarner Losh /*
20495ed39SKyle Evans ** $Id: lgc.c $
38e3e3a7aSWarner Losh ** Garbage Collector
48e3e3a7aSWarner Losh ** See Copyright Notice in lua.h
58e3e3a7aSWarner Losh */
68e3e3a7aSWarner Losh
78e3e3a7aSWarner Losh #define lgc_c
88e3e3a7aSWarner Losh #define LUA_CORE
98e3e3a7aSWarner Losh
108e3e3a7aSWarner Losh #include "lprefix.h"
118e3e3a7aSWarner Losh
120495ed39SKyle Evans #include <stdio.h>
138e3e3a7aSWarner Losh #include <string.h>
148e3e3a7aSWarner Losh
150495ed39SKyle Evans
168e3e3a7aSWarner Losh #include "lua.h"
178e3e3a7aSWarner Losh
188e3e3a7aSWarner Losh #include "ldebug.h"
198e3e3a7aSWarner Losh #include "ldo.h"
208e3e3a7aSWarner Losh #include "lfunc.h"
218e3e3a7aSWarner Losh #include "lgc.h"
228e3e3a7aSWarner Losh #include "lmem.h"
238e3e3a7aSWarner Losh #include "lobject.h"
248e3e3a7aSWarner Losh #include "lstate.h"
258e3e3a7aSWarner Losh #include "lstring.h"
268e3e3a7aSWarner Losh #include "ltable.h"
278e3e3a7aSWarner Losh #include "ltm.h"
288e3e3a7aSWarner Losh
298e3e3a7aSWarner Losh
308e3e3a7aSWarner Losh /*
310495ed39SKyle Evans ** Maximum number of elements to sweep in each single step.
320495ed39SKyle Evans ** (Large enough to dissipate fixed overheads but small enough
330495ed39SKyle Evans ** to allow small steps for the collector.)
348e3e3a7aSWarner Losh */
350495ed39SKyle Evans #define GCSWEEPMAX 100
368e3e3a7aSWarner Losh
378e3e3a7aSWarner Losh /*
380495ed39SKyle Evans ** Maximum number of finalizers to call in each single step.
398e3e3a7aSWarner Losh */
400495ed39SKyle Evans #define GCFINMAX 10
418e3e3a7aSWarner Losh
428e3e3a7aSWarner Losh
438e3e3a7aSWarner Losh /*
440495ed39SKyle Evans ** Cost of calling one finalizer.
458e3e3a7aSWarner Losh */
460495ed39SKyle Evans #define GCFINALIZECOST 50
470495ed39SKyle Evans
480495ed39SKyle Evans
490495ed39SKyle Evans /*
500495ed39SKyle Evans ** The equivalent, in bytes, of one unit of "work" (visiting a slot,
510495ed39SKyle Evans ** sweeping an object, etc.)
520495ed39SKyle Evans */
530495ed39SKyle Evans #define WORK2MEM sizeof(TValue)
548e3e3a7aSWarner Losh
558e3e3a7aSWarner Losh
568e3e3a7aSWarner Losh /*
578e3e3a7aSWarner Losh ** macro to adjust 'pause': 'pause' is actually used like
588e3e3a7aSWarner Losh ** 'pause / PAUSEADJ' (value chosen by tests)
598e3e3a7aSWarner Losh */
608e3e3a7aSWarner Losh #define PAUSEADJ 100
618e3e3a7aSWarner Losh
628e3e3a7aSWarner Losh
630495ed39SKyle Evans /* mask with all color bits */
640495ed39SKyle Evans #define maskcolors (bitmask(BLACKBIT) | WHITEBITS)
658e3e3a7aSWarner Losh
660495ed39SKyle Evans /* mask with all GC bits */
670495ed39SKyle Evans #define maskgcbits (maskcolors | AGEBITS)
680495ed39SKyle Evans
690495ed39SKyle Evans
700495ed39SKyle Evans /* macro to erase all color bits then set only the current white bit */
710495ed39SKyle Evans #define makewhite(g,x) \
720495ed39SKyle Evans (x->marked = cast_byte((x->marked & ~maskcolors) | luaC_white(g)))
730495ed39SKyle Evans
740495ed39SKyle Evans /* make an object gray (neither white nor black) */
750495ed39SKyle Evans #define set2gray(x) resetbits(x->marked, maskcolors)
760495ed39SKyle Evans
770495ed39SKyle Evans
780495ed39SKyle Evans /* make an object black (coming from any color) */
790495ed39SKyle Evans #define set2black(x) \
800495ed39SKyle Evans (x->marked = cast_byte((x->marked & ~WHITEBITS) | bitmask(BLACKBIT)))
818e3e3a7aSWarner Losh
828e3e3a7aSWarner Losh
838e3e3a7aSWarner Losh #define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x)))
848e3e3a7aSWarner Losh
850495ed39SKyle Evans #define keyiswhite(n) (keyiscollectable(n) && iswhite(gckey(n)))
868e3e3a7aSWarner Losh
878e3e3a7aSWarner Losh
880495ed39SKyle Evans /*
890495ed39SKyle Evans ** Protected access to objects in values
900495ed39SKyle Evans */
910495ed39SKyle Evans #define gcvalueN(o) (iscollectable(o) ? gcvalue(o) : NULL)
928e3e3a7aSWarner Losh
938e3e3a7aSWarner Losh
940495ed39SKyle Evans #define markvalue(g,o) { checkliveness(g->mainthread,o); \
958e3e3a7aSWarner Losh if (valiswhite(o)) reallymarkobject(g,gcvalue(o)); }
968e3e3a7aSWarner Losh
970495ed39SKyle Evans #define markkey(g, n) { if keyiswhite(n) reallymarkobject(g,gckey(n)); }
980495ed39SKyle Evans
998e3e3a7aSWarner Losh #define markobject(g,t) { if (iswhite(t)) reallymarkobject(g, obj2gco(t)); }
1008e3e3a7aSWarner Losh
1018e3e3a7aSWarner Losh /*
1028e3e3a7aSWarner Losh ** mark an object that can be NULL (either because it is really optional,
1038e3e3a7aSWarner Losh ** or it was stripped as debug info, or inside an uncompleted structure)
1048e3e3a7aSWarner Losh */
1058e3e3a7aSWarner Losh #define markobjectN(g,t) { if (t) markobject(g,t); }
1068e3e3a7aSWarner Losh
1078e3e3a7aSWarner Losh static void reallymarkobject (global_State *g, GCObject *o);
1080495ed39SKyle Evans static lu_mem atomic (lua_State *L);
1090495ed39SKyle Evans static void entersweep (lua_State *L);
1108e3e3a7aSWarner Losh
1118e3e3a7aSWarner Losh
1128e3e3a7aSWarner Losh /*
1138e3e3a7aSWarner Losh ** {======================================================
1148e3e3a7aSWarner Losh ** Generic functions
1158e3e3a7aSWarner Losh ** =======================================================
1168e3e3a7aSWarner Losh */
1178e3e3a7aSWarner Losh
1188e3e3a7aSWarner Losh
1198e3e3a7aSWarner Losh /*
1208e3e3a7aSWarner Losh ** one after last element in a hash array
1218e3e3a7aSWarner Losh */
1220495ed39SKyle Evans #define gnodelast(h) gnode(h, cast_sizet(sizenode(h)))
1230495ed39SKyle Evans
1240495ed39SKyle Evans
getgclist(GCObject * o)1250495ed39SKyle Evans static GCObject **getgclist (GCObject *o) {
1260495ed39SKyle Evans switch (o->tt) {
1270495ed39SKyle Evans case LUA_VTABLE: return &gco2t(o)->gclist;
1280495ed39SKyle Evans case LUA_VLCL: return &gco2lcl(o)->gclist;
1290495ed39SKyle Evans case LUA_VCCL: return &gco2ccl(o)->gclist;
1300495ed39SKyle Evans case LUA_VTHREAD: return &gco2th(o)->gclist;
1310495ed39SKyle Evans case LUA_VPROTO: return &gco2p(o)->gclist;
1320495ed39SKyle Evans case LUA_VUSERDATA: {
1330495ed39SKyle Evans Udata *u = gco2u(o);
1340495ed39SKyle Evans lua_assert(u->nuvalue > 0);
1350495ed39SKyle Evans return &u->gclist;
1360495ed39SKyle Evans }
1370495ed39SKyle Evans default: lua_assert(0); return 0;
1380495ed39SKyle Evans }
1390495ed39SKyle Evans }
1408e3e3a7aSWarner Losh
1418e3e3a7aSWarner Losh
1428e3e3a7aSWarner Losh /*
1430495ed39SKyle Evans ** Link a collectable object 'o' with a known type into the list 'p'.
1440495ed39SKyle Evans ** (Must be a macro to access the 'gclist' field in different types.)
1458e3e3a7aSWarner Losh */
1460495ed39SKyle Evans #define linkgclist(o,p) linkgclist_(obj2gco(o), &(o)->gclist, &(p))
1470495ed39SKyle Evans
linkgclist_(GCObject * o,GCObject ** pnext,GCObject ** list)1480495ed39SKyle Evans static void linkgclist_ (GCObject *o, GCObject **pnext, GCObject **list) {
1490495ed39SKyle Evans lua_assert(!isgray(o)); /* cannot be in a gray list */
1500495ed39SKyle Evans *pnext = *list;
1510495ed39SKyle Evans *list = o;
1520495ed39SKyle Evans set2gray(o); /* now it is */
1530495ed39SKyle Evans }
1548e3e3a7aSWarner Losh
1558e3e3a7aSWarner Losh
1568e3e3a7aSWarner Losh /*
1570495ed39SKyle Evans ** Link a generic collectable object 'o' into the list 'p'.
1588e3e3a7aSWarner Losh */
1590495ed39SKyle Evans #define linkobjgclist(o,p) linkgclist_(obj2gco(o), getgclist(o), &(p))
1600495ed39SKyle Evans
1610495ed39SKyle Evans
1620495ed39SKyle Evans
1630495ed39SKyle Evans /*
1640495ed39SKyle Evans ** Clear keys for empty entries in tables. If entry is empty, mark its
1650495ed39SKyle Evans ** entry as dead. This allows the collection of the key, but keeps its
1660495ed39SKyle Evans ** entry in the table: its removal could break a chain and could break
1670495ed39SKyle Evans ** a table traversal. Other places never manipulate dead keys, because
1680495ed39SKyle Evans ** its associated empty value is enough to signal that the entry is
1690495ed39SKyle Evans ** logically empty.
1700495ed39SKyle Evans */
clearkey(Node * n)1710495ed39SKyle Evans static void clearkey (Node *n) {
1720495ed39SKyle Evans lua_assert(isempty(gval(n)));
1730495ed39SKyle Evans if (keyiscollectable(n))
1740495ed39SKyle Evans setdeadkey(n); /* unused key; remove it */
1758e3e3a7aSWarner Losh }
1768e3e3a7aSWarner Losh
1778e3e3a7aSWarner Losh
1788e3e3a7aSWarner Losh /*
1798e3e3a7aSWarner Losh ** tells whether a key or value can be cleared from a weak
1808e3e3a7aSWarner Losh ** table. Non-collectable objects are never removed from weak
1818e3e3a7aSWarner Losh ** tables. Strings behave as 'values', so are never removed too. for
1828e3e3a7aSWarner Losh ** other objects: if really collected, cannot keep them; for objects
1838e3e3a7aSWarner Losh ** being finalized, keep them in keys, but not in values
1848e3e3a7aSWarner Losh */
iscleared(global_State * g,const GCObject * o)1850495ed39SKyle Evans static int iscleared (global_State *g, const GCObject *o) {
1860495ed39SKyle Evans if (o == NULL) return 0; /* non-collectable value */
1870495ed39SKyle Evans else if (novariant(o->tt) == LUA_TSTRING) {
1880495ed39SKyle Evans markobject(g, o); /* strings are 'values', so are never weak */
1898e3e3a7aSWarner Losh return 0;
1908e3e3a7aSWarner Losh }
1910495ed39SKyle Evans else return iswhite(o);
1928e3e3a7aSWarner Losh }
1938e3e3a7aSWarner Losh
1948e3e3a7aSWarner Losh
1958e3e3a7aSWarner Losh /*
1960495ed39SKyle Evans ** Barrier that moves collector forward, that is, marks the white object
1970495ed39SKyle Evans ** 'v' being pointed by the black object 'o'. In the generational
1980495ed39SKyle Evans ** mode, 'v' must also become old, if 'o' is old; however, it cannot
1990495ed39SKyle Evans ** be changed directly to OLD, because it may still point to non-old
2000495ed39SKyle Evans ** objects. So, it is marked as OLD0. In the next cycle it will become
2010495ed39SKyle Evans ** OLD1, and in the next it will finally become OLD (regular old). By
2020495ed39SKyle Evans ** then, any object it points to will also be old. If called in the
2030495ed39SKyle Evans ** incremental sweep phase, it clears the black object to white (sweep
2040495ed39SKyle Evans ** it) to avoid other barrier calls for this same object. (That cannot
2050495ed39SKyle Evans ** be done is generational mode, as its sweep does not distinguish
2060495ed39SKyle Evans ** whites from deads.)
2078e3e3a7aSWarner Losh */
luaC_barrier_(lua_State * L,GCObject * o,GCObject * v)2088e3e3a7aSWarner Losh void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) {
2098e3e3a7aSWarner Losh global_State *g = G(L);
2108e3e3a7aSWarner Losh lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o));
2110495ed39SKyle Evans if (keepinvariant(g)) { /* must keep invariant? */
2128e3e3a7aSWarner Losh reallymarkobject(g, v); /* restore invariant */
2130495ed39SKyle Evans if (isold(o)) {
2140495ed39SKyle Evans lua_assert(!isold(v)); /* white object could not be old */
2150495ed39SKyle Evans setage(v, G_OLD0); /* restore generational invariant */
2160495ed39SKyle Evans }
2170495ed39SKyle Evans }
2188e3e3a7aSWarner Losh else { /* sweep phase */
2198e3e3a7aSWarner Losh lua_assert(issweepphase(g));
2200495ed39SKyle Evans if (g->gckind == KGC_INC) /* incremental mode? */
2210495ed39SKyle Evans makewhite(g, o); /* mark 'o' as white to avoid other barriers */
2228e3e3a7aSWarner Losh }
2238e3e3a7aSWarner Losh }
2248e3e3a7aSWarner Losh
2258e3e3a7aSWarner Losh
2268e3e3a7aSWarner Losh /*
2278e3e3a7aSWarner Losh ** barrier that moves collector backward, that is, mark the black object
2288e3e3a7aSWarner Losh ** pointing to a white object as gray again.
2298e3e3a7aSWarner Losh */
luaC_barrierback_(lua_State * L,GCObject * o)2300495ed39SKyle Evans void luaC_barrierback_ (lua_State *L, GCObject *o) {
2318e3e3a7aSWarner Losh global_State *g = G(L);
2320495ed39SKyle Evans lua_assert(isblack(o) && !isdead(g, o));
2330495ed39SKyle Evans lua_assert((g->gckind == KGC_GEN) == (isold(o) && getage(o) != G_TOUCHED1));
2340495ed39SKyle Evans if (getage(o) == G_TOUCHED2) /* already in gray list? */
2350495ed39SKyle Evans set2gray(o); /* make it gray to become touched1 */
2360495ed39SKyle Evans else /* link it in 'grayagain' and paint it gray */
2370495ed39SKyle Evans linkobjgclist(o, g->grayagain);
2380495ed39SKyle Evans if (isold(o)) /* generational mode? */
2390495ed39SKyle Evans setage(o, G_TOUCHED1); /* touched in current cycle */
2408e3e3a7aSWarner Losh }
2418e3e3a7aSWarner Losh
2428e3e3a7aSWarner Losh
luaC_fix(lua_State * L,GCObject * o)2438e3e3a7aSWarner Losh void luaC_fix (lua_State *L, GCObject *o) {
2448e3e3a7aSWarner Losh global_State *g = G(L);
2458e3e3a7aSWarner Losh lua_assert(g->allgc == o); /* object must be 1st in 'allgc' list! */
2460495ed39SKyle Evans set2gray(o); /* they will be gray forever */
2470495ed39SKyle Evans setage(o, G_OLD); /* and old forever */
2488e3e3a7aSWarner Losh g->allgc = o->next; /* remove object from 'allgc' list */
2498e3e3a7aSWarner Losh o->next = g->fixedgc; /* link it to 'fixedgc' list */
2508e3e3a7aSWarner Losh g->fixedgc = o;
2518e3e3a7aSWarner Losh }
2528e3e3a7aSWarner Losh
2538e3e3a7aSWarner Losh
2548e3e3a7aSWarner Losh /*
255*a9490b81SWarner Losh ** create a new collectable object (with given type, size, and offset)
256*a9490b81SWarner Losh ** and link it to 'allgc' list.
2578e3e3a7aSWarner Losh */
luaC_newobjdt(lua_State * L,int tt,size_t sz,size_t offset)258*a9490b81SWarner Losh GCObject *luaC_newobjdt (lua_State *L, int tt, size_t sz, size_t offset) {
2598e3e3a7aSWarner Losh global_State *g = G(L);
260*a9490b81SWarner Losh char *p = cast_charp(luaM_newobject(L, novariant(tt), sz));
261*a9490b81SWarner Losh GCObject *o = cast(GCObject *, p + offset);
2628e3e3a7aSWarner Losh o->marked = luaC_white(g);
2638e3e3a7aSWarner Losh o->tt = tt;
2648e3e3a7aSWarner Losh o->next = g->allgc;
2658e3e3a7aSWarner Losh g->allgc = o;
2668e3e3a7aSWarner Losh return o;
2678e3e3a7aSWarner Losh }
2688e3e3a7aSWarner Losh
269*a9490b81SWarner Losh
luaC_newobj(lua_State * L,int tt,size_t sz)270*a9490b81SWarner Losh GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) {
271*a9490b81SWarner Losh return luaC_newobjdt(L, tt, sz, 0);
272*a9490b81SWarner Losh }
273*a9490b81SWarner Losh
2748e3e3a7aSWarner Losh /* }====================================================== */
2758e3e3a7aSWarner Losh
2768e3e3a7aSWarner Losh
2778e3e3a7aSWarner Losh
2788e3e3a7aSWarner Losh /*
2798e3e3a7aSWarner Losh ** {======================================================
2808e3e3a7aSWarner Losh ** Mark functions
2818e3e3a7aSWarner Losh ** =======================================================
2828e3e3a7aSWarner Losh */
2838e3e3a7aSWarner Losh
2848e3e3a7aSWarner Losh
2858e3e3a7aSWarner Losh /*
2860495ed39SKyle Evans ** Mark an object. Userdata with no user values, strings, and closed
2870495ed39SKyle Evans ** upvalues are visited and turned black here. Open upvalues are
2880495ed39SKyle Evans ** already indirectly linked through their respective threads in the
2890495ed39SKyle Evans ** 'twups' list, so they don't go to the gray list; nevertheless, they
2900495ed39SKyle Evans ** are kept gray to avoid barriers, as their values will be revisited
2910495ed39SKyle Evans ** by the thread or by 'remarkupvals'. Other objects are added to the
2920495ed39SKyle Evans ** gray list to be visited (and turned black) later. Both userdata and
2930495ed39SKyle Evans ** upvalues can call this function recursively, but this recursion goes
2940495ed39SKyle Evans ** for at most two levels: An upvalue cannot refer to another upvalue
2950495ed39SKyle Evans ** (only closures can), and a userdata's metatable must be a table.
2968e3e3a7aSWarner Losh */
reallymarkobject(global_State * g,GCObject * o)2978e3e3a7aSWarner Losh static void reallymarkobject (global_State *g, GCObject *o) {
2988e3e3a7aSWarner Losh switch (o->tt) {
2990495ed39SKyle Evans case LUA_VSHRSTR:
3000495ed39SKyle Evans case LUA_VLNGSTR: {
3010495ed39SKyle Evans set2black(o); /* nothing to visit */
3028e3e3a7aSWarner Losh break;
3038e3e3a7aSWarner Losh }
3040495ed39SKyle Evans case LUA_VUPVAL: {
3050495ed39SKyle Evans UpVal *uv = gco2upv(o);
3060495ed39SKyle Evans if (upisopen(uv))
3070495ed39SKyle Evans set2gray(uv); /* open upvalues are kept gray */
3080495ed39SKyle Evans else
3090495ed39SKyle Evans set2black(uv); /* closed upvalues are visited here */
310*a9490b81SWarner Losh markvalue(g, uv->v.p); /* mark its content */
3118e3e3a7aSWarner Losh break;
3128e3e3a7aSWarner Losh }
3130495ed39SKyle Evans case LUA_VUSERDATA: {
3140495ed39SKyle Evans Udata *u = gco2u(o);
3150495ed39SKyle Evans if (u->nuvalue == 0) { /* no user values? */
3160495ed39SKyle Evans markobjectN(g, u->metatable); /* mark its metatable */
3170495ed39SKyle Evans set2black(u); /* nothing else to mark */
3188e3e3a7aSWarner Losh break;
3198e3e3a7aSWarner Losh }
3200495ed39SKyle Evans /* else... */
3210495ed39SKyle Evans } /* FALLTHROUGH */
3220495ed39SKyle Evans case LUA_VLCL: case LUA_VCCL: case LUA_VTABLE:
3230495ed39SKyle Evans case LUA_VTHREAD: case LUA_VPROTO: {
3240495ed39SKyle Evans linkobjgclist(o, g->gray); /* to be visited later */
3258e3e3a7aSWarner Losh break;
3268e3e3a7aSWarner Losh }
3278e3e3a7aSWarner Losh default: lua_assert(0); break;
3288e3e3a7aSWarner Losh }
3298e3e3a7aSWarner Losh }
3308e3e3a7aSWarner Losh
3318e3e3a7aSWarner Losh
3328e3e3a7aSWarner Losh /*
3338e3e3a7aSWarner Losh ** mark metamethods for basic types
3348e3e3a7aSWarner Losh */
markmt(global_State * g)3358e3e3a7aSWarner Losh static void markmt (global_State *g) {
3368e3e3a7aSWarner Losh int i;
3378e3e3a7aSWarner Losh for (i=0; i < LUA_NUMTAGS; i++)
3388e3e3a7aSWarner Losh markobjectN(g, g->mt[i]);
3398e3e3a7aSWarner Losh }
3408e3e3a7aSWarner Losh
3418e3e3a7aSWarner Losh
3428e3e3a7aSWarner Losh /*
3438e3e3a7aSWarner Losh ** mark all objects in list of being-finalized
3448e3e3a7aSWarner Losh */
markbeingfnz(global_State * g)3450495ed39SKyle Evans static lu_mem markbeingfnz (global_State *g) {
3468e3e3a7aSWarner Losh GCObject *o;
3470495ed39SKyle Evans lu_mem count = 0;
3480495ed39SKyle Evans for (o = g->tobefnz; o != NULL; o = o->next) {
3490495ed39SKyle Evans count++;
3508e3e3a7aSWarner Losh markobject(g, o);
3518e3e3a7aSWarner Losh }
3520495ed39SKyle Evans return count;
3530495ed39SKyle Evans }
3548e3e3a7aSWarner Losh
3558e3e3a7aSWarner Losh
3568e3e3a7aSWarner Losh /*
3570495ed39SKyle Evans ** For each non-marked thread, simulates a barrier between each open
3580495ed39SKyle Evans ** upvalue and its value. (If the thread is collected, the value will be
3590495ed39SKyle Evans ** assigned to the upvalue, but then it can be too late for the barrier
3600495ed39SKyle Evans ** to act. The "barrier" does not need to check colors: A non-marked
3610495ed39SKyle Evans ** thread must be young; upvalues cannot be older than their threads; so
3620495ed39SKyle Evans ** any visited upvalue must be young too.) Also removes the thread from
3630495ed39SKyle Evans ** the list, as it was already visited. Removes also threads with no
3640495ed39SKyle Evans ** upvalues, as they have nothing to be checked. (If the thread gets an
3650495ed39SKyle Evans ** upvalue later, it will be linked in the list again.)
3668e3e3a7aSWarner Losh */
remarkupvals(global_State * g)3670495ed39SKyle Evans static int remarkupvals (global_State *g) {
3688e3e3a7aSWarner Losh lua_State *thread;
3698e3e3a7aSWarner Losh lua_State **p = &g->twups;
3700495ed39SKyle Evans int work = 0; /* estimate of how much work was done here */
3718e3e3a7aSWarner Losh while ((thread = *p) != NULL) {
3720495ed39SKyle Evans work++;
3730495ed39SKyle Evans if (!iswhite(thread) && thread->openupval != NULL)
3748e3e3a7aSWarner Losh p = &thread->twups; /* keep marked thread with upvalues in the list */
3758e3e3a7aSWarner Losh else { /* thread is not marked or without upvalues */
3768e3e3a7aSWarner Losh UpVal *uv;
3770495ed39SKyle Evans lua_assert(!isold(thread) || thread->openupval == NULL);
3788e3e3a7aSWarner Losh *p = thread->twups; /* remove thread from the list */
3798e3e3a7aSWarner Losh thread->twups = thread; /* mark that it is out of list */
3808e3e3a7aSWarner Losh for (uv = thread->openupval; uv != NULL; uv = uv->u.open.next) {
3810495ed39SKyle Evans lua_assert(getage(uv) <= getage(thread));
3820495ed39SKyle Evans work++;
3830495ed39SKyle Evans if (!iswhite(uv)) { /* upvalue already visited? */
3840495ed39SKyle Evans lua_assert(upisopen(uv) && isgray(uv));
385*a9490b81SWarner Losh markvalue(g, uv->v.p); /* mark its value */
3868e3e3a7aSWarner Losh }
3878e3e3a7aSWarner Losh }
3888e3e3a7aSWarner Losh }
3898e3e3a7aSWarner Losh }
3900495ed39SKyle Evans return work;
3910495ed39SKyle Evans }
3920495ed39SKyle Evans
3930495ed39SKyle Evans
cleargraylists(global_State * g)3940495ed39SKyle Evans static void cleargraylists (global_State *g) {
3950495ed39SKyle Evans g->gray = g->grayagain = NULL;
3960495ed39SKyle Evans g->weak = g->allweak = g->ephemeron = NULL;
3978e3e3a7aSWarner Losh }
3988e3e3a7aSWarner Losh
3998e3e3a7aSWarner Losh
4008e3e3a7aSWarner Losh /*
4018e3e3a7aSWarner Losh ** mark root set and reset all gray lists, to start a new collection
4028e3e3a7aSWarner Losh */
restartcollection(global_State * g)4038e3e3a7aSWarner Losh static void restartcollection (global_State *g) {
4040495ed39SKyle Evans cleargraylists(g);
4058e3e3a7aSWarner Losh markobject(g, g->mainthread);
4068e3e3a7aSWarner Losh markvalue(g, &g->l_registry);
4078e3e3a7aSWarner Losh markmt(g);
4088e3e3a7aSWarner Losh markbeingfnz(g); /* mark any finalizing object left from previous cycle */
4098e3e3a7aSWarner Losh }
4108e3e3a7aSWarner Losh
4118e3e3a7aSWarner Losh /* }====================================================== */
4128e3e3a7aSWarner Losh
4138e3e3a7aSWarner Losh
4148e3e3a7aSWarner Losh /*
4158e3e3a7aSWarner Losh ** {======================================================
4168e3e3a7aSWarner Losh ** Traverse functions
4178e3e3a7aSWarner Losh ** =======================================================
4188e3e3a7aSWarner Losh */
4198e3e3a7aSWarner Losh
4200495ed39SKyle Evans
4210495ed39SKyle Evans /*
4220495ed39SKyle Evans ** Check whether object 'o' should be kept in the 'grayagain' list for
4230495ed39SKyle Evans ** post-processing by 'correctgraylist'. (It could put all old objects
4240495ed39SKyle Evans ** in the list and leave all the work to 'correctgraylist', but it is
4250495ed39SKyle Evans ** more efficient to avoid adding elements that will be removed.) Only
4260495ed39SKyle Evans ** TOUCHED1 objects need to be in the list. TOUCHED2 doesn't need to go
4270495ed39SKyle Evans ** back to a gray list, but then it must become OLD. (That is what
4280495ed39SKyle Evans ** 'correctgraylist' does when it finds a TOUCHED2 object.)
4290495ed39SKyle Evans */
genlink(global_State * g,GCObject * o)4300495ed39SKyle Evans static void genlink (global_State *g, GCObject *o) {
4310495ed39SKyle Evans lua_assert(isblack(o));
4320495ed39SKyle Evans if (getage(o) == G_TOUCHED1) { /* touched in this cycle? */
4330495ed39SKyle Evans linkobjgclist(o, g->grayagain); /* link it back in 'grayagain' */
4340495ed39SKyle Evans } /* everything else do not need to be linked back */
4350495ed39SKyle Evans else if (getage(o) == G_TOUCHED2)
4360495ed39SKyle Evans changeage(o, G_TOUCHED2, G_OLD); /* advance age */
4370495ed39SKyle Evans }
4380495ed39SKyle Evans
4390495ed39SKyle Evans
4408e3e3a7aSWarner Losh /*
4418e3e3a7aSWarner Losh ** Traverse a table with weak values and link it to proper list. During
4428e3e3a7aSWarner Losh ** propagate phase, keep it in 'grayagain' list, to be revisited in the
4438e3e3a7aSWarner Losh ** atomic phase. In the atomic phase, if table has any white value,
4448e3e3a7aSWarner Losh ** put it in 'weak' list, to be cleared.
4458e3e3a7aSWarner Losh */
traverseweakvalue(global_State * g,Table * h)4468e3e3a7aSWarner Losh static void traverseweakvalue (global_State *g, Table *h) {
4478e3e3a7aSWarner Losh Node *n, *limit = gnodelast(h);
4488e3e3a7aSWarner Losh /* if there is array part, assume it may have white values (it is not
4498e3e3a7aSWarner Losh worth traversing it now just to check) */
4500495ed39SKyle Evans int hasclears = (h->alimit > 0);
4518e3e3a7aSWarner Losh for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */
4520495ed39SKyle Evans if (isempty(gval(n))) /* entry is empty? */
4530495ed39SKyle Evans clearkey(n); /* clear its key */
4548e3e3a7aSWarner Losh else {
4550495ed39SKyle Evans lua_assert(!keyisnil(n));
4560495ed39SKyle Evans markkey(g, n);
4570495ed39SKyle Evans if (!hasclears && iscleared(g, gcvalueN(gval(n)))) /* a white value? */
4588e3e3a7aSWarner Losh hasclears = 1; /* table will have to be cleared */
4598e3e3a7aSWarner Losh }
4608e3e3a7aSWarner Losh }
4610495ed39SKyle Evans if (g->gcstate == GCSatomic && hasclears)
4628e3e3a7aSWarner Losh linkgclist(h, g->weak); /* has to be cleared later */
4630495ed39SKyle Evans else
4640495ed39SKyle Evans linkgclist(h, g->grayagain); /* must retraverse it in atomic phase */
4658e3e3a7aSWarner Losh }
4668e3e3a7aSWarner Losh
4678e3e3a7aSWarner Losh
4688e3e3a7aSWarner Losh /*
4698e3e3a7aSWarner Losh ** Traverse an ephemeron table and link it to proper list. Returns true
4708e3e3a7aSWarner Losh ** iff any object was marked during this traversal (which implies that
4718e3e3a7aSWarner Losh ** convergence has to continue). During propagation phase, keep table
4728e3e3a7aSWarner Losh ** in 'grayagain' list, to be visited again in the atomic phase. In
4738e3e3a7aSWarner Losh ** the atomic phase, if table has any white->white entry, it has to
4748e3e3a7aSWarner Losh ** be revisited during ephemeron convergence (as that key may turn
4758e3e3a7aSWarner Losh ** black). Otherwise, if it has any white key, table has to be cleared
4760495ed39SKyle Evans ** (in the atomic phase). In generational mode, some tables
4770495ed39SKyle Evans ** must be kept in some gray list for post-processing; this is done
4780495ed39SKyle Evans ** by 'genlink'.
4798e3e3a7aSWarner Losh */
traverseephemeron(global_State * g,Table * h,int inv)4800495ed39SKyle Evans static int traverseephemeron (global_State *g, Table *h, int inv) {
4818e3e3a7aSWarner Losh int marked = 0; /* true if an object is marked in this traversal */
4828e3e3a7aSWarner Losh int hasclears = 0; /* true if table has white keys */
4838e3e3a7aSWarner Losh int hasww = 0; /* true if table has entry "white-key -> white-value" */
4848e3e3a7aSWarner Losh unsigned int i;
4850495ed39SKyle Evans unsigned int asize = luaH_realasize(h);
4860495ed39SKyle Evans unsigned int nsize = sizenode(h);
4878e3e3a7aSWarner Losh /* traverse array part */
4880495ed39SKyle Evans for (i = 0; i < asize; i++) {
4898e3e3a7aSWarner Losh if (valiswhite(&h->array[i])) {
4908e3e3a7aSWarner Losh marked = 1;
4918e3e3a7aSWarner Losh reallymarkobject(g, gcvalue(&h->array[i]));
4928e3e3a7aSWarner Losh }
4938e3e3a7aSWarner Losh }
4940495ed39SKyle Evans /* traverse hash part; if 'inv', traverse descending
4950495ed39SKyle Evans (see 'convergeephemerons') */
4960495ed39SKyle Evans for (i = 0; i < nsize; i++) {
4970495ed39SKyle Evans Node *n = inv ? gnode(h, nsize - 1 - i) : gnode(h, i);
4980495ed39SKyle Evans if (isempty(gval(n))) /* entry is empty? */
4990495ed39SKyle Evans clearkey(n); /* clear its key */
5000495ed39SKyle Evans else if (iscleared(g, gckeyN(n))) { /* key is not marked (yet)? */
5018e3e3a7aSWarner Losh hasclears = 1; /* table must be cleared */
5028e3e3a7aSWarner Losh if (valiswhite(gval(n))) /* value not marked yet? */
5038e3e3a7aSWarner Losh hasww = 1; /* white-white entry */
5048e3e3a7aSWarner Losh }
5058e3e3a7aSWarner Losh else if (valiswhite(gval(n))) { /* value not marked yet? */
5068e3e3a7aSWarner Losh marked = 1;
5078e3e3a7aSWarner Losh reallymarkobject(g, gcvalue(gval(n))); /* mark it now */
5088e3e3a7aSWarner Losh }
5098e3e3a7aSWarner Losh }
5108e3e3a7aSWarner Losh /* link table into proper list */
5118e3e3a7aSWarner Losh if (g->gcstate == GCSpropagate)
5128e3e3a7aSWarner Losh linkgclist(h, g->grayagain); /* must retraverse it in atomic phase */
5138e3e3a7aSWarner Losh else if (hasww) /* table has white->white entries? */
5148e3e3a7aSWarner Losh linkgclist(h, g->ephemeron); /* have to propagate again */
5158e3e3a7aSWarner Losh else if (hasclears) /* table has white keys? */
5168e3e3a7aSWarner Losh linkgclist(h, g->allweak); /* may have to clean white keys */
5170495ed39SKyle Evans else
5180495ed39SKyle Evans genlink(g, obj2gco(h)); /* check whether collector still needs to see it */
5198e3e3a7aSWarner Losh return marked;
5208e3e3a7aSWarner Losh }
5218e3e3a7aSWarner Losh
5228e3e3a7aSWarner Losh
traversestrongtable(global_State * g,Table * h)5238e3e3a7aSWarner Losh static void traversestrongtable (global_State *g, Table *h) {
5248e3e3a7aSWarner Losh Node *n, *limit = gnodelast(h);
5258e3e3a7aSWarner Losh unsigned int i;
5260495ed39SKyle Evans unsigned int asize = luaH_realasize(h);
5270495ed39SKyle Evans for (i = 0; i < asize; i++) /* traverse array part */
5288e3e3a7aSWarner Losh markvalue(g, &h->array[i]);
5298e3e3a7aSWarner Losh for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */
5300495ed39SKyle Evans if (isempty(gval(n))) /* entry is empty? */
5310495ed39SKyle Evans clearkey(n); /* clear its key */
5328e3e3a7aSWarner Losh else {
5330495ed39SKyle Evans lua_assert(!keyisnil(n));
5340495ed39SKyle Evans markkey(g, n);
5350495ed39SKyle Evans markvalue(g, gval(n));
5368e3e3a7aSWarner Losh }
5378e3e3a7aSWarner Losh }
5380495ed39SKyle Evans genlink(g, obj2gco(h));
5398e3e3a7aSWarner Losh }
5408e3e3a7aSWarner Losh
5418e3e3a7aSWarner Losh
traversetable(global_State * g,Table * h)5428e3e3a7aSWarner Losh static lu_mem traversetable (global_State *g, Table *h) {
5438e3e3a7aSWarner Losh const char *weakkey, *weakvalue;
5448e3e3a7aSWarner Losh const TValue *mode = gfasttm(g, h->metatable, TM_MODE);
5458e3e3a7aSWarner Losh markobjectN(g, h->metatable);
5468e3e3a7aSWarner Losh if (mode && ttisstring(mode) && /* is there a weak mode? */
5470495ed39SKyle Evans (cast_void(weakkey = strchr(svalue(mode), 'k')),
5480495ed39SKyle Evans cast_void(weakvalue = strchr(svalue(mode), 'v')),
5498e3e3a7aSWarner Losh (weakkey || weakvalue))) { /* is really weak? */
5508e3e3a7aSWarner Losh if (!weakkey) /* strong keys? */
5518e3e3a7aSWarner Losh traverseweakvalue(g, h);
5528e3e3a7aSWarner Losh else if (!weakvalue) /* strong values? */
5530495ed39SKyle Evans traverseephemeron(g, h, 0);
5548e3e3a7aSWarner Losh else /* all weak */
5558e3e3a7aSWarner Losh linkgclist(h, g->allweak); /* nothing to traverse now */
5568e3e3a7aSWarner Losh }
5578e3e3a7aSWarner Losh else /* not weak */
5588e3e3a7aSWarner Losh traversestrongtable(g, h);
5590495ed39SKyle Evans return 1 + h->alimit + 2 * allocsizenode(h);
5600495ed39SKyle Evans }
5610495ed39SKyle Evans
5620495ed39SKyle Evans
traverseudata(global_State * g,Udata * u)5630495ed39SKyle Evans static int traverseudata (global_State *g, Udata *u) {
5640495ed39SKyle Evans int i;
5650495ed39SKyle Evans markobjectN(g, u->metatable); /* mark its metatable */
5660495ed39SKyle Evans for (i = 0; i < u->nuvalue; i++)
5670495ed39SKyle Evans markvalue(g, &u->uv[i].uv);
5680495ed39SKyle Evans genlink(g, obj2gco(u));
5690495ed39SKyle Evans return 1 + u->nuvalue;
5708e3e3a7aSWarner Losh }
5718e3e3a7aSWarner Losh
5728e3e3a7aSWarner Losh
5738e3e3a7aSWarner Losh /*
5748e3e3a7aSWarner Losh ** Traverse a prototype. (While a prototype is being build, its
5758e3e3a7aSWarner Losh ** arrays can be larger than needed; the extra slots are filled with
5768e3e3a7aSWarner Losh ** NULL, so the use of 'markobjectN')
5778e3e3a7aSWarner Losh */
traverseproto(global_State * g,Proto * f)5788e3e3a7aSWarner Losh static int traverseproto (global_State *g, Proto *f) {
5798e3e3a7aSWarner Losh int i;
5808e3e3a7aSWarner Losh markobjectN(g, f->source);
5818e3e3a7aSWarner Losh for (i = 0; i < f->sizek; i++) /* mark literals */
5828e3e3a7aSWarner Losh markvalue(g, &f->k[i]);
5838e3e3a7aSWarner Losh for (i = 0; i < f->sizeupvalues; i++) /* mark upvalue names */
5848e3e3a7aSWarner Losh markobjectN(g, f->upvalues[i].name);
5858e3e3a7aSWarner Losh for (i = 0; i < f->sizep; i++) /* mark nested protos */
5868e3e3a7aSWarner Losh markobjectN(g, f->p[i]);
5878e3e3a7aSWarner Losh for (i = 0; i < f->sizelocvars; i++) /* mark local-variable names */
5888e3e3a7aSWarner Losh markobjectN(g, f->locvars[i].varname);
5890495ed39SKyle Evans return 1 + f->sizek + f->sizeupvalues + f->sizep + f->sizelocvars;
5908e3e3a7aSWarner Losh }
5918e3e3a7aSWarner Losh
5928e3e3a7aSWarner Losh
traverseCclosure(global_State * g,CClosure * cl)5930495ed39SKyle Evans static int traverseCclosure (global_State *g, CClosure *cl) {
5948e3e3a7aSWarner Losh int i;
5958e3e3a7aSWarner Losh for (i = 0; i < cl->nupvalues; i++) /* mark its upvalues */
5968e3e3a7aSWarner Losh markvalue(g, &cl->upvalue[i]);
5970495ed39SKyle Evans return 1 + cl->nupvalues;
5988e3e3a7aSWarner Losh }
5998e3e3a7aSWarner Losh
6008e3e3a7aSWarner Losh /*
6010495ed39SKyle Evans ** Traverse a Lua closure, marking its prototype and its upvalues.
6020495ed39SKyle Evans ** (Both can be NULL while closure is being created.)
6038e3e3a7aSWarner Losh */
traverseLclosure(global_State * g,LClosure * cl)6040495ed39SKyle Evans static int traverseLclosure (global_State *g, LClosure *cl) {
6058e3e3a7aSWarner Losh int i;
6068e3e3a7aSWarner Losh markobjectN(g, cl->p); /* mark its prototype */
6070495ed39SKyle Evans for (i = 0; i < cl->nupvalues; i++) { /* visit its upvalues */
6088e3e3a7aSWarner Losh UpVal *uv = cl->upvals[i];
6090495ed39SKyle Evans markobjectN(g, uv); /* mark upvalue */
6108e3e3a7aSWarner Losh }
6110495ed39SKyle Evans return 1 + cl->nupvalues;
6128e3e3a7aSWarner Losh }
6138e3e3a7aSWarner Losh
6148e3e3a7aSWarner Losh
6150495ed39SKyle Evans /*
6160495ed39SKyle Evans ** Traverse a thread, marking the elements in the stack up to its top
6170495ed39SKyle Evans ** and cleaning the rest of the stack in the final traversal. That
6180495ed39SKyle Evans ** ensures that the entire stack have valid (non-dead) objects.
6190495ed39SKyle Evans ** Threads have no barriers. In gen. mode, old threads must be visited
6200495ed39SKyle Evans ** at every cycle, because they might point to young objects. In inc.
6210495ed39SKyle Evans ** mode, the thread can still be modified before the end of the cycle,
6220495ed39SKyle Evans ** and therefore it must be visited again in the atomic phase. To ensure
6230495ed39SKyle Evans ** these visits, threads must return to a gray list if they are not new
6240495ed39SKyle Evans ** (which can only happen in generational mode) or if the traverse is in
6250495ed39SKyle Evans ** the propagate phase (which can only happen in incremental mode).
6260495ed39SKyle Evans */
traversethread(global_State * g,lua_State * th)6270495ed39SKyle Evans static int traversethread (global_State *g, lua_State *th) {
6280495ed39SKyle Evans UpVal *uv;
629*a9490b81SWarner Losh StkId o = th->stack.p;
6300495ed39SKyle Evans if (isold(th) || g->gcstate == GCSpropagate)
6310495ed39SKyle Evans linkgclist(th, g->grayagain); /* insert into 'grayagain' list */
6328e3e3a7aSWarner Losh if (o == NULL)
6338e3e3a7aSWarner Losh return 1; /* stack not completely built yet */
6340495ed39SKyle Evans lua_assert(g->gcstate == GCSatomic ||
6358e3e3a7aSWarner Losh th->openupval == NULL || isintwups(th));
636*a9490b81SWarner Losh for (; o < th->top.p; o++) /* mark live elements in the stack */
6370495ed39SKyle Evans markvalue(g, s2v(o));
6380495ed39SKyle Evans for (uv = th->openupval; uv != NULL; uv = uv->u.open.next)
6390495ed39SKyle Evans markobject(g, uv); /* open upvalues cannot be collected */
6400495ed39SKyle Evans if (g->gcstate == GCSatomic) { /* final traversal? */
641*a9490b81SWarner Losh for (; o < th->stack_last.p + EXTRA_STACK; o++)
6420495ed39SKyle Evans setnilvalue(s2v(o)); /* clear dead stack slice */
6438e3e3a7aSWarner Losh /* 'remarkupvals' may have removed thread from 'twups' list */
6448e3e3a7aSWarner Losh if (!isintwups(th) && th->openupval != NULL) {
6458e3e3a7aSWarner Losh th->twups = g->twups; /* link it back to the list */
6468e3e3a7aSWarner Losh g->twups = th;
6478e3e3a7aSWarner Losh }
6488e3e3a7aSWarner Losh }
6490495ed39SKyle Evans else if (!g->gcemergency)
6508e3e3a7aSWarner Losh luaD_shrinkstack(th); /* do not change stack in emergency cycle */
6510495ed39SKyle Evans return 1 + stacksize(th);
6528e3e3a7aSWarner Losh }
6538e3e3a7aSWarner Losh
6548e3e3a7aSWarner Losh
6558e3e3a7aSWarner Losh /*
6560495ed39SKyle Evans ** traverse one gray object, turning it to black.
6578e3e3a7aSWarner Losh */
propagatemark(global_State * g)6580495ed39SKyle Evans static lu_mem propagatemark (global_State *g) {
6598e3e3a7aSWarner Losh GCObject *o = g->gray;
6600495ed39SKyle Evans nw2black(o);
6610495ed39SKyle Evans g->gray = *getgclist(o); /* remove from 'gray' list */
6628e3e3a7aSWarner Losh switch (o->tt) {
6630495ed39SKyle Evans case LUA_VTABLE: return traversetable(g, gco2t(o));
6640495ed39SKyle Evans case LUA_VUSERDATA: return traverseudata(g, gco2u(o));
6650495ed39SKyle Evans case LUA_VLCL: return traverseLclosure(g, gco2lcl(o));
6660495ed39SKyle Evans case LUA_VCCL: return traverseCclosure(g, gco2ccl(o));
6670495ed39SKyle Evans case LUA_VPROTO: return traverseproto(g, gco2p(o));
6680495ed39SKyle Evans case LUA_VTHREAD: return traversethread(g, gco2th(o));
6690495ed39SKyle Evans default: lua_assert(0); return 0;
6708e3e3a7aSWarner Losh }
6718e3e3a7aSWarner Losh }
6728e3e3a7aSWarner Losh
6738e3e3a7aSWarner Losh
propagateall(global_State * g)6740495ed39SKyle Evans static lu_mem propagateall (global_State *g) {
6750495ed39SKyle Evans lu_mem tot = 0;
6760495ed39SKyle Evans while (g->gray)
6770495ed39SKyle Evans tot += propagatemark(g);
6780495ed39SKyle Evans return tot;
6798e3e3a7aSWarner Losh }
6808e3e3a7aSWarner Losh
6818e3e3a7aSWarner Losh
6820495ed39SKyle Evans /*
6830495ed39SKyle Evans ** Traverse all ephemeron tables propagating marks from keys to values.
6840495ed39SKyle Evans ** Repeat until it converges, that is, nothing new is marked. 'dir'
6850495ed39SKyle Evans ** inverts the direction of the traversals, trying to speed up
6860495ed39SKyle Evans ** convergence on chains in the same table.
6870495ed39SKyle Evans **
6880495ed39SKyle Evans */
convergeephemerons(global_State * g)6898e3e3a7aSWarner Losh static void convergeephemerons (global_State *g) {
6908e3e3a7aSWarner Losh int changed;
6910495ed39SKyle Evans int dir = 0;
6928e3e3a7aSWarner Losh do {
6938e3e3a7aSWarner Losh GCObject *w;
6948e3e3a7aSWarner Losh GCObject *next = g->ephemeron; /* get ephemeron list */
6958e3e3a7aSWarner Losh g->ephemeron = NULL; /* tables may return to this list when traversed */
6968e3e3a7aSWarner Losh changed = 0;
6970495ed39SKyle Evans while ((w = next) != NULL) { /* for each ephemeron table */
6980495ed39SKyle Evans Table *h = gco2t(w);
6990495ed39SKyle Evans next = h->gclist; /* list is rebuilt during loop */
7000495ed39SKyle Evans nw2black(h); /* out of the list (for now) */
7010495ed39SKyle Evans if (traverseephemeron(g, h, dir)) { /* marked some value? */
7028e3e3a7aSWarner Losh propagateall(g); /* propagate changes */
7038e3e3a7aSWarner Losh changed = 1; /* will have to revisit all ephemeron tables */
7048e3e3a7aSWarner Losh }
7058e3e3a7aSWarner Losh }
7060495ed39SKyle Evans dir = !dir; /* invert direction next time */
7070495ed39SKyle Evans } while (changed); /* repeat until no more changes */
7088e3e3a7aSWarner Losh }
7098e3e3a7aSWarner Losh
7108e3e3a7aSWarner Losh /* }====================================================== */
7118e3e3a7aSWarner Losh
7128e3e3a7aSWarner Losh
7138e3e3a7aSWarner Losh /*
7148e3e3a7aSWarner Losh ** {======================================================
7158e3e3a7aSWarner Losh ** Sweep Functions
7168e3e3a7aSWarner Losh ** =======================================================
7178e3e3a7aSWarner Losh */
7188e3e3a7aSWarner Losh
7198e3e3a7aSWarner Losh
7208e3e3a7aSWarner Losh /*
7210495ed39SKyle Evans ** clear entries with unmarked keys from all weaktables in list 'l'
7228e3e3a7aSWarner Losh */
clearbykeys(global_State * g,GCObject * l)7230495ed39SKyle Evans static void clearbykeys (global_State *g, GCObject *l) {
7240495ed39SKyle Evans for (; l; l = gco2t(l)->gclist) {
7258e3e3a7aSWarner Losh Table *h = gco2t(l);
7260495ed39SKyle Evans Node *limit = gnodelast(h);
7270495ed39SKyle Evans Node *n;
7288e3e3a7aSWarner Losh for (n = gnode(h, 0); n < limit; n++) {
7290495ed39SKyle Evans if (iscleared(g, gckeyN(n))) /* unmarked key? */
7300495ed39SKyle Evans setempty(gval(n)); /* remove entry */
7310495ed39SKyle Evans if (isempty(gval(n))) /* is entry empty? */
7320495ed39SKyle Evans clearkey(n); /* clear its key */
7338e3e3a7aSWarner Losh }
7348e3e3a7aSWarner Losh }
7358e3e3a7aSWarner Losh }
7368e3e3a7aSWarner Losh
7378e3e3a7aSWarner Losh
7388e3e3a7aSWarner Losh /*
7398e3e3a7aSWarner Losh ** clear entries with unmarked values from all weaktables in list 'l' up
7408e3e3a7aSWarner Losh ** to element 'f'
7418e3e3a7aSWarner Losh */
clearbyvalues(global_State * g,GCObject * l,GCObject * f)7420495ed39SKyle Evans static void clearbyvalues (global_State *g, GCObject *l, GCObject *f) {
7438e3e3a7aSWarner Losh for (; l != f; l = gco2t(l)->gclist) {
7448e3e3a7aSWarner Losh Table *h = gco2t(l);
7458e3e3a7aSWarner Losh Node *n, *limit = gnodelast(h);
7468e3e3a7aSWarner Losh unsigned int i;
7470495ed39SKyle Evans unsigned int asize = luaH_realasize(h);
7480495ed39SKyle Evans for (i = 0; i < asize; i++) {
7498e3e3a7aSWarner Losh TValue *o = &h->array[i];
7500495ed39SKyle Evans if (iscleared(g, gcvalueN(o))) /* value was collected? */
7510495ed39SKyle Evans setempty(o); /* remove entry */
7528e3e3a7aSWarner Losh }
7538e3e3a7aSWarner Losh for (n = gnode(h, 0); n < limit; n++) {
7540495ed39SKyle Evans if (iscleared(g, gcvalueN(gval(n)))) /* unmarked value? */
7550495ed39SKyle Evans setempty(gval(n)); /* remove entry */
7560495ed39SKyle Evans if (isempty(gval(n))) /* is entry empty? */
7570495ed39SKyle Evans clearkey(n); /* clear its key */
7588e3e3a7aSWarner Losh }
7598e3e3a7aSWarner Losh }
7608e3e3a7aSWarner Losh }
7618e3e3a7aSWarner Losh
7628e3e3a7aSWarner Losh
freeupval(lua_State * L,UpVal * uv)7630495ed39SKyle Evans static void freeupval (lua_State *L, UpVal *uv) {
7640495ed39SKyle Evans if (upisopen(uv))
7650495ed39SKyle Evans luaF_unlinkupval(uv);
7668e3e3a7aSWarner Losh luaM_free(L, uv);
7678e3e3a7aSWarner Losh }
7688e3e3a7aSWarner Losh
7698e3e3a7aSWarner Losh
freeobj(lua_State * L,GCObject * o)7708e3e3a7aSWarner Losh static void freeobj (lua_State *L, GCObject *o) {
7718e3e3a7aSWarner Losh switch (o->tt) {
7720495ed39SKyle Evans case LUA_VPROTO:
7730495ed39SKyle Evans luaF_freeproto(L, gco2p(o));
7740495ed39SKyle Evans break;
7750495ed39SKyle Evans case LUA_VUPVAL:
7760495ed39SKyle Evans freeupval(L, gco2upv(o));
7770495ed39SKyle Evans break;
7780495ed39SKyle Evans case LUA_VLCL: {
7790495ed39SKyle Evans LClosure *cl = gco2lcl(o);
7800495ed39SKyle Evans luaM_freemem(L, cl, sizeLclosure(cl->nupvalues));
7818e3e3a7aSWarner Losh break;
7828e3e3a7aSWarner Losh }
7830495ed39SKyle Evans case LUA_VCCL: {
7840495ed39SKyle Evans CClosure *cl = gco2ccl(o);
7850495ed39SKyle Evans luaM_freemem(L, cl, sizeCclosure(cl->nupvalues));
7868e3e3a7aSWarner Losh break;
7878e3e3a7aSWarner Losh }
7880495ed39SKyle Evans case LUA_VTABLE:
7890495ed39SKyle Evans luaH_free(L, gco2t(o));
7908e3e3a7aSWarner Losh break;
7910495ed39SKyle Evans case LUA_VTHREAD:
7920495ed39SKyle Evans luaE_freethread(L, gco2th(o));
7930495ed39SKyle Evans break;
7940495ed39SKyle Evans case LUA_VUSERDATA: {
7950495ed39SKyle Evans Udata *u = gco2u(o);
7960495ed39SKyle Evans luaM_freemem(L, o, sizeudata(u->nuvalue, u->len));
7970495ed39SKyle Evans break;
7980495ed39SKyle Evans }
7990495ed39SKyle Evans case LUA_VSHRSTR: {
8000495ed39SKyle Evans TString *ts = gco2ts(o);
8010495ed39SKyle Evans luaS_remove(L, ts); /* remove it from hash table */
8020495ed39SKyle Evans luaM_freemem(L, ts, sizelstring(ts->shrlen));
8030495ed39SKyle Evans break;
8040495ed39SKyle Evans }
8050495ed39SKyle Evans case LUA_VLNGSTR: {
8060495ed39SKyle Evans TString *ts = gco2ts(o);
8070495ed39SKyle Evans luaM_freemem(L, ts, sizelstring(ts->u.lnglen));
8088e3e3a7aSWarner Losh break;
8098e3e3a7aSWarner Losh }
8108e3e3a7aSWarner Losh default: lua_assert(0);
8118e3e3a7aSWarner Losh }
8128e3e3a7aSWarner Losh }
8138e3e3a7aSWarner Losh
8148e3e3a7aSWarner Losh
8158e3e3a7aSWarner Losh /*
8160495ed39SKyle Evans ** sweep at most 'countin' elements from a list of GCObjects erasing dead
8178e3e3a7aSWarner Losh ** objects, where a dead object is one marked with the old (non current)
8188e3e3a7aSWarner Losh ** white; change all non-dead objects back to white, preparing for next
8198e3e3a7aSWarner Losh ** collection cycle. Return where to continue the traversal or NULL if
8200495ed39SKyle Evans ** list is finished. ('*countout' gets the number of elements traversed.)
8218e3e3a7aSWarner Losh */
sweeplist(lua_State * L,GCObject ** p,int countin,int * countout)8220495ed39SKyle Evans static GCObject **sweeplist (lua_State *L, GCObject **p, int countin,
8230495ed39SKyle Evans int *countout) {
8248e3e3a7aSWarner Losh global_State *g = G(L);
8258e3e3a7aSWarner Losh int ow = otherwhite(g);
8260495ed39SKyle Evans int i;
8278e3e3a7aSWarner Losh int white = luaC_white(g); /* current white */
8280495ed39SKyle Evans for (i = 0; *p != NULL && i < countin; i++) {
8298e3e3a7aSWarner Losh GCObject *curr = *p;
8308e3e3a7aSWarner Losh int marked = curr->marked;
8318e3e3a7aSWarner Losh if (isdeadm(ow, marked)) { /* is 'curr' dead? */
8328e3e3a7aSWarner Losh *p = curr->next; /* remove 'curr' from list */
8338e3e3a7aSWarner Losh freeobj(L, curr); /* erase 'curr' */
8348e3e3a7aSWarner Losh }
8358e3e3a7aSWarner Losh else { /* change mark to 'white' */
8360495ed39SKyle Evans curr->marked = cast_byte((marked & ~maskgcbits) | white);
8378e3e3a7aSWarner Losh p = &curr->next; /* go to next element */
8388e3e3a7aSWarner Losh }
8398e3e3a7aSWarner Losh }
8400495ed39SKyle Evans if (countout)
8410495ed39SKyle Evans *countout = i; /* number of elements traversed */
8428e3e3a7aSWarner Losh return (*p == NULL) ? NULL : p;
8438e3e3a7aSWarner Losh }
8448e3e3a7aSWarner Losh
8458e3e3a7aSWarner Losh
8468e3e3a7aSWarner Losh /*
8478e3e3a7aSWarner Losh ** sweep a list until a live object (or end of list)
8488e3e3a7aSWarner Losh */
sweeptolive(lua_State * L,GCObject ** p)8498e3e3a7aSWarner Losh static GCObject **sweeptolive (lua_State *L, GCObject **p) {
8508e3e3a7aSWarner Losh GCObject **old = p;
8518e3e3a7aSWarner Losh do {
8520495ed39SKyle Evans p = sweeplist(L, p, 1, NULL);
8538e3e3a7aSWarner Losh } while (p == old);
8548e3e3a7aSWarner Losh return p;
8558e3e3a7aSWarner Losh }
8568e3e3a7aSWarner Losh
8578e3e3a7aSWarner Losh /* }====================================================== */
8588e3e3a7aSWarner Losh
8598e3e3a7aSWarner Losh
8608e3e3a7aSWarner Losh /*
8618e3e3a7aSWarner Losh ** {======================================================
8628e3e3a7aSWarner Losh ** Finalization
8638e3e3a7aSWarner Losh ** =======================================================
8648e3e3a7aSWarner Losh */
8658e3e3a7aSWarner Losh
8668e3e3a7aSWarner Losh /*
8670495ed39SKyle Evans ** If possible, shrink string table.
8688e3e3a7aSWarner Losh */
checkSizes(lua_State * L,global_State * g)8698e3e3a7aSWarner Losh static void checkSizes (lua_State *L, global_State *g) {
8700495ed39SKyle Evans if (!g->gcemergency) {
8710495ed39SKyle Evans if (g->strt.nuse < g->strt.size / 4) { /* string table too big? */
8728e3e3a7aSWarner Losh l_mem olddebt = g->GCdebt;
8730495ed39SKyle Evans luaS_resize(L, g->strt.size / 2);
8740495ed39SKyle Evans g->GCestimate += g->GCdebt - olddebt; /* correct estimate */
8750495ed39SKyle Evans }
8768e3e3a7aSWarner Losh }
8778e3e3a7aSWarner Losh }
8788e3e3a7aSWarner Losh
8798e3e3a7aSWarner Losh
8800495ed39SKyle Evans /*
8810495ed39SKyle Evans ** Get the next udata to be finalized from the 'tobefnz' list, and
8820495ed39SKyle Evans ** link it back into the 'allgc' list.
8830495ed39SKyle Evans */
udata2finalize(global_State * g)8848e3e3a7aSWarner Losh static GCObject *udata2finalize (global_State *g) {
8858e3e3a7aSWarner Losh GCObject *o = g->tobefnz; /* get first element */
8868e3e3a7aSWarner Losh lua_assert(tofinalize(o));
8878e3e3a7aSWarner Losh g->tobefnz = o->next; /* remove it from 'tobefnz' list */
8888e3e3a7aSWarner Losh o->next = g->allgc; /* return it to 'allgc' list */
8898e3e3a7aSWarner Losh g->allgc = o;
8908e3e3a7aSWarner Losh resetbit(o->marked, FINALIZEDBIT); /* object is "normal" again */
8918e3e3a7aSWarner Losh if (issweepphase(g))
8928e3e3a7aSWarner Losh makewhite(g, o); /* "sweep" object */
8930495ed39SKyle Evans else if (getage(o) == G_OLD1)
8940495ed39SKyle Evans g->firstold1 = o; /* it is the first OLD1 object in the list */
8958e3e3a7aSWarner Losh return o;
8968e3e3a7aSWarner Losh }
8978e3e3a7aSWarner Losh
8988e3e3a7aSWarner Losh
dothecall(lua_State * L,void * ud)8998e3e3a7aSWarner Losh static void dothecall (lua_State *L, void *ud) {
9008e3e3a7aSWarner Losh UNUSED(ud);
901*a9490b81SWarner Losh luaD_callnoyield(L, L->top.p - 2, 0);
9028e3e3a7aSWarner Losh }
9038e3e3a7aSWarner Losh
9048e3e3a7aSWarner Losh
GCTM(lua_State * L)9050495ed39SKyle Evans static void GCTM (lua_State *L) {
9068e3e3a7aSWarner Losh global_State *g = G(L);
9078e3e3a7aSWarner Losh const TValue *tm;
9088e3e3a7aSWarner Losh TValue v;
9090495ed39SKyle Evans lua_assert(!g->gcemergency);
9108e3e3a7aSWarner Losh setgcovalue(L, &v, udata2finalize(g));
9118e3e3a7aSWarner Losh tm = luaT_gettmbyobj(L, &v, TM_GC);
9120495ed39SKyle Evans if (!notm(tm)) { /* is there a finalizer? */
9138e3e3a7aSWarner Losh int status;
9148e3e3a7aSWarner Losh lu_byte oldah = L->allowhook;
9158c784bb8SWarner Losh int oldgcstp = g->gcstp;
9168c784bb8SWarner Losh g->gcstp |= GCSTPGC; /* avoid GC steps */
9178e3e3a7aSWarner Losh L->allowhook = 0; /* stop debug hooks during GC metamethod */
918*a9490b81SWarner Losh setobj2s(L, L->top.p++, tm); /* push finalizer... */
919*a9490b81SWarner Losh setobj2s(L, L->top.p++, &v); /* ... and its argument */
9208e3e3a7aSWarner Losh L->ci->callstatus |= CIST_FIN; /* will run a finalizer */
921*a9490b81SWarner Losh status = luaD_pcall(L, dothecall, NULL, savestack(L, L->top.p - 2), 0);
9228e3e3a7aSWarner Losh L->ci->callstatus &= ~CIST_FIN; /* not running a finalizer anymore */
9238e3e3a7aSWarner Losh L->allowhook = oldah; /* restore hooks */
9248c784bb8SWarner Losh g->gcstp = oldgcstp; /* restore state */
9258c784bb8SWarner Losh if (l_unlikely(status != LUA_OK)) { /* error while running __gc? */
9268c784bb8SWarner Losh luaE_warnerror(L, "__gc");
927*a9490b81SWarner Losh L->top.p--; /* pops error object */
9288e3e3a7aSWarner Losh }
9298e3e3a7aSWarner Losh }
9308e3e3a7aSWarner Losh }
9318e3e3a7aSWarner Losh
9328e3e3a7aSWarner Losh
9338e3e3a7aSWarner Losh /*
9340495ed39SKyle Evans ** Call a few finalizers
9358e3e3a7aSWarner Losh */
runafewfinalizers(lua_State * L,int n)9360495ed39SKyle Evans static int runafewfinalizers (lua_State *L, int n) {
9378e3e3a7aSWarner Losh global_State *g = G(L);
9380495ed39SKyle Evans int i;
9390495ed39SKyle Evans for (i = 0; i < n && g->tobefnz; i++)
9400495ed39SKyle Evans GCTM(L); /* call one finalizer */
9418e3e3a7aSWarner Losh return i;
9428e3e3a7aSWarner Losh }
9438e3e3a7aSWarner Losh
9448e3e3a7aSWarner Losh
9458e3e3a7aSWarner Losh /*
9468e3e3a7aSWarner Losh ** call all pending finalizers
9478e3e3a7aSWarner Losh */
callallpendingfinalizers(lua_State * L)9488e3e3a7aSWarner Losh static void callallpendingfinalizers (lua_State *L) {
9498e3e3a7aSWarner Losh global_State *g = G(L);
9508e3e3a7aSWarner Losh while (g->tobefnz)
9510495ed39SKyle Evans GCTM(L);
9528e3e3a7aSWarner Losh }
9538e3e3a7aSWarner Losh
9548e3e3a7aSWarner Losh
9558e3e3a7aSWarner Losh /*
9568e3e3a7aSWarner Losh ** find last 'next' field in list 'p' list (to add elements in its end)
9578e3e3a7aSWarner Losh */
findlast(GCObject ** p)9588e3e3a7aSWarner Losh static GCObject **findlast (GCObject **p) {
9598e3e3a7aSWarner Losh while (*p != NULL)
9608e3e3a7aSWarner Losh p = &(*p)->next;
9618e3e3a7aSWarner Losh return p;
9628e3e3a7aSWarner Losh }
9638e3e3a7aSWarner Losh
9648e3e3a7aSWarner Losh
9658e3e3a7aSWarner Losh /*
9660495ed39SKyle Evans ** Move all unreachable objects (or 'all' objects) that need
9670495ed39SKyle Evans ** finalization from list 'finobj' to list 'tobefnz' (to be finalized).
9680495ed39SKyle Evans ** (Note that objects after 'finobjold1' cannot be white, so they
9690495ed39SKyle Evans ** don't need to be traversed. In incremental mode, 'finobjold1' is NULL,
9700495ed39SKyle Evans ** so the whole list is traversed.)
9718e3e3a7aSWarner Losh */
separatetobefnz(global_State * g,int all)9728e3e3a7aSWarner Losh static void separatetobefnz (global_State *g, int all) {
9738e3e3a7aSWarner Losh GCObject *curr;
9748e3e3a7aSWarner Losh GCObject **p = &g->finobj;
9758e3e3a7aSWarner Losh GCObject **lastnext = findlast(&g->tobefnz);
9760495ed39SKyle Evans while ((curr = *p) != g->finobjold1) { /* traverse all finalizable objects */
9778e3e3a7aSWarner Losh lua_assert(tofinalize(curr));
9788e3e3a7aSWarner Losh if (!(iswhite(curr) || all)) /* not being collected? */
9798e3e3a7aSWarner Losh p = &curr->next; /* don't bother with it */
9808e3e3a7aSWarner Losh else {
9810495ed39SKyle Evans if (curr == g->finobjsur) /* removing 'finobjsur'? */
9820495ed39SKyle Evans g->finobjsur = curr->next; /* correct it */
9838e3e3a7aSWarner Losh *p = curr->next; /* remove 'curr' from 'finobj' list */
9848e3e3a7aSWarner Losh curr->next = *lastnext; /* link at the end of 'tobefnz' list */
9858e3e3a7aSWarner Losh *lastnext = curr;
9868e3e3a7aSWarner Losh lastnext = &curr->next;
9878e3e3a7aSWarner Losh }
9888e3e3a7aSWarner Losh }
9898e3e3a7aSWarner Losh }
9908e3e3a7aSWarner Losh
9918e3e3a7aSWarner Losh
9928e3e3a7aSWarner Losh /*
9930495ed39SKyle Evans ** If pointer 'p' points to 'o', move it to the next element.
9940495ed39SKyle Evans */
checkpointer(GCObject ** p,GCObject * o)9950495ed39SKyle Evans static void checkpointer (GCObject **p, GCObject *o) {
9960495ed39SKyle Evans if (o == *p)
9970495ed39SKyle Evans *p = o->next;
9980495ed39SKyle Evans }
9990495ed39SKyle Evans
10000495ed39SKyle Evans
10010495ed39SKyle Evans /*
10020495ed39SKyle Evans ** Correct pointers to objects inside 'allgc' list when
10030495ed39SKyle Evans ** object 'o' is being removed from the list.
10040495ed39SKyle Evans */
correctpointers(global_State * g,GCObject * o)10050495ed39SKyle Evans static void correctpointers (global_State *g, GCObject *o) {
10060495ed39SKyle Evans checkpointer(&g->survival, o);
10070495ed39SKyle Evans checkpointer(&g->old1, o);
10080495ed39SKyle Evans checkpointer(&g->reallyold, o);
10090495ed39SKyle Evans checkpointer(&g->firstold1, o);
10100495ed39SKyle Evans }
10110495ed39SKyle Evans
10120495ed39SKyle Evans
10130495ed39SKyle Evans /*
10148e3e3a7aSWarner Losh ** if object 'o' has a finalizer, remove it from 'allgc' list (must
10158e3e3a7aSWarner Losh ** search the list to find it) and link it in 'finobj' list.
10168e3e3a7aSWarner Losh */
luaC_checkfinalizer(lua_State * L,GCObject * o,Table * mt)10178e3e3a7aSWarner Losh void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) {
10188e3e3a7aSWarner Losh global_State *g = G(L);
10198e3e3a7aSWarner Losh if (tofinalize(o) || /* obj. is already marked... */
10208c784bb8SWarner Losh gfasttm(g, mt, TM_GC) == NULL || /* or has no finalizer... */
10218c784bb8SWarner Losh (g->gcstp & GCSTPCLS)) /* or closing state? */
10228e3e3a7aSWarner Losh return; /* nothing to be done */
10238e3e3a7aSWarner Losh else { /* move 'o' to 'finobj' list */
10248e3e3a7aSWarner Losh GCObject **p;
10258e3e3a7aSWarner Losh if (issweepphase(g)) {
10268e3e3a7aSWarner Losh makewhite(g, o); /* "sweep" object 'o' */
10278e3e3a7aSWarner Losh if (g->sweepgc == &o->next) /* should not remove 'sweepgc' object */
10288e3e3a7aSWarner Losh g->sweepgc = sweeptolive(L, g->sweepgc); /* change 'sweepgc' */
10298e3e3a7aSWarner Losh }
10300495ed39SKyle Evans else
10310495ed39SKyle Evans correctpointers(g, o);
10328e3e3a7aSWarner Losh /* search for pointer pointing to 'o' */
10338e3e3a7aSWarner Losh for (p = &g->allgc; *p != o; p = &(*p)->next) { /* empty */ }
10348e3e3a7aSWarner Losh *p = o->next; /* remove 'o' from 'allgc' list */
10358e3e3a7aSWarner Losh o->next = g->finobj; /* link it in 'finobj' list */
10368e3e3a7aSWarner Losh g->finobj = o;
10378e3e3a7aSWarner Losh l_setbit(o->marked, FINALIZEDBIT); /* mark it as such */
10388e3e3a7aSWarner Losh }
10398e3e3a7aSWarner Losh }
10408e3e3a7aSWarner Losh
10418e3e3a7aSWarner Losh /* }====================================================== */
10428e3e3a7aSWarner Losh
10438e3e3a7aSWarner Losh
10440495ed39SKyle Evans /*
10450495ed39SKyle Evans ** {======================================================
10460495ed39SKyle Evans ** Generational Collector
10470495ed39SKyle Evans ** =======================================================
10480495ed39SKyle Evans */
10490495ed39SKyle Evans
1050*a9490b81SWarner Losh
1051*a9490b81SWarner Losh /*
1052*a9490b81SWarner Losh ** Set the "time" to wait before starting a new GC cycle; cycle will
1053*a9490b81SWarner Losh ** start when memory use hits the threshold of ('estimate' * pause /
1054*a9490b81SWarner Losh ** PAUSEADJ). (Division by 'estimate' should be OK: it cannot be zero,
1055*a9490b81SWarner Losh ** because Lua cannot even start with less than PAUSEADJ bytes).
1056*a9490b81SWarner Losh */
setpause(global_State * g)1057*a9490b81SWarner Losh static void setpause (global_State *g) {
1058*a9490b81SWarner Losh l_mem threshold, debt;
1059*a9490b81SWarner Losh int pause = getgcparam(g->gcpause);
1060*a9490b81SWarner Losh l_mem estimate = g->GCestimate / PAUSEADJ; /* adjust 'estimate' */
1061*a9490b81SWarner Losh lua_assert(estimate > 0);
1062*a9490b81SWarner Losh threshold = (pause < MAX_LMEM / estimate) /* overflow? */
1063*a9490b81SWarner Losh ? estimate * pause /* no overflow */
1064*a9490b81SWarner Losh : MAX_LMEM; /* overflow; truncate to maximum */
1065*a9490b81SWarner Losh debt = gettotalbytes(g) - threshold;
1066*a9490b81SWarner Losh if (debt > 0) debt = 0;
1067*a9490b81SWarner Losh luaE_setdebt(g, debt);
1068*a9490b81SWarner Losh }
10690495ed39SKyle Evans
10700495ed39SKyle Evans
10710495ed39SKyle Evans /*
10720495ed39SKyle Evans ** Sweep a list of objects to enter generational mode. Deletes dead
10730495ed39SKyle Evans ** objects and turns the non dead to old. All non-dead threads---which
10740495ed39SKyle Evans ** are now old---must be in a gray list. Everything else is not in a
10750495ed39SKyle Evans ** gray list. Open upvalues are also kept gray.
10760495ed39SKyle Evans */
sweep2old(lua_State * L,GCObject ** p)10770495ed39SKyle Evans static void sweep2old (lua_State *L, GCObject **p) {
10780495ed39SKyle Evans GCObject *curr;
10790495ed39SKyle Evans global_State *g = G(L);
10800495ed39SKyle Evans while ((curr = *p) != NULL) {
10810495ed39SKyle Evans if (iswhite(curr)) { /* is 'curr' dead? */
10820495ed39SKyle Evans lua_assert(isdead(g, curr));
10830495ed39SKyle Evans *p = curr->next; /* remove 'curr' from list */
10840495ed39SKyle Evans freeobj(L, curr); /* erase 'curr' */
10850495ed39SKyle Evans }
10860495ed39SKyle Evans else { /* all surviving objects become old */
10870495ed39SKyle Evans setage(curr, G_OLD);
10880495ed39SKyle Evans if (curr->tt == LUA_VTHREAD) { /* threads must be watched */
10890495ed39SKyle Evans lua_State *th = gco2th(curr);
10900495ed39SKyle Evans linkgclist(th, g->grayagain); /* insert into 'grayagain' list */
10910495ed39SKyle Evans }
10920495ed39SKyle Evans else if (curr->tt == LUA_VUPVAL && upisopen(gco2upv(curr)))
10930495ed39SKyle Evans set2gray(curr); /* open upvalues are always gray */
10940495ed39SKyle Evans else /* everything else is black */
10950495ed39SKyle Evans nw2black(curr);
10960495ed39SKyle Evans p = &curr->next; /* go to next element */
10970495ed39SKyle Evans }
10980495ed39SKyle Evans }
10990495ed39SKyle Evans }
11000495ed39SKyle Evans
11010495ed39SKyle Evans
11020495ed39SKyle Evans /*
11030495ed39SKyle Evans ** Sweep for generational mode. Delete dead objects. (Because the
11040495ed39SKyle Evans ** collection is not incremental, there are no "new white" objects
11050495ed39SKyle Evans ** during the sweep. So, any white object must be dead.) For
11060495ed39SKyle Evans ** non-dead objects, advance their ages and clear the color of
11070495ed39SKyle Evans ** new objects. (Old objects keep their colors.)
11080495ed39SKyle Evans ** The ages of G_TOUCHED1 and G_TOUCHED2 objects cannot be advanced
11090495ed39SKyle Evans ** here, because these old-generation objects are usually not swept
11100495ed39SKyle Evans ** here. They will all be advanced in 'correctgraylist'. That function
11110495ed39SKyle Evans ** will also remove objects turned white here from any gray list.
11120495ed39SKyle Evans */
sweepgen(lua_State * L,global_State * g,GCObject ** p,GCObject * limit,GCObject ** pfirstold1)11130495ed39SKyle Evans static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p,
11140495ed39SKyle Evans GCObject *limit, GCObject **pfirstold1) {
11150495ed39SKyle Evans static const lu_byte nextage[] = {
11160495ed39SKyle Evans G_SURVIVAL, /* from G_NEW */
11170495ed39SKyle Evans G_OLD1, /* from G_SURVIVAL */
11180495ed39SKyle Evans G_OLD1, /* from G_OLD0 */
11190495ed39SKyle Evans G_OLD, /* from G_OLD1 */
11200495ed39SKyle Evans G_OLD, /* from G_OLD (do not change) */
11210495ed39SKyle Evans G_TOUCHED1, /* from G_TOUCHED1 (do not change) */
11220495ed39SKyle Evans G_TOUCHED2 /* from G_TOUCHED2 (do not change) */
11230495ed39SKyle Evans };
11240495ed39SKyle Evans int white = luaC_white(g);
11250495ed39SKyle Evans GCObject *curr;
11260495ed39SKyle Evans while ((curr = *p) != limit) {
11270495ed39SKyle Evans if (iswhite(curr)) { /* is 'curr' dead? */
11280495ed39SKyle Evans lua_assert(!isold(curr) && isdead(g, curr));
11290495ed39SKyle Evans *p = curr->next; /* remove 'curr' from list */
11300495ed39SKyle Evans freeobj(L, curr); /* erase 'curr' */
11310495ed39SKyle Evans }
11320495ed39SKyle Evans else { /* correct mark and age */
11330495ed39SKyle Evans if (getage(curr) == G_NEW) { /* new objects go back to white */
11340495ed39SKyle Evans int marked = curr->marked & ~maskgcbits; /* erase GC bits */
11350495ed39SKyle Evans curr->marked = cast_byte(marked | G_SURVIVAL | white);
11360495ed39SKyle Evans }
11370495ed39SKyle Evans else { /* all other objects will be old, and so keep their color */
11380495ed39SKyle Evans setage(curr, nextage[getage(curr)]);
11390495ed39SKyle Evans if (getage(curr) == G_OLD1 && *pfirstold1 == NULL)
11400495ed39SKyle Evans *pfirstold1 = curr; /* first OLD1 object in the list */
11410495ed39SKyle Evans }
11420495ed39SKyle Evans p = &curr->next; /* go to next element */
11430495ed39SKyle Evans }
11440495ed39SKyle Evans }
11450495ed39SKyle Evans return p;
11460495ed39SKyle Evans }
11470495ed39SKyle Evans
11480495ed39SKyle Evans
11490495ed39SKyle Evans /*
11500495ed39SKyle Evans ** Traverse a list making all its elements white and clearing their
11510495ed39SKyle Evans ** age. In incremental mode, all objects are 'new' all the time,
11520495ed39SKyle Evans ** except for fixed strings (which are always old).
11530495ed39SKyle Evans */
whitelist(global_State * g,GCObject * p)11540495ed39SKyle Evans static void whitelist (global_State *g, GCObject *p) {
11550495ed39SKyle Evans int white = luaC_white(g);
11560495ed39SKyle Evans for (; p != NULL; p = p->next)
11570495ed39SKyle Evans p->marked = cast_byte((p->marked & ~maskgcbits) | white);
11580495ed39SKyle Evans }
11590495ed39SKyle Evans
11600495ed39SKyle Evans
11610495ed39SKyle Evans /*
11620495ed39SKyle Evans ** Correct a list of gray objects. Return pointer to where rest of the
11630495ed39SKyle Evans ** list should be linked.
11640495ed39SKyle Evans ** Because this correction is done after sweeping, young objects might
11650495ed39SKyle Evans ** be turned white and still be in the list. They are only removed.
11660495ed39SKyle Evans ** 'TOUCHED1' objects are advanced to 'TOUCHED2' and remain on the list;
11670495ed39SKyle Evans ** Non-white threads also remain on the list; 'TOUCHED2' objects become
11680495ed39SKyle Evans ** regular old; they and anything else are removed from the list.
11690495ed39SKyle Evans */
correctgraylist(GCObject ** p)11700495ed39SKyle Evans static GCObject **correctgraylist (GCObject **p) {
11710495ed39SKyle Evans GCObject *curr;
11720495ed39SKyle Evans while ((curr = *p) != NULL) {
11730495ed39SKyle Evans GCObject **next = getgclist(curr);
11740495ed39SKyle Evans if (iswhite(curr))
11750495ed39SKyle Evans goto remove; /* remove all white objects */
11760495ed39SKyle Evans else if (getage(curr) == G_TOUCHED1) { /* touched in this cycle? */
11770495ed39SKyle Evans lua_assert(isgray(curr));
11780495ed39SKyle Evans nw2black(curr); /* make it black, for next barrier */
11790495ed39SKyle Evans changeage(curr, G_TOUCHED1, G_TOUCHED2);
11800495ed39SKyle Evans goto remain; /* keep it in the list and go to next element */
11810495ed39SKyle Evans }
11820495ed39SKyle Evans else if (curr->tt == LUA_VTHREAD) {
11830495ed39SKyle Evans lua_assert(isgray(curr));
11840495ed39SKyle Evans goto remain; /* keep non-white threads on the list */
11850495ed39SKyle Evans }
11860495ed39SKyle Evans else { /* everything else is removed */
11870495ed39SKyle Evans lua_assert(isold(curr)); /* young objects should be white here */
11880495ed39SKyle Evans if (getage(curr) == G_TOUCHED2) /* advance from TOUCHED2... */
11890495ed39SKyle Evans changeage(curr, G_TOUCHED2, G_OLD); /* ... to OLD */
11900495ed39SKyle Evans nw2black(curr); /* make object black (to be removed) */
11910495ed39SKyle Evans goto remove;
11920495ed39SKyle Evans }
11930495ed39SKyle Evans remove: *p = *next; continue;
11940495ed39SKyle Evans remain: p = next; continue;
11950495ed39SKyle Evans }
11960495ed39SKyle Evans return p;
11970495ed39SKyle Evans }
11980495ed39SKyle Evans
11990495ed39SKyle Evans
12000495ed39SKyle Evans /*
12010495ed39SKyle Evans ** Correct all gray lists, coalescing them into 'grayagain'.
12020495ed39SKyle Evans */
correctgraylists(global_State * g)12030495ed39SKyle Evans static void correctgraylists (global_State *g) {
12040495ed39SKyle Evans GCObject **list = correctgraylist(&g->grayagain);
12050495ed39SKyle Evans *list = g->weak; g->weak = NULL;
12060495ed39SKyle Evans list = correctgraylist(list);
12070495ed39SKyle Evans *list = g->allweak; g->allweak = NULL;
12080495ed39SKyle Evans list = correctgraylist(list);
12090495ed39SKyle Evans *list = g->ephemeron; g->ephemeron = NULL;
12100495ed39SKyle Evans correctgraylist(list);
12110495ed39SKyle Evans }
12120495ed39SKyle Evans
12130495ed39SKyle Evans
12140495ed39SKyle Evans /*
12150495ed39SKyle Evans ** Mark black 'OLD1' objects when starting a new young collection.
12160495ed39SKyle Evans ** Gray objects are already in some gray list, and so will be visited
12170495ed39SKyle Evans ** in the atomic step.
12180495ed39SKyle Evans */
markold(global_State * g,GCObject * from,GCObject * to)12190495ed39SKyle Evans static void markold (global_State *g, GCObject *from, GCObject *to) {
12200495ed39SKyle Evans GCObject *p;
12210495ed39SKyle Evans for (p = from; p != to; p = p->next) {
12220495ed39SKyle Evans if (getage(p) == G_OLD1) {
12230495ed39SKyle Evans lua_assert(!iswhite(p));
12240495ed39SKyle Evans changeage(p, G_OLD1, G_OLD); /* now they are old */
12250495ed39SKyle Evans if (isblack(p))
12260495ed39SKyle Evans reallymarkobject(g, p);
12270495ed39SKyle Evans }
12280495ed39SKyle Evans }
12290495ed39SKyle Evans }
12300495ed39SKyle Evans
12310495ed39SKyle Evans
12320495ed39SKyle Evans /*
12330495ed39SKyle Evans ** Finish a young-generation collection.
12340495ed39SKyle Evans */
finishgencycle(lua_State * L,global_State * g)12350495ed39SKyle Evans static void finishgencycle (lua_State *L, global_State *g) {
12360495ed39SKyle Evans correctgraylists(g);
12370495ed39SKyle Evans checkSizes(L, g);
12380495ed39SKyle Evans g->gcstate = GCSpropagate; /* skip restart */
12390495ed39SKyle Evans if (!g->gcemergency)
12400495ed39SKyle Evans callallpendingfinalizers(L);
12410495ed39SKyle Evans }
12420495ed39SKyle Evans
12430495ed39SKyle Evans
12440495ed39SKyle Evans /*
12450495ed39SKyle Evans ** Does a young collection. First, mark 'OLD1' objects. Then does the
12460495ed39SKyle Evans ** atomic step. Then, sweep all lists and advance pointers. Finally,
12470495ed39SKyle Evans ** finish the collection.
12480495ed39SKyle Evans */
youngcollection(lua_State * L,global_State * g)12490495ed39SKyle Evans static void youngcollection (lua_State *L, global_State *g) {
12500495ed39SKyle Evans GCObject **psurvival; /* to point to first non-dead survival object */
12510495ed39SKyle Evans GCObject *dummy; /* dummy out parameter to 'sweepgen' */
12520495ed39SKyle Evans lua_assert(g->gcstate == GCSpropagate);
12530495ed39SKyle Evans if (g->firstold1) { /* are there regular OLD1 objects? */
12540495ed39SKyle Evans markold(g, g->firstold1, g->reallyold); /* mark them */
12550495ed39SKyle Evans g->firstold1 = NULL; /* no more OLD1 objects (for now) */
12560495ed39SKyle Evans }
12570495ed39SKyle Evans markold(g, g->finobj, g->finobjrold);
12580495ed39SKyle Evans markold(g, g->tobefnz, NULL);
12590495ed39SKyle Evans atomic(L);
12600495ed39SKyle Evans
12610495ed39SKyle Evans /* sweep nursery and get a pointer to its last live element */
12620495ed39SKyle Evans g->gcstate = GCSswpallgc;
12630495ed39SKyle Evans psurvival = sweepgen(L, g, &g->allgc, g->survival, &g->firstold1);
12640495ed39SKyle Evans /* sweep 'survival' */
12650495ed39SKyle Evans sweepgen(L, g, psurvival, g->old1, &g->firstold1);
12660495ed39SKyle Evans g->reallyold = g->old1;
12670495ed39SKyle Evans g->old1 = *psurvival; /* 'survival' survivals are old now */
12680495ed39SKyle Evans g->survival = g->allgc; /* all news are survivals */
12690495ed39SKyle Evans
12700495ed39SKyle Evans /* repeat for 'finobj' lists */
12710495ed39SKyle Evans dummy = NULL; /* no 'firstold1' optimization for 'finobj' lists */
12720495ed39SKyle Evans psurvival = sweepgen(L, g, &g->finobj, g->finobjsur, &dummy);
12730495ed39SKyle Evans /* sweep 'survival' */
12740495ed39SKyle Evans sweepgen(L, g, psurvival, g->finobjold1, &dummy);
12750495ed39SKyle Evans g->finobjrold = g->finobjold1;
12760495ed39SKyle Evans g->finobjold1 = *psurvival; /* 'survival' survivals are old now */
12770495ed39SKyle Evans g->finobjsur = g->finobj; /* all news are survivals */
12780495ed39SKyle Evans
12790495ed39SKyle Evans sweepgen(L, g, &g->tobefnz, NULL, &dummy);
12800495ed39SKyle Evans finishgencycle(L, g);
12810495ed39SKyle Evans }
12820495ed39SKyle Evans
12830495ed39SKyle Evans
12840495ed39SKyle Evans /*
12850495ed39SKyle Evans ** Clears all gray lists, sweeps objects, and prepare sublists to enter
12860495ed39SKyle Evans ** generational mode. The sweeps remove dead objects and turn all
12870495ed39SKyle Evans ** surviving objects to old. Threads go back to 'grayagain'; everything
12880495ed39SKyle Evans ** else is turned black (not in any gray list).
12890495ed39SKyle Evans */
atomic2gen(lua_State * L,global_State * g)12900495ed39SKyle Evans static void atomic2gen (lua_State *L, global_State *g) {
12910495ed39SKyle Evans cleargraylists(g);
12920495ed39SKyle Evans /* sweep all elements making them old */
12930495ed39SKyle Evans g->gcstate = GCSswpallgc;
12940495ed39SKyle Evans sweep2old(L, &g->allgc);
12950495ed39SKyle Evans /* everything alive now is old */
12960495ed39SKyle Evans g->reallyold = g->old1 = g->survival = g->allgc;
12970495ed39SKyle Evans g->firstold1 = NULL; /* there are no OLD1 objects anywhere */
12980495ed39SKyle Evans
12990495ed39SKyle Evans /* repeat for 'finobj' lists */
13000495ed39SKyle Evans sweep2old(L, &g->finobj);
13010495ed39SKyle Evans g->finobjrold = g->finobjold1 = g->finobjsur = g->finobj;
13020495ed39SKyle Evans
13030495ed39SKyle Evans sweep2old(L, &g->tobefnz);
13040495ed39SKyle Evans
13050495ed39SKyle Evans g->gckind = KGC_GEN;
13060495ed39SKyle Evans g->lastatomic = 0;
13070495ed39SKyle Evans g->GCestimate = gettotalbytes(g); /* base for memory control */
13080495ed39SKyle Evans finishgencycle(L, g);
13090495ed39SKyle Evans }
13100495ed39SKyle Evans
13110495ed39SKyle Evans
13120495ed39SKyle Evans /*
1313*a9490b81SWarner Losh ** Set debt for the next minor collection, which will happen when
1314*a9490b81SWarner Losh ** memory grows 'genminormul'%.
1315*a9490b81SWarner Losh */
setminordebt(global_State * g)1316*a9490b81SWarner Losh static void setminordebt (global_State *g) {
1317*a9490b81SWarner Losh luaE_setdebt(g, -(cast(l_mem, (gettotalbytes(g) / 100)) * g->genminormul));
1318*a9490b81SWarner Losh }
1319*a9490b81SWarner Losh
1320*a9490b81SWarner Losh
1321*a9490b81SWarner Losh /*
13220495ed39SKyle Evans ** Enter generational mode. Must go until the end of an atomic cycle
13230495ed39SKyle Evans ** to ensure that all objects are correctly marked and weak tables
13240495ed39SKyle Evans ** are cleared. Then, turn all objects into old and finishes the
13250495ed39SKyle Evans ** collection.
13260495ed39SKyle Evans */
entergen(lua_State * L,global_State * g)13270495ed39SKyle Evans static lu_mem entergen (lua_State *L, global_State *g) {
13280495ed39SKyle Evans lu_mem numobjs;
13290495ed39SKyle Evans luaC_runtilstate(L, bitmask(GCSpause)); /* prepare to start a new cycle */
13300495ed39SKyle Evans luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */
13310495ed39SKyle Evans numobjs = atomic(L); /* propagates all and then do the atomic stuff */
13320495ed39SKyle Evans atomic2gen(L, g);
1333*a9490b81SWarner Losh setminordebt(g); /* set debt assuming next cycle will be minor */
13340495ed39SKyle Evans return numobjs;
13350495ed39SKyle Evans }
13360495ed39SKyle Evans
13370495ed39SKyle Evans
13380495ed39SKyle Evans /*
13390495ed39SKyle Evans ** Enter incremental mode. Turn all objects white, make all
13400495ed39SKyle Evans ** intermediate lists point to NULL (to avoid invalid pointers),
13410495ed39SKyle Evans ** and go to the pause state.
13420495ed39SKyle Evans */
enterinc(global_State * g)13430495ed39SKyle Evans static void enterinc (global_State *g) {
13440495ed39SKyle Evans whitelist(g, g->allgc);
13450495ed39SKyle Evans g->reallyold = g->old1 = g->survival = NULL;
13460495ed39SKyle Evans whitelist(g, g->finobj);
13470495ed39SKyle Evans whitelist(g, g->tobefnz);
13480495ed39SKyle Evans g->finobjrold = g->finobjold1 = g->finobjsur = NULL;
13490495ed39SKyle Evans g->gcstate = GCSpause;
13500495ed39SKyle Evans g->gckind = KGC_INC;
13510495ed39SKyle Evans g->lastatomic = 0;
13520495ed39SKyle Evans }
13530495ed39SKyle Evans
13540495ed39SKyle Evans
13550495ed39SKyle Evans /*
13560495ed39SKyle Evans ** Change collector mode to 'newmode'.
13570495ed39SKyle Evans */
luaC_changemode(lua_State * L,int newmode)13580495ed39SKyle Evans void luaC_changemode (lua_State *L, int newmode) {
13590495ed39SKyle Evans global_State *g = G(L);
13600495ed39SKyle Evans if (newmode != g->gckind) {
13610495ed39SKyle Evans if (newmode == KGC_GEN) /* entering generational mode? */
13620495ed39SKyle Evans entergen(L, g);
13630495ed39SKyle Evans else
13640495ed39SKyle Evans enterinc(g); /* entering incremental mode */
13650495ed39SKyle Evans }
13660495ed39SKyle Evans g->lastatomic = 0;
13670495ed39SKyle Evans }
13680495ed39SKyle Evans
13690495ed39SKyle Evans
13700495ed39SKyle Evans /*
13710495ed39SKyle Evans ** Does a full collection in generational mode.
13720495ed39SKyle Evans */
fullgen(lua_State * L,global_State * g)13730495ed39SKyle Evans static lu_mem fullgen (lua_State *L, global_State *g) {
13740495ed39SKyle Evans enterinc(g);
13750495ed39SKyle Evans return entergen(L, g);
13760495ed39SKyle Evans }
13770495ed39SKyle Evans
13780495ed39SKyle Evans
13790495ed39SKyle Evans /*
13800495ed39SKyle Evans ** Does a major collection after last collection was a "bad collection".
13810495ed39SKyle Evans **
13820495ed39SKyle Evans ** When the program is building a big structure, it allocates lots of
13830495ed39SKyle Evans ** memory but generates very little garbage. In those scenarios,
13840495ed39SKyle Evans ** the generational mode just wastes time doing small collections, and
13850495ed39SKyle Evans ** major collections are frequently what we call a "bad collection", a
13860495ed39SKyle Evans ** collection that frees too few objects. To avoid the cost of switching
13870495ed39SKyle Evans ** between generational mode and the incremental mode needed for full
13880495ed39SKyle Evans ** (major) collections, the collector tries to stay in incremental mode
13890495ed39SKyle Evans ** after a bad collection, and to switch back to generational mode only
13900495ed39SKyle Evans ** after a "good" collection (one that traverses less than 9/8 objects
13910495ed39SKyle Evans ** of the previous one).
13920495ed39SKyle Evans ** The collector must choose whether to stay in incremental mode or to
13930495ed39SKyle Evans ** switch back to generational mode before sweeping. At this point, it
13940495ed39SKyle Evans ** does not know the real memory in use, so it cannot use memory to
13950495ed39SKyle Evans ** decide whether to return to generational mode. Instead, it uses the
13960495ed39SKyle Evans ** number of objects traversed (returned by 'atomic') as a proxy. The
13970495ed39SKyle Evans ** field 'g->lastatomic' keeps this count from the last collection.
13980495ed39SKyle Evans ** ('g->lastatomic != 0' also means that the last collection was bad.)
13990495ed39SKyle Evans */
stepgenfull(lua_State * L,global_State * g)14000495ed39SKyle Evans static void stepgenfull (lua_State *L, global_State *g) {
14010495ed39SKyle Evans lu_mem newatomic; /* count of traversed objects */
14020495ed39SKyle Evans lu_mem lastatomic = g->lastatomic; /* count from last collection */
14030495ed39SKyle Evans if (g->gckind == KGC_GEN) /* still in generational mode? */
14040495ed39SKyle Evans enterinc(g); /* enter incremental mode */
14050495ed39SKyle Evans luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */
14060495ed39SKyle Evans newatomic = atomic(L); /* mark everybody */
14070495ed39SKyle Evans if (newatomic < lastatomic + (lastatomic >> 3)) { /* good collection? */
14080495ed39SKyle Evans atomic2gen(L, g); /* return to generational mode */
14090495ed39SKyle Evans setminordebt(g);
14100495ed39SKyle Evans }
14110495ed39SKyle Evans else { /* another bad collection; stay in incremental mode */
14120495ed39SKyle Evans g->GCestimate = gettotalbytes(g); /* first estimate */;
14130495ed39SKyle Evans entersweep(L);
14140495ed39SKyle Evans luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */
14150495ed39SKyle Evans setpause(g);
14160495ed39SKyle Evans g->lastatomic = newatomic;
14170495ed39SKyle Evans }
14180495ed39SKyle Evans }
14190495ed39SKyle Evans
14200495ed39SKyle Evans
14210495ed39SKyle Evans /*
14220495ed39SKyle Evans ** Does a generational "step".
14230495ed39SKyle Evans ** Usually, this means doing a minor collection and setting the debt to
14240495ed39SKyle Evans ** make another collection when memory grows 'genminormul'% larger.
14250495ed39SKyle Evans **
14260495ed39SKyle Evans ** However, there are exceptions. If memory grows 'genmajormul'%
14270495ed39SKyle Evans ** larger than it was at the end of the last major collection (kept
14280495ed39SKyle Evans ** in 'g->GCestimate'), the function does a major collection. At the
14290495ed39SKyle Evans ** end, it checks whether the major collection was able to free a
14300495ed39SKyle Evans ** decent amount of memory (at least half the growth in memory since
14310495ed39SKyle Evans ** previous major collection). If so, the collector keeps its state,
14320495ed39SKyle Evans ** and the next collection will probably be minor again. Otherwise,
14330495ed39SKyle Evans ** we have what we call a "bad collection". In that case, set the field
14340495ed39SKyle Evans ** 'g->lastatomic' to signal that fact, so that the next collection will
14350495ed39SKyle Evans ** go to 'stepgenfull'.
14360495ed39SKyle Evans **
14370495ed39SKyle Evans ** 'GCdebt <= 0' means an explicit call to GC step with "size" zero;
14380495ed39SKyle Evans ** in that case, do a minor collection.
14390495ed39SKyle Evans */
genstep(lua_State * L,global_State * g)14400495ed39SKyle Evans static void genstep (lua_State *L, global_State *g) {
14410495ed39SKyle Evans if (g->lastatomic != 0) /* last collection was a bad one? */
14420495ed39SKyle Evans stepgenfull(L, g); /* do a full step */
14430495ed39SKyle Evans else {
14440495ed39SKyle Evans lu_mem majorbase = g->GCestimate; /* memory after last major collection */
14450495ed39SKyle Evans lu_mem majorinc = (majorbase / 100) * getgcparam(g->genmajormul);
14460495ed39SKyle Evans if (g->GCdebt > 0 && gettotalbytes(g) > majorbase + majorinc) {
14470495ed39SKyle Evans lu_mem numobjs = fullgen(L, g); /* do a major collection */
14480495ed39SKyle Evans if (gettotalbytes(g) < majorbase + (majorinc / 2)) {
14490495ed39SKyle Evans /* collected at least half of memory growth since last major
1450*a9490b81SWarner Losh collection; keep doing minor collections. */
1451*a9490b81SWarner Losh lua_assert(g->lastatomic == 0);
14520495ed39SKyle Evans }
14530495ed39SKyle Evans else { /* bad collection */
14540495ed39SKyle Evans g->lastatomic = numobjs; /* signal that last collection was bad */
14550495ed39SKyle Evans setpause(g); /* do a long wait for next (major) collection */
14560495ed39SKyle Evans }
14570495ed39SKyle Evans }
14580495ed39SKyle Evans else { /* regular case; do a minor collection */
14590495ed39SKyle Evans youngcollection(L, g);
14600495ed39SKyle Evans setminordebt(g);
14610495ed39SKyle Evans g->GCestimate = majorbase; /* preserve base value */
14620495ed39SKyle Evans }
14630495ed39SKyle Evans }
14640495ed39SKyle Evans lua_assert(isdecGCmodegen(g));
14650495ed39SKyle Evans }
14660495ed39SKyle Evans
14670495ed39SKyle Evans /* }====================================================== */
14680495ed39SKyle Evans
14698e3e3a7aSWarner Losh
14708e3e3a7aSWarner Losh /*
14718e3e3a7aSWarner Losh ** {======================================================
14728e3e3a7aSWarner Losh ** GC control
14738e3e3a7aSWarner Losh ** =======================================================
14748e3e3a7aSWarner Losh */
14758e3e3a7aSWarner Losh
14768e3e3a7aSWarner Losh
14778e3e3a7aSWarner Losh /*
14788e3e3a7aSWarner Losh ** Enter first sweep phase.
14790495ed39SKyle Evans ** The call to 'sweeptolive' makes the pointer point to an object
14808e3e3a7aSWarner Losh ** inside the list (instead of to the header), so that the real sweep do
14818e3e3a7aSWarner Losh ** not need to skip objects created between "now" and the start of the
14828e3e3a7aSWarner Losh ** real sweep.
14838e3e3a7aSWarner Losh */
entersweep(lua_State * L)14848e3e3a7aSWarner Losh static void entersweep (lua_State *L) {
14858e3e3a7aSWarner Losh global_State *g = G(L);
14868e3e3a7aSWarner Losh g->gcstate = GCSswpallgc;
14878e3e3a7aSWarner Losh lua_assert(g->sweepgc == NULL);
14880495ed39SKyle Evans g->sweepgc = sweeptolive(L, &g->allgc);
14898e3e3a7aSWarner Losh }
14908e3e3a7aSWarner Losh
14918e3e3a7aSWarner Losh
14920495ed39SKyle Evans /*
14930495ed39SKyle Evans ** Delete all objects in list 'p' until (but not including) object
14940495ed39SKyle Evans ** 'limit'.
14950495ed39SKyle Evans */
deletelist(lua_State * L,GCObject * p,GCObject * limit)14960495ed39SKyle Evans static void deletelist (lua_State *L, GCObject *p, GCObject *limit) {
14970495ed39SKyle Evans while (p != limit) {
14980495ed39SKyle Evans GCObject *next = p->next;
14990495ed39SKyle Evans freeobj(L, p);
15000495ed39SKyle Evans p = next;
15010495ed39SKyle Evans }
15020495ed39SKyle Evans }
15030495ed39SKyle Evans
15040495ed39SKyle Evans
15050495ed39SKyle Evans /*
15060495ed39SKyle Evans ** Call all finalizers of the objects in the given Lua state, and
15070495ed39SKyle Evans ** then free all objects, except for the main thread.
15080495ed39SKyle Evans */
luaC_freeallobjects(lua_State * L)15098e3e3a7aSWarner Losh void luaC_freeallobjects (lua_State *L) {
15108e3e3a7aSWarner Losh global_State *g = G(L);
15118c784bb8SWarner Losh g->gcstp = GCSTPCLS; /* no extra finalizers after here */
15120495ed39SKyle Evans luaC_changemode(L, KGC_INC);
15138e3e3a7aSWarner Losh separatetobefnz(g, 1); /* separate all objects with finalizers */
15148e3e3a7aSWarner Losh lua_assert(g->finobj == NULL);
15158e3e3a7aSWarner Losh callallpendingfinalizers(L);
15160495ed39SKyle Evans deletelist(L, g->allgc, obj2gco(g->mainthread));
15178c784bb8SWarner Losh lua_assert(g->finobj == NULL); /* no new finalizers */
15180495ed39SKyle Evans deletelist(L, g->fixedgc, NULL); /* collect fixed objects */
15198e3e3a7aSWarner Losh lua_assert(g->strt.nuse == 0);
15208e3e3a7aSWarner Losh }
15218e3e3a7aSWarner Losh
15228e3e3a7aSWarner Losh
atomic(lua_State * L)15230495ed39SKyle Evans static lu_mem atomic (lua_State *L) {
15248e3e3a7aSWarner Losh global_State *g = G(L);
15250495ed39SKyle Evans lu_mem work = 0;
15268e3e3a7aSWarner Losh GCObject *origweak, *origall;
15278e3e3a7aSWarner Losh GCObject *grayagain = g->grayagain; /* save original list */
15280495ed39SKyle Evans g->grayagain = NULL;
15298e3e3a7aSWarner Losh lua_assert(g->ephemeron == NULL && g->weak == NULL);
15308e3e3a7aSWarner Losh lua_assert(!iswhite(g->mainthread));
15310495ed39SKyle Evans g->gcstate = GCSatomic;
15328e3e3a7aSWarner Losh markobject(g, L); /* mark running thread */
15338e3e3a7aSWarner Losh /* registry and global metatables may be changed by API */
15348e3e3a7aSWarner Losh markvalue(g, &g->l_registry);
15358e3e3a7aSWarner Losh markmt(g); /* mark global metatables */
15360495ed39SKyle Evans work += propagateall(g); /* empties 'gray' list */
15378e3e3a7aSWarner Losh /* remark occasional upvalues of (maybe) dead threads */
15380495ed39SKyle Evans work += remarkupvals(g);
15390495ed39SKyle Evans work += propagateall(g); /* propagate changes */
15408e3e3a7aSWarner Losh g->gray = grayagain;
15410495ed39SKyle Evans work += propagateall(g); /* traverse 'grayagain' list */
15428e3e3a7aSWarner Losh convergeephemerons(g);
15438e3e3a7aSWarner Losh /* at this point, all strongly accessible objects are marked. */
15448e3e3a7aSWarner Losh /* Clear values from weak tables, before checking finalizers */
15450495ed39SKyle Evans clearbyvalues(g, g->weak, NULL);
15460495ed39SKyle Evans clearbyvalues(g, g->allweak, NULL);
15478e3e3a7aSWarner Losh origweak = g->weak; origall = g->allweak;
15488e3e3a7aSWarner Losh separatetobefnz(g, 0); /* separate objects to be finalized */
15490495ed39SKyle Evans work += markbeingfnz(g); /* mark objects that will be finalized */
15500495ed39SKyle Evans work += propagateall(g); /* remark, to propagate 'resurrection' */
15518e3e3a7aSWarner Losh convergeephemerons(g);
15528e3e3a7aSWarner Losh /* at this point, all resurrected objects are marked. */
15538e3e3a7aSWarner Losh /* remove dead objects from weak tables */
15540495ed39SKyle Evans clearbykeys(g, g->ephemeron); /* clear keys from all ephemeron tables */
15550495ed39SKyle Evans clearbykeys(g, g->allweak); /* clear keys from all 'allweak' tables */
15568e3e3a7aSWarner Losh /* clear values from resurrected weak tables */
15570495ed39SKyle Evans clearbyvalues(g, g->weak, origweak);
15580495ed39SKyle Evans clearbyvalues(g, g->allweak, origall);
15598e3e3a7aSWarner Losh luaS_clearcache(g);
15608e3e3a7aSWarner Losh g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */
15610495ed39SKyle Evans lua_assert(g->gray == NULL);
15620495ed39SKyle Evans return work; /* estimate of slots marked by 'atomic' */
15638e3e3a7aSWarner Losh }
15648e3e3a7aSWarner Losh
15658e3e3a7aSWarner Losh
sweepstep(lua_State * L,global_State * g,int nextstate,GCObject ** nextlist)15660495ed39SKyle Evans static int sweepstep (lua_State *L, global_State *g,
15678e3e3a7aSWarner Losh int nextstate, GCObject **nextlist) {
15688e3e3a7aSWarner Losh if (g->sweepgc) {
15698e3e3a7aSWarner Losh l_mem olddebt = g->GCdebt;
15700495ed39SKyle Evans int count;
15710495ed39SKyle Evans g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX, &count);
15728e3e3a7aSWarner Losh g->GCestimate += g->GCdebt - olddebt; /* update estimate */
15730495ed39SKyle Evans return count;
15748e3e3a7aSWarner Losh }
15750495ed39SKyle Evans else { /* enter next state */
15768e3e3a7aSWarner Losh g->gcstate = nextstate;
15778e3e3a7aSWarner Losh g->sweepgc = nextlist;
15780495ed39SKyle Evans return 0; /* no work done */
15790495ed39SKyle Evans }
15808e3e3a7aSWarner Losh }
15818e3e3a7aSWarner Losh
15828e3e3a7aSWarner Losh
singlestep(lua_State * L)15838e3e3a7aSWarner Losh static lu_mem singlestep (lua_State *L) {
15848e3e3a7aSWarner Losh global_State *g = G(L);
15858c784bb8SWarner Losh lu_mem work;
15868c784bb8SWarner Losh lua_assert(!g->gcstopem); /* collector is not reentrant */
15878c784bb8SWarner Losh g->gcstopem = 1; /* no emergency collections while collecting */
15888e3e3a7aSWarner Losh switch (g->gcstate) {
15898e3e3a7aSWarner Losh case GCSpause: {
15908e3e3a7aSWarner Losh restartcollection(g);
15918e3e3a7aSWarner Losh g->gcstate = GCSpropagate;
15928c784bb8SWarner Losh work = 1;
15938c784bb8SWarner Losh break;
15948e3e3a7aSWarner Losh }
15958e3e3a7aSWarner Losh case GCSpropagate: {
15960495ed39SKyle Evans if (g->gray == NULL) { /* no more gray objects? */
15970495ed39SKyle Evans g->gcstate = GCSenteratomic; /* finish propagate phase */
15988c784bb8SWarner Losh work = 0;
15998e3e3a7aSWarner Losh }
16000495ed39SKyle Evans else
16018c784bb8SWarner Losh work = propagatemark(g); /* traverse one gray object */
16028c784bb8SWarner Losh break;
16030495ed39SKyle Evans }
16040495ed39SKyle Evans case GCSenteratomic: {
16058c784bb8SWarner Losh work = atomic(L); /* work is what was traversed by 'atomic' */
16068e3e3a7aSWarner Losh entersweep(L);
16078e3e3a7aSWarner Losh g->GCestimate = gettotalbytes(g); /* first estimate */;
16088c784bb8SWarner Losh break;
16098e3e3a7aSWarner Losh }
16108e3e3a7aSWarner Losh case GCSswpallgc: { /* sweep "regular" objects */
16118c784bb8SWarner Losh work = sweepstep(L, g, GCSswpfinobj, &g->finobj);
16128c784bb8SWarner Losh break;
16138e3e3a7aSWarner Losh }
16148e3e3a7aSWarner Losh case GCSswpfinobj: { /* sweep objects with finalizers */
16158c784bb8SWarner Losh work = sweepstep(L, g, GCSswptobefnz, &g->tobefnz);
16168c784bb8SWarner Losh break;
16178e3e3a7aSWarner Losh }
16188e3e3a7aSWarner Losh case GCSswptobefnz: { /* sweep objects to be finalized */
16198c784bb8SWarner Losh work = sweepstep(L, g, GCSswpend, NULL);
16208c784bb8SWarner Losh break;
16218e3e3a7aSWarner Losh }
16228e3e3a7aSWarner Losh case GCSswpend: { /* finish sweeps */
16238e3e3a7aSWarner Losh checkSizes(L, g);
16248e3e3a7aSWarner Losh g->gcstate = GCScallfin;
16258c784bb8SWarner Losh work = 0;
16268c784bb8SWarner Losh break;
16278e3e3a7aSWarner Losh }
16288e3e3a7aSWarner Losh case GCScallfin: { /* call remaining finalizers */
16290495ed39SKyle Evans if (g->tobefnz && !g->gcemergency) {
16308c784bb8SWarner Losh g->gcstopem = 0; /* ok collections during finalizers */
16318c784bb8SWarner Losh work = runafewfinalizers(L, GCFINMAX) * GCFINALIZECOST;
16328e3e3a7aSWarner Losh }
16338e3e3a7aSWarner Losh else { /* emergency mode or no more finalizers */
16348e3e3a7aSWarner Losh g->gcstate = GCSpause; /* finish collection */
16358c784bb8SWarner Losh work = 0;
16368e3e3a7aSWarner Losh }
16378c784bb8SWarner Losh break;
16388e3e3a7aSWarner Losh }
16398e3e3a7aSWarner Losh default: lua_assert(0); return 0;
16408e3e3a7aSWarner Losh }
16418c784bb8SWarner Losh g->gcstopem = 0;
16428c784bb8SWarner Losh return work;
16438e3e3a7aSWarner Losh }
16448e3e3a7aSWarner Losh
16458e3e3a7aSWarner Losh
16468e3e3a7aSWarner Losh /*
16478e3e3a7aSWarner Losh ** advances the garbage collector until it reaches a state allowed
16488e3e3a7aSWarner Losh ** by 'statemask'
16498e3e3a7aSWarner Losh */
luaC_runtilstate(lua_State * L,int statesmask)16508e3e3a7aSWarner Losh void luaC_runtilstate (lua_State *L, int statesmask) {
16518e3e3a7aSWarner Losh global_State *g = G(L);
16528e3e3a7aSWarner Losh while (!testbit(statesmask, g->gcstate))
16538e3e3a7aSWarner Losh singlestep(L);
16548e3e3a7aSWarner Losh }
16558e3e3a7aSWarner Losh
16568e3e3a7aSWarner Losh
16578c784bb8SWarner Losh
16588e3e3a7aSWarner Losh /*
16590495ed39SKyle Evans ** Performs a basic incremental step. The debt and step size are
16600495ed39SKyle Evans ** converted from bytes to "units of work"; then the function loops
16610495ed39SKyle Evans ** running single steps until adding that many units of work or
16620495ed39SKyle Evans ** finishing a cycle (pause state). Finally, it sets the debt that
16630495ed39SKyle Evans ** controls when next step will be performed.
16648e3e3a7aSWarner Losh */
incstep(lua_State * L,global_State * g)16650495ed39SKyle Evans static void incstep (lua_State *L, global_State *g) {
16660495ed39SKyle Evans int stepmul = (getgcparam(g->gcstepmul) | 1); /* avoid division by 0 */
16670495ed39SKyle Evans l_mem debt = (g->GCdebt / WORK2MEM) * stepmul;
16680495ed39SKyle Evans l_mem stepsize = (g->gcstepsize <= log2maxs(l_mem))
16690495ed39SKyle Evans ? ((cast(l_mem, 1) << g->gcstepsize) / WORK2MEM) * stepmul
16700495ed39SKyle Evans : MAX_LMEM; /* overflow; keep maximum value */
16710495ed39SKyle Evans do { /* repeat until pause or enough "credit" (negative debt) */
16720495ed39SKyle Evans lu_mem work = singlestep(L); /* perform one single step */
16730495ed39SKyle Evans debt -= work;
16740495ed39SKyle Evans } while (debt > -stepsize && g->gcstate != GCSpause);
16750495ed39SKyle Evans if (g->gcstate == GCSpause)
16760495ed39SKyle Evans setpause(g); /* pause until next cycle */
16778e3e3a7aSWarner Losh else {
16780495ed39SKyle Evans debt = (debt / stepmul) * WORK2MEM; /* convert 'work units' to bytes */
16790495ed39SKyle Evans luaE_setdebt(g, debt);
16808e3e3a7aSWarner Losh }
16818e3e3a7aSWarner Losh }
16828e3e3a7aSWarner Losh
16838e3e3a7aSWarner Losh /*
1684*a9490b81SWarner Losh ** Performs a basic GC step if collector is running. (If collector is
1685*a9490b81SWarner Losh ** not running, set a reasonable debt to avoid it being called at
1686*a9490b81SWarner Losh ** every single check.)
16878e3e3a7aSWarner Losh */
luaC_step(lua_State * L)16888e3e3a7aSWarner Losh void luaC_step (lua_State *L) {
16898e3e3a7aSWarner Losh global_State *g = G(L);
1690*a9490b81SWarner Losh if (!gcrunning(g)) /* not running? */
1691*a9490b81SWarner Losh luaE_setdebt(g, -2000);
1692*a9490b81SWarner Losh else {
16930495ed39SKyle Evans if(isdecGCmodegen(g))
16940495ed39SKyle Evans genstep(L, g);
16950495ed39SKyle Evans else
16960495ed39SKyle Evans incstep(L, g);
16978e3e3a7aSWarner Losh }
16988e3e3a7aSWarner Losh }
16990495ed39SKyle Evans
17000495ed39SKyle Evans
17010495ed39SKyle Evans /*
17020495ed39SKyle Evans ** Perform a full collection in incremental mode.
17030495ed39SKyle Evans ** Before running the collection, check 'keepinvariant'; if it is true,
17040495ed39SKyle Evans ** there may be some objects marked as black, so the collector has
17050495ed39SKyle Evans ** to sweep all objects to turn them back to white (as white has not
17060495ed39SKyle Evans ** changed, nothing will be collected).
17070495ed39SKyle Evans */
fullinc(lua_State * L,global_State * g)17080495ed39SKyle Evans static void fullinc (lua_State *L, global_State *g) {
17090495ed39SKyle Evans if (keepinvariant(g)) /* black objects? */
17100495ed39SKyle Evans entersweep(L); /* sweep everything to turn them back to white */
17110495ed39SKyle Evans /* finish any pending sweep phase to start a new cycle */
17120495ed39SKyle Evans luaC_runtilstate(L, bitmask(GCSpause));
17130495ed39SKyle Evans luaC_runtilstate(L, bitmask(GCScallfin)); /* run up to finalizers */
17140495ed39SKyle Evans /* estimate must be correct after a full GC cycle */
17150495ed39SKyle Evans lua_assert(g->GCestimate == gettotalbytes(g));
17160495ed39SKyle Evans luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */
17170495ed39SKyle Evans setpause(g);
17188e3e3a7aSWarner Losh }
17198e3e3a7aSWarner Losh
17208e3e3a7aSWarner Losh
17218e3e3a7aSWarner Losh /*
17228e3e3a7aSWarner Losh ** Performs a full GC cycle; if 'isemergency', set a flag to avoid
17238e3e3a7aSWarner Losh ** some operations which could change the interpreter state in some
17248e3e3a7aSWarner Losh ** unexpected ways (running finalizers and shrinking some structures).
17258e3e3a7aSWarner Losh */
luaC_fullgc(lua_State * L,int isemergency)17268e3e3a7aSWarner Losh void luaC_fullgc (lua_State *L, int isemergency) {
17278e3e3a7aSWarner Losh global_State *g = G(L);
17280495ed39SKyle Evans lua_assert(!g->gcemergency);
17290495ed39SKyle Evans g->gcemergency = isemergency; /* set flag */
17300495ed39SKyle Evans if (g->gckind == KGC_INC)
17310495ed39SKyle Evans fullinc(L, g);
17320495ed39SKyle Evans else
17330495ed39SKyle Evans fullgen(L, g);
17340495ed39SKyle Evans g->gcemergency = 0;
17358e3e3a7aSWarner Losh }
17368e3e3a7aSWarner Losh
17378e3e3a7aSWarner Losh /* }====================================================== */
17388e3e3a7aSWarner Losh
17398e3e3a7aSWarner Losh
1740