LCOV - code coverage report
Current view: top level - src - lgc.c Coverage Total Hit
Test: Lua 5.2.4 Lines: 85.1 % 612 521
Test Date: 2024-04-28 10:23:12
Legend: Lines: hit not hit

            Line data    Source code
       1              : /*
       2              : ** $Id: lgc.c,v 2.140.1.3 2014/09/01 16:55:08 roberto Exp $
       3              : ** Garbage Collector
       4              : ** See Copyright Notice in lua.h
       5              : */
       6              : 
       7              : #include <string.h>
       8              : 
       9              : #define lgc_c
      10              : #define LUA_CORE
      11              : 
      12              : #include "lua.h"
      13              : 
      14              : #include "ldebug.h"
      15              : #include "ldo.h"
      16              : #include "lfunc.h"
      17              : #include "lgc.h"
      18              : #include "lmem.h"
      19              : #include "lobject.h"
      20              : #include "lstate.h"
      21              : #include "lstring.h"
      22              : #include "ltable.h"
      23              : #include "ltm.h"
      24              : 
      25              : 
      26              : 
      27              : /*
      28              : ** cost of sweeping one element (the size of a small object divided
      29              : ** by some adjust for the sweep speed)
      30              : */
      31              : #define GCSWEEPCOST     ((sizeof(TString) + 4) / 4)
      32              : 
      33              : /* maximum number of elements to sweep in each single step */
      34              : #define GCSWEEPMAX      (cast_int((GCSTEPSIZE / GCSWEEPCOST) / 4))
      35              : 
      36              : /* maximum number of finalizers to call in each GC step */
      37              : #define GCFINALIZENUM   4
      38              : 
      39              : 
      40              : /*
      41              : ** macro to adjust 'stepmul': 'stepmul' is actually used like
      42              : ** 'stepmul / STEPMULADJ' (value chosen by tests)
      43              : */
      44              : #define STEPMULADJ              200
      45              : 
      46              : 
      47              : /*
      48              : ** macro to adjust 'pause': 'pause' is actually used like
      49              : ** 'pause / PAUSEADJ' (value chosen by tests)
      50              : */
      51              : #define PAUSEADJ                100
      52              : 
      53              : 
      54              : /*
      55              : ** 'makewhite' erases all color bits plus the old bit and then
      56              : ** sets only the current white bit
      57              : */
      58              : #define maskcolors      (~(bit2mask(BLACKBIT, OLDBIT) | WHITEBITS))
      59              : #define makewhite(g,x)  \
      60              :  (gch(x)->marked = cast_byte((gch(x)->marked & maskcolors) | luaC_white(g)))
      61              : 
      62              : #define white2gray(x)   resetbits(gch(x)->marked, WHITEBITS)
      63              : #define black2gray(x)   resetbit(gch(x)->marked, BLACKBIT)
      64              : 
      65              : 
      66              : #define isfinalized(x)          testbit(gch(x)->marked, FINALIZEDBIT)
      67              : 
      68              : #define checkdeadkey(n) lua_assert(!ttisdeadkey(gkey(n)) || ttisnil(gval(n)))
      69              : 
      70              : 
      71              : #define checkconsistency(obj)  \
      72              :   lua_longassert(!iscollectable(obj) || righttt(obj))
      73              : 
      74              : 
      75              : #define markvalue(g,o) { checkconsistency(o); \
      76              :   if (valiswhite(o)) reallymarkobject(g,gcvalue(o)); }
      77              : 
      78              : #define markobject(g,t) { if ((t) && iswhite(obj2gco(t))) \
      79              :                 reallymarkobject(g, obj2gco(t)); }
      80              : 
      81              : static void reallymarkobject (global_State *g, GCObject *o);
      82              : 
      83              : 
      84              : /*
      85              : ** {======================================================
      86              : ** Generic functions
      87              : ** =======================================================
      88              : */
      89              : 
      90              : 
      91              : /*
      92              : ** one after last element in a hash array
      93              : */
      94              : #define gnodelast(h)    gnode(h, cast(size_t, sizenode(h)))
      95              : 
      96              : 
      97              : /*
      98              : ** link table 'h' into list pointed by 'p'
      99              : */
     100              : #define linktable(h,p)  ((h)->gclist = *(p), *(p) = obj2gco(h))
     101              : 
     102              : 
     103              : /*
     104              : ** if key is not marked, mark its entry as dead (therefore removing it
     105              : ** from the table)
     106              : */
     107        13806 : static void removeentry (Node *n) {
     108              :   lua_assert(ttisnil(gval(n)));
     109        13806 :   if (valiswhite(gkey(n)))
     110            1 :     setdeadvalue(gkey(n));  /* unused and unmarked key; remove it */
     111        13806 : }
     112              : 
     113              : 
     114              : /*
     115              : ** tells whether a key or value can be cleared from a weak
     116              : ** table. Non-collectable objects are never removed from weak
     117              : ** tables. Strings behave as `values', so are never removed too. for
     118              : ** other objects: if really collected, cannot keep them; for objects
     119              : ** being finalized, keep them in keys, but not in values
     120              : */
     121            0 : static int iscleared (global_State *g, const TValue *o) {
     122            0 :   if (!iscollectable(o)) return 0;
     123            0 :   else if (ttisstring(o)) {
     124            0 :     markobject(g, rawtsvalue(o));  /* strings are `values', so are never weak */
     125            0 :     return 0;
     126              :   }
     127            0 :   else return iswhite(gcvalue(o));
     128              : }
     129              : 
     130              : 
     131              : /*
     132              : ** barrier that moves collector forward, that is, mark the white object
     133              : ** being pointed by a black object.
     134              : */
     135         1026 : void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) {
     136         1026 :   global_State *g = G(L);
     137              :   lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o));
     138              :   lua_assert(g->gcstate != GCSpause);
     139              :   lua_assert(gch(o)->tt != LUA_TTABLE);
     140         1026 :   if (keepinvariantout(g))  /* must keep invariant? */
     141          891 :     reallymarkobject(g, v);  /* restore invariant */
     142              :   else {  /* sweep phase */
     143              :     lua_assert(issweepphase(g));
     144          135 :     makewhite(g, o);  /* mark main obj. as white to avoid other barriers */
     145              :   }
     146         1026 : }
     147              : 
     148              : 
     149              : /*
     150              : ** barrier that moves collector backward, that is, mark the black object
     151              : ** pointing to a white object as gray again. (Current implementation
     152              : ** only works for tables; access to 'gclist' is not uniform across
     153              : ** different types.)
     154              : */
     155          218 : void luaC_barrierback_ (lua_State *L, GCObject *o) {
     156          218 :   global_State *g = G(L);
     157              :   lua_assert(isblack(o) && !isdead(g, o) && gch(o)->tt == LUA_TTABLE);
     158          218 :   black2gray(o);  /* make object gray (again) */
     159          218 :   gco2t(o)->gclist = g->grayagain;
     160          218 :   g->grayagain = o;
     161          218 : }
     162              : 
     163              : 
     164              : /*
     165              : ** barrier for prototypes. When creating first closure (cache is
     166              : ** NULL), use a forward barrier; this may be the only closure of the
     167              : ** prototype (if it is a "regular" function, with a single instance)
     168              : ** and the prototype may be big, so it is better to avoid traversing
     169              : ** it again. Otherwise, use a backward barrier, to avoid marking all
     170              : ** possible instances.
     171              : */
     172          174 : LUAI_FUNC void luaC_barrierproto_ (lua_State *L, Proto *p, Closure *c) {
     173          174 :   global_State *g = G(L);
     174              :   lua_assert(isblack(obj2gco(p)));
     175          174 :   if (p->cache == NULL) {  /* first time? */
     176          169 :     luaC_objbarrier(L, p, c);
     177              :   }
     178              :   else {  /* use a backward barrier */
     179            5 :     black2gray(obj2gco(p));  /* make prototype gray (again) */
     180            5 :     p->gclist = g->grayagain;
     181            5 :     g->grayagain = obj2gco(p);
     182              :   }
     183          174 : }
     184              : 
     185              : 
     186              : /*
     187              : ** check color (and invariants) for an upvalue that was closed,
     188              : ** i.e., moved into the 'allgc' list
     189              : */
     190         5703 : void luaC_checkupvalcolor (global_State *g, UpVal *uv) {
     191         5703 :   GCObject *o = obj2gco(uv);
     192              :   lua_assert(!isblack(o));  /* open upvalues are never black */
     193         5703 :   if (isgray(o)) {
     194           43 :     if (keepinvariant(g)) {
     195           40 :       resetoldbit(o);  /* see MOVE OLD rule */
     196           40 :       gray2black(o);  /* it is being visited now */
     197           40 :       markvalue(g, uv->v);
     198              :     }
     199              :     else {
     200              :       lua_assert(issweepphase(g));
     201            3 :       makewhite(g, o);
     202              :     }
     203              :   }
     204         5703 : }
     205              : 
     206              : 
     207              : /*
     208              : ** create a new collectable object (with given type and size) and link
     209              : ** it to '*list'. 'offset' tells how many bytes to allocate before the
     210              : ** object itself (used only by states).
     211              : */
     212        69819 : GCObject *luaC_newobj (lua_State *L, int tt, size_t sz, GCObject **list,
     213              :                        int offset) {
     214        69819 :   global_State *g = G(L);
     215        69819 :   char *raw = cast(char *, luaM_newobject(L, novariant(tt), sz));
     216        69819 :   GCObject *o = obj2gco(raw + offset);
     217        69819 :   if (list == NULL)
     218        21920 :     list = &g->allgc;  /* standard list for collectable objects */
     219        69819 :   gch(o)->marked = luaC_white(g);
     220        69819 :   gch(o)->tt = tt;
     221        69819 :   gch(o)->next = *list;
     222        69819 :   *list = o;
     223        69819 :   return o;
     224              : }
     225              : 
     226              : /* }====================================================== */
     227              : 
     228              : 
     229              : 
     230              : /*
     231              : ** {======================================================
     232              : ** Mark functions
     233              : ** =======================================================
     234              : */
     235              : 
     236              : 
     237              : /*
     238              : ** mark an object. Userdata, strings, and closed upvalues are visited
     239              : ** and turned black here. Other objects are marked gray and added
     240              : ** to appropriate list to be visited (and turned black) later. (Open
     241              : ** upvalues are already linked in 'headuv' list.)
     242              : */
     243        50941 : static void reallymarkobject (global_State *g, GCObject *o) {
     244              :   lu_mem size;
     245        50941 :   white2gray(o);
     246        50941 :   switch (gch(o)->tt) {
     247        36763 :     case LUA_TSHRSTR:
     248              :     case LUA_TLNGSTR: {
     249        36763 :       size = sizestring(gco2ts(o));
     250        36763 :       break;  /* nothing else to mark; make it black */
     251              :     }
     252          391 :     case LUA_TUSERDATA: {
     253          391 :       Table *mt = gco2u(o)->metatable;
     254          391 :       markobject(g, mt);
     255          391 :       markobject(g, gco2u(o)->env);
     256          391 :       size = sizeudata(gco2u(o));
     257          391 :       break;
     258              :     }
     259         3939 :     case LUA_TUPVAL: {
     260         3939 :       UpVal *uv = gco2uv(o);
     261         3939 :       markvalue(g, uv->v);
     262         3939 :       if (uv->v != &uv->u.value)  /* open? */
     263          645 :         return;  /* open upvalues remain gray */
     264         3294 :       size = sizeof(UpVal);
     265         3294 :       break;
     266              :     }
     267         2656 :     case LUA_TLCL: {
     268         2656 :       gco2lcl(o)->gclist = g->gray;
     269         2656 :       g->gray = o;
     270         2656 :       return;
     271              :     }
     272          695 :     case LUA_TCCL: {
     273          695 :       gco2ccl(o)->gclist = g->gray;
     274          695 :       g->gray = o;
     275          695 :       return;
     276              :     }
     277         3046 :     case LUA_TTABLE: {
     278         3046 :       linktable(gco2t(o), &g->gray);
     279         3046 :       return;
     280              :     }
     281          300 :     case LUA_TTHREAD: {
     282          300 :       gco2th(o)->gclist = g->gray;
     283          300 :       g->gray = o;
     284          300 :       return;
     285              :     }
     286         3151 :     case LUA_TPROTO: {
     287         3151 :       gco2p(o)->gclist = g->gray;
     288         3151 :       g->gray = o;
     289         3151 :       return;
     290              :     }
     291            0 :     default: lua_assert(0); return;
     292              :   }
     293        40448 :   gray2black(o);
     294        40448 :   g->GCmemtrav += size;
     295              : }
     296              : 
     297              : 
     298              : /*
     299              : ** mark metamethods for basic types
     300              : */
     301          499 : static void markmt (global_State *g) {
     302              :   int i;
     303         4990 :   for (i=0; i < LUA_NUMTAGS; i++)
     304         4491 :     markobject(g, g->mt[i]);
     305          499 : }
     306              : 
     307              : 
     308              : /*
     309              : ** mark all objects in list of being-finalized
     310              : */
     311          499 : static void markbeingfnz (global_State *g) {
     312              :   GCObject *o;
     313          508 :   for (o = g->tobefnz; o != NULL; o = gch(o)->next) {
     314            9 :     makewhite(g, o);
     315            9 :     reallymarkobject(g, o);
     316              :   }
     317          499 : }
     318              : 
     319              : 
     320              : /*
     321              : ** mark all values stored in marked open upvalues. (See comment in
     322              : ** 'lstate.h'.)
     323              : */
     324          215 : static void remarkupvals (global_State *g) {
     325              :   UpVal *uv;
     326          825 :   for (uv = g->uvhead.u.l.next; uv != &g->uvhead; uv = uv->u.l.next) {
     327          610 :     if (isgray(obj2gco(uv)))
     328          309 :       markvalue(g, uv->v);
     329              :   }
     330          215 : }
     331              : 
     332              : 
     333              : /*
     334              : ** mark root set and reset all gray lists, to start a new
     335              : ** incremental (or full) collection
     336              : */
     337          284 : static void restartcollection (global_State *g) {
     338          284 :   g->gray = g->grayagain = NULL;
     339          284 :   g->weak = g->allweak = g->ephemeron = NULL;
     340          284 :   markobject(g, g->mainthread);
     341          284 :   markvalue(g, &g->l_registry);
     342          284 :   markmt(g);
     343          284 :   markbeingfnz(g);  /* mark any finalizing object left from previous cycle */
     344          284 : }
     345              : 
     346              : /* }====================================================== */
     347              : 
     348              : 
     349              : /*
     350              : ** {======================================================
     351              : ** Traverse functions
     352              : ** =======================================================
     353              : */
     354              : 
     355            0 : static void traverseweakvalue (global_State *g, Table *h) {
     356            0 :   Node *n, *limit = gnodelast(h);
     357              :   /* if there is array part, assume it may have white values (do not
     358              :      traverse it just to check) */
     359            0 :   int hasclears = (h->sizearray > 0);
     360            0 :   for (n = gnode(h, 0); n < limit; n++) {
     361              :     checkdeadkey(n);
     362            0 :     if (ttisnil(gval(n)))  /* entry is empty? */
     363            0 :       removeentry(n);  /* remove it */
     364              :     else {
     365              :       lua_assert(!ttisnil(gkey(n)));
     366            0 :       markvalue(g, gkey(n));  /* mark key */
     367            0 :       if (!hasclears && iscleared(g, gval(n)))  /* is there a white value? */
     368            0 :         hasclears = 1;  /* table will have to be cleared */
     369              :     }
     370              :   }
     371            0 :   if (hasclears)
     372            0 :     linktable(h, &g->weak);  /* has to be cleared later */
     373              :   else  /* no white values */
     374            0 :     linktable(h, &g->grayagain);  /* no need to clean */
     375            0 : }
     376              : 
     377              : 
     378            0 : static int traverseephemeron (global_State *g, Table *h) {
     379            0 :   int marked = 0;  /* true if an object is marked in this traversal */
     380            0 :   int hasclears = 0;  /* true if table has white keys */
     381            0 :   int prop = 0;  /* true if table has entry "white-key -> white-value" */
     382            0 :   Node *n, *limit = gnodelast(h);
     383              :   int i;
     384              :   /* traverse array part (numeric keys are 'strong') */
     385            0 :   for (i = 0; i < h->sizearray; i++) {
     386            0 :     if (valiswhite(&h->array[i])) {
     387            0 :       marked = 1;
     388            0 :       reallymarkobject(g, gcvalue(&h->array[i]));
     389              :     }
     390              :   }
     391              :   /* traverse hash part */
     392            0 :   for (n = gnode(h, 0); n < limit; n++) {
     393              :     checkdeadkey(n);
     394            0 :     if (ttisnil(gval(n)))  /* entry is empty? */
     395            0 :       removeentry(n);  /* remove it */
     396            0 :     else if (iscleared(g, gkey(n))) {  /* key is not marked (yet)? */
     397            0 :       hasclears = 1;  /* table must be cleared */
     398            0 :       if (valiswhite(gval(n)))  /* value not marked yet? */
     399            0 :         prop = 1;  /* must propagate again */
     400              :     }
     401            0 :     else if (valiswhite(gval(n))) {  /* value not marked yet? */
     402            0 :       marked = 1;
     403            0 :       reallymarkobject(g, gcvalue(gval(n)));  /* mark it now */
     404              :     }
     405              :   }
     406            0 :   if (g->gcstate != GCSatomic || prop)
     407            0 :     linktable(h, &g->ephemeron);  /* have to propagate again */
     408            0 :   else if (hasclears)  /* does table have white keys? */
     409            0 :     linktable(h, &g->allweak);  /* may have to clean white keys */
     410              :   else  /* no white keys */
     411            0 :     linktable(h, &g->grayagain);  /* no need to clean */
     412            0 :   return marked;
     413              : }
     414              : 
     415              : 
     416         3002 : static void traversestrongtable (global_State *g, Table *h) {
     417         3002 :   Node *n, *limit = gnodelast(h);
     418              :   int i;
     419         4633 :   for (i = 0; i < h->sizearray; i++)  /* traverse array part */
     420         1631 :     markvalue(g, &h->array[i]);
     421        51045 :   for (n = gnode(h, 0); n < limit; n++) {  /* traverse hash part */
     422              :     checkdeadkey(n);
     423        48043 :     if (ttisnil(gval(n)))  /* entry is empty? */
     424        13806 :       removeentry(n);  /* remove it */
     425              :     else {
     426              :       lua_assert(!ttisnil(gkey(n)));
     427        34237 :       markvalue(g, gkey(n));  /* mark key */
     428        34237 :       markvalue(g, gval(n));  /* mark value */
     429              :     }
     430              :   }
     431         3002 : }
     432              : 
     433              : 
     434         3002 : static lu_mem traversetable (global_State *g, Table *h) {
     435              :   const char *weakkey, *weakvalue;
     436         3002 :   const TValue *mode = gfasttm(g, h->metatable, TM_MODE);
     437         3002 :   markobject(g, h->metatable);
     438         3002 :   if (mode && ttisstring(mode) &&  /* is there a weak mode? */
     439            0 :       ((weakkey = strchr(svalue(mode), 'k')),
     440            0 :        (weakvalue = strchr(svalue(mode), 'v')),
     441            0 :        (weakkey || weakvalue))) {  /* is really weak? */
     442            0 :     black2gray(obj2gco(h));  /* keep table gray */
     443            0 :     if (!weakkey)  /* strong keys? */
     444            0 :       traverseweakvalue(g, h);
     445            0 :     else if (!weakvalue)  /* strong values? */
     446            0 :       traverseephemeron(g, h);
     447              :     else  /* all weak */
     448            0 :       linktable(h, &g->allweak);  /* nothing to traverse now */
     449              :   }
     450              :   else  /* not weak */
     451         3002 :     traversestrongtable(g, h);
     452         6004 :   return sizeof(Table) + sizeof(TValue) * h->sizearray +
     453         3002 :                          sizeof(Node) * cast(size_t, sizenode(h));
     454              : }
     455              : 
     456              : 
     457         2912 : static int traverseproto (global_State *g, Proto *f) {
     458              :   int i;
     459         2912 :   if (f->cache && iswhite(obj2gco(f->cache)))
     460          422 :     f->cache = NULL;  /* allow cache to be collected */
     461         2912 :   markobject(g, f->source);
     462        22546 :   for (i = 0; i < f->sizek; i++)  /* mark literals */
     463        19634 :     markvalue(g, &f->k[i]);
     464         8014 :   for (i = 0; i < f->sizeupvalues; i++)  /* mark upvalue names */
     465         5102 :     markobject(g, f->upvalues[i].name);
     466         5113 :   for (i = 0; i < f->sizep; i++)  /* mark nested protos */
     467         2201 :     markobject(g, f->p[i]);
     468        11142 :   for (i = 0; i < f->sizelocvars; i++)  /* mark local-variable names */
     469         8230 :     markobject(g, f->locvars[i].varname);
     470         2912 :   return sizeof(Proto) + sizeof(Instruction) * f->sizecode +
     471         2912 :                          sizeof(Proto *) * f->sizep +
     472         2912 :                          sizeof(TValue) * f->sizek +
     473         2912 :                          sizeof(int) * f->sizelineinfo +
     474         5824 :                          sizeof(LocVar) * f->sizelocvars +
     475         2912 :                          sizeof(Upvaldesc) * f->sizeupvalues;
     476              : }
     477              : 
     478              : 
     479          675 : static lu_mem traverseCclosure (global_State *g, CClosure *cl) {
     480              :   int i;
     481         1362 :   for (i = 0; i < cl->nupvalues; i++)  /* mark its upvalues */
     482          687 :     markvalue(g, &cl->upvalue[i]);
     483          675 :   return sizeCclosure(cl->nupvalues);
     484              : }
     485              : 
     486         2038 : static lu_mem traverseLclosure (global_State *g, LClosure *cl) {
     487              :   int i;
     488         2038 :   markobject(g, cl->p);  /* mark its prototype */
     489         9198 :   for (i = 0; i < cl->nupvalues; i++)  /* mark its upvalues */
     490         7160 :     markobject(g, cl->upvals[i]);
     491         2038 :   return sizeLclosure(cl->nupvalues);
     492              : }
     493              : 
     494              : 
     495          466 : static lu_mem traversestack (global_State *g, lua_State *th) {
     496          466 :   int n = 0;
     497          466 :   StkId o = th->stack;
     498          466 :   if (o == NULL)
     499            0 :     return 1;  /* stack not completely built yet */
     500         8444 :   for (; o < th->top; o++)  /* mark live elements in the stack */
     501         7978 :     markvalue(g, o);
     502          466 :   if (g->gcstate == GCSatomic) {  /* final traversal? */
     503          228 :     StkId lim = th->stack + th->stacksize;  /* real end of stack */
     504         9921 :     for (; o < lim; o++)  /* clear not-marked stack slice */
     505         9693 :       setnilvalue(o);
     506              :   }
     507              :   else {  /* count call infos to compute size */
     508              :     CallInfo *ci;
     509          869 :     for (ci = &th->base_ci; ci != th->ci; ci = ci->next)
     510          631 :       n++;
     511              :   }
     512          466 :   return sizeof(lua_State) + sizeof(TValue) * th->stacksize +
     513              :          sizeof(CallInfo) * n;
     514              : }
     515              : 
     516              : 
     517              : /*
     518              : ** traverse one gray object, turning it to black (except for threads,
     519              : ** which are always gray).
     520              : */
     521         9093 : static void propagatemark (global_State *g) {
     522              :   lu_mem size;
     523         9093 :   GCObject *o = g->gray;
     524              :   lua_assert(isgray(o));
     525         9093 :   gray2black(o);
     526         9093 :   switch (gch(o)->tt) {
     527         3002 :     case LUA_TTABLE: {
     528         3002 :       Table *h = gco2t(o);
     529         3002 :       g->gray = h->gclist;  /* remove from 'gray' list */
     530         3002 :       size = traversetable(g, h);
     531         3002 :       break;
     532              :     }
     533         2038 :     case LUA_TLCL: {
     534         2038 :       LClosure *cl = gco2lcl(o);
     535         2038 :       g->gray = cl->gclist;  /* remove from 'gray' list */
     536         2038 :       size = traverseLclosure(g, cl);
     537         2038 :       break;
     538              :     }
     539          675 :     case LUA_TCCL: {
     540          675 :       CClosure *cl = gco2ccl(o);
     541          675 :       g->gray = cl->gclist;  /* remove from 'gray' list */
     542          675 :       size = traverseCclosure(g, cl);
     543          675 :       break;
     544              :     }
     545          466 :     case LUA_TTHREAD: {
     546          466 :       lua_State *th = gco2th(o);
     547          466 :       g->gray = th->gclist;  /* remove from 'gray' list */
     548          466 :       th->gclist = g->grayagain;
     549          466 :       g->grayagain = o;  /* insert into 'grayagain' list */
     550          466 :       black2gray(o);
     551          466 :       size = traversestack(g, th);
     552          466 :       break;
     553              :     }
     554         2912 :     case LUA_TPROTO: {
     555         2912 :       Proto *p = gco2p(o);
     556         2912 :       g->gray = p->gclist;  /* remove from 'gray' list */
     557         2912 :       size = traverseproto(g, p);
     558         2912 :       break;
     559              :     }
     560            0 :     default: lua_assert(0); return;
     561              :   }
     562         9093 :   g->GCmemtrav += size;
     563              : }
     564              : 
     565              : 
     566         1290 : static void propagateall (global_State *g) {
     567         2375 :   while (g->gray) propagatemark(g);
     568         1290 : }
     569              : 
     570              : 
     571          645 : static void propagatelist (global_State *g, GCObject *l) {
     572              :   lua_assert(g->gray == NULL);  /* no grays left */
     573          645 :   g->gray = l;
     574          645 :   propagateall(g);  /* traverse all elements from 'l' */
     575          645 : }
     576              : 
     577              : /*
     578              : ** retraverse all gray lists. Because tables may be reinserted in other
     579              : ** lists when traversed, traverse the original lists to avoid traversing
     580              : ** twice the same table (which is not wrong, but inefficient)
     581              : */
     582          215 : static void retraversegrays (global_State *g) {
     583          215 :   GCObject *weak = g->weak;  /* save original lists */
     584          215 :   GCObject *grayagain = g->grayagain;
     585          215 :   GCObject *ephemeron = g->ephemeron;
     586          215 :   g->weak = g->grayagain = g->ephemeron = NULL;
     587          215 :   propagateall(g);  /* traverse main gray list */
     588          215 :   propagatelist(g, grayagain);
     589          215 :   propagatelist(g, weak);
     590          215 :   propagatelist(g, ephemeron);
     591          215 : }
     592              : 
     593              : 
     594          430 : static void convergeephemerons (global_State *g) {
     595              :   int changed;
     596              :   do {
     597              :     GCObject *w;
     598          430 :     GCObject *next = g->ephemeron;  /* get ephemeron list */
     599          430 :     g->ephemeron = NULL;  /* tables will return to this list when traversed */
     600          430 :     changed = 0;
     601          430 :     while ((w = next) != NULL) {
     602            0 :       next = gco2t(w)->gclist;
     603            0 :       if (traverseephemeron(g, gco2t(w))) {  /* traverse marked some value? */
     604            0 :         propagateall(g);  /* propagate changes */
     605            0 :         changed = 1;  /* will have to revisit all ephemeron tables */
     606              :       }
     607              :     }
     608          430 :   } while (changed);
     609          430 : }
     610              : 
     611              : /* }====================================================== */
     612              : 
     613              : 
     614              : /*
     615              : ** {======================================================
     616              : ** Sweep Functions
     617              : ** =======================================================
     618              : */
     619              : 
     620              : 
     621              : /*
     622              : ** clear entries with unmarked keys from all weaktables in list 'l' up
     623              : ** to element 'f'
     624              : */
     625          430 : static void clearkeys (global_State *g, GCObject *l, GCObject *f) {
     626          430 :   for (; l != f; l = gco2t(l)->gclist) {
     627            0 :     Table *h = gco2t(l);
     628            0 :     Node *n, *limit = gnodelast(h);
     629            0 :     for (n = gnode(h, 0); n < limit; n++) {
     630            0 :       if (!ttisnil(gval(n)) && (iscleared(g, gkey(n)))) {
     631            0 :         setnilvalue(gval(n));  /* remove value ... */
     632            0 :         removeentry(n);  /* and remove entry from table */
     633              :       }
     634              :     }
     635              :   }
     636          430 : }
     637              : 
     638              : 
     639              : /*
     640              : ** clear entries with unmarked values from all weaktables in list 'l' up
     641              : ** to element 'f'
     642              : */
     643          860 : static void clearvalues (global_State *g, GCObject *l, GCObject *f) {
     644          860 :   for (; l != f; l = gco2t(l)->gclist) {
     645            0 :     Table *h = gco2t(l);
     646            0 :     Node *n, *limit = gnodelast(h);
     647              :     int i;
     648            0 :     for (i = 0; i < h->sizearray; i++) {
     649            0 :       TValue *o = &h->array[i];
     650            0 :       if (iscleared(g, o))  /* value was collected? */
     651            0 :         setnilvalue(o);  /* remove value */
     652              :     }
     653            0 :     for (n = gnode(h, 0); n < limit; n++) {
     654            0 :       if (!ttisnil(gval(n)) && iscleared(g, gval(n))) {
     655            0 :         setnilvalue(gval(n));  /* remove value ... */
     656            0 :         removeentry(n);  /* and remove entry from table */
     657              :       }
     658              :     }
     659              :   }
     660          860 : }
     661              : 
     662              : 
     663        65676 : static void freeobj (lua_State *L, GCObject *o) {
     664        65676 :   switch (gch(o)->tt) {
     665         2056 :     case LUA_TPROTO: luaF_freeproto(L, gco2p(o)); break;
     666         3220 :     case LUA_TLCL: {
     667         3220 :       luaM_freemem(L, o, sizeLclosure(gco2lcl(o)->nupvalues));
     668         3220 :       break;
     669              :     }
     670          481 :     case LUA_TCCL: {
     671          481 :       luaM_freemem(L, o, sizeCclosure(gco2ccl(o)->nupvalues));
     672          481 :       break;
     673              :     }
     674         6045 :     case LUA_TUPVAL: luaF_freeupval(L, gco2uv(o)); break;
     675        10150 :     case LUA_TTABLE: luaH_free(L, gco2t(o)); break;
     676           26 :     case LUA_TTHREAD: luaE_freethread(L, gco2th(o)); break;
     677          362 :     case LUA_TUSERDATA: luaM_freemem(L, o, sizeudata(gco2u(o))); break;
     678        39082 :     case LUA_TSHRSTR:
     679        39082 :       G(L)->strt.nuse--;
     680              :       /* go through */
     681        43336 :     case LUA_TLNGSTR: {
     682        43336 :       luaM_freemem(L, o, sizestring(gco2ts(o)));
     683        43336 :       break;
     684              :     }
     685        65676 :     default: lua_assert(0);
     686              :   }
     687        65676 : }
     688              : 
     689              : 
     690              : #define sweepwholelist(L,p)     sweeplist(L,p,MAX_LUMEM)
     691              : static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count);
     692              : 
     693              : 
     694              : /*
     695              : ** sweep the (open) upvalues of a thread and resize its stack and
     696              : ** list of call-info structures.
     697              : */
     698          202 : static void sweepthread (lua_State *L, lua_State *L1) {
     699          202 :   if (L1->stack == NULL) return;  /* stack not completely built yet */
     700          202 :   sweepwholelist(L, &L1->openupval);  /* sweep open upvalues */
     701          202 :   luaE_freeCI(L1);  /* free extra CallInfo slots */
     702              :   /* should not change the stack during an emergency gc cycle */
     703          202 :   if (G(L)->gckind != KGC_EMERGENCY)
     704          202 :     luaD_shrinkstack(L1);
     705              : }
     706              : 
     707              : 
     708              : /*
     709              : ** sweep at most 'count' elements from a list of GCObjects erasing dead
     710              : ** objects, where a dead (not alive) object is one marked with the "old"
     711              : ** (non current) white and not fixed.
     712              : ** In non-generational mode, change all non-dead objects back to white,
     713              : ** preparing for next collection cycle.
     714              : ** In generational mode, keep black objects black, and also mark them as
     715              : ** old; stop when hitting an old object, as all objects after that
     716              : ** one will be old too.
     717              : ** When object is a thread, sweep its list of open upvalues too.
     718              : */
     719       124593 : static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count) {
     720       124593 :   global_State *g = G(L);
     721       124593 :   int ow = otherwhite(g);
     722              :   int toclear, toset;  /* bits to clear and to set in all live objects */
     723              :   int tostop;  /* stop sweep when this is true */
     724       124593 :   if (isgenerational(g)) {  /* generational mode? */
     725        10275 :     toclear = ~0;  /* clear nothing */
     726        10275 :     toset = bitmask(OLDBIT);  /* set the old bit of all surviving objects */
     727        10275 :     tostop = bitmask(OLDBIT);  /* do not sweep old generation */
     728              :   }
     729              :   else {  /* normal mode */
     730       114318 :     toclear = maskcolors;  /* clear all color bits + old bit */
     731       114318 :     toset = luaC_white(g);  /* make object white */
     732       114318 :     tostop = 0;  /* do not stop */
     733              :   }
     734       237951 :   while (*p != NULL && count-- > 0) {
     735       117201 :     GCObject *curr = *p;
     736       117201 :     int marked = gch(curr)->marked;
     737       117201 :     if (isdeadm(ow, marked)) {  /* is 'curr' dead? */
     738        65676 :       *p = gch(curr)->next;  /* remove 'curr' from list */
     739        65676 :       freeobj(L, curr);  /* erase 'curr' */
     740              :     }
     741              :     else {
     742        51525 :       if (testbits(marked, tostop))
     743         3843 :         return NULL;  /* stop sweeping this list */
     744        47682 :       if (gch(curr)->tt == LUA_TTHREAD)
     745          202 :         sweepthread(L, gco2th(curr));  /* sweep thread's upvalues */
     746              :       /* update marks */
     747        47682 :       gch(curr)->marked = cast_byte((marked & toclear) | toset);
     748        47682 :       p = &gch(curr)->next;  /* go to next element */
     749              :     }
     750              :   }
     751       120750 :   return (*p == NULL) ? NULL : p;
     752              : }
     753              : 
     754              : 
     755              : /*
     756              : ** sweep a list until a live object (or end of list)
     757              : */
     758          444 : static GCObject **sweeptolive (lua_State *L, GCObject **p, int *n) {
     759          444 :   GCObject ** old = p;
     760          444 :   int i = 0;
     761              :   do {
     762          607 :     i++;
     763          607 :     p = sweeplist(L, p, 1);
     764          607 :   } while (p == old);
     765          444 :   if (n) *n += i;
     766          444 :   return p;
     767              : }
     768              : 
     769              : /* }====================================================== */
     770              : 
     771              : 
     772              : /*
     773              : ** {======================================================
     774              : ** Finalization
     775              : ** =======================================================
     776              : */
     777              : 
     778          200 : static void checkSizes (lua_State *L) {
     779          200 :   global_State *g = G(L);
     780          200 :   if (g->gckind != KGC_EMERGENCY) {  /* do not change sizes in emergency */
     781          200 :     int hs = g->strt.size / 2;  /* half the size of the string table */
     782          200 :     if (g->strt.nuse < cast(lu_int32, hs))  /* using less than that half? */
     783           18 :       luaS_resize(L, hs);  /* halve its size */
     784          200 :     luaZ_freebuffer(L, &g->buff);  /* free concatenation buffer */
     785              :   }
     786          200 : }
     787              : 
     788              : 
     789          434 : static GCObject *udata2finalize (global_State *g) {
     790          434 :   GCObject *o = g->tobefnz;  /* get first element */
     791              :   lua_assert(isfinalized(o));
     792          434 :   g->tobefnz = gch(o)->next;  /* remove it from 'tobefnz' list */
     793          434 :   gch(o)->next = g->allgc;  /* return it to 'allgc' list */
     794          434 :   g->allgc = o;
     795          434 :   resetbit(gch(o)->marked, SEPARATED);  /* mark that it is not in 'tobefnz' */
     796              :   lua_assert(!isold(o));  /* see MOVE OLD rule */
     797          434 :   if (!keepinvariantout(g))  /* not keeping invariant? */
     798           67 :     makewhite(g, o);  /* "sweep" object */
     799          434 :   return o;
     800              : }
     801              : 
     802              : 
     803          434 : static void dothecall (lua_State *L, void *ud) {
     804              :   UNUSED(ud);
     805          434 :   luaD_call(L, L->top - 2, 0, 0);
     806          434 : }
     807              : 
     808              : 
     809          434 : static void GCTM (lua_State *L, int propagateerrors) {
     810          434 :   global_State *g = G(L);
     811              :   const TValue *tm;
     812              :   TValue v;
     813          434 :   setgcovalue(L, &v, udata2finalize(g));
     814          434 :   tm = luaT_gettmbyobj(L, &v, TM_GC);
     815          434 :   if (tm != NULL && ttisfunction(tm)) {  /* is there a finalizer? */
     816              :     int status;
     817          434 :     lu_byte oldah = L->allowhook;
     818          434 :     int running  = g->gcrunning;
     819          434 :     L->allowhook = 0;  /* stop debug hooks during GC metamethod */
     820          434 :     g->gcrunning = 0;  /* avoid GC steps */
     821          434 :     setobj2s(L, L->top, tm);  /* push finalizer... */
     822          434 :     setobj2s(L, L->top + 1, &v);  /* ... and its argument */
     823          434 :     L->top += 2;  /* and (next line) call the finalizer */
     824          434 :     status = luaD_pcall(L, dothecall, NULL, savestack(L, L->top - 2), 0);
     825          434 :     L->allowhook = oldah;  /* restore hooks */
     826          434 :     g->gcrunning = running;  /* restore state */
     827          434 :     if (status != LUA_OK && propagateerrors) {  /* error while running __gc? */
     828            0 :       if (status == LUA_ERRRUN) {  /* is there an error object? */
     829            0 :         const char *msg = (ttisstring(L->top - 1))
     830            0 :                             ? svalue(L->top - 1)
     831            0 :                             : "no message";
     832            0 :         luaO_pushfstring(L, "error in __gc metamethod (%s)", msg);
     833            0 :         status = LUA_ERRGCMM;  /* error in __gc metamethod */
     834              :       }
     835            0 :       luaD_throw(L, status);  /* re-throw error */
     836              :     }
     837              :   }
     838          434 : }
     839              : 
     840              : 
     841              : /*
     842              : ** move all unreachable objects (or 'all' objects) that need
     843              : ** finalization from list 'finobj' to list 'tobefnz' (to be finalized)
     844              : */
     845          303 : static void separatetobefnz (lua_State *L, int all) {
     846          303 :   global_State *g = G(L);
     847          303 :   GCObject **p = &g->finobj;
     848              :   GCObject *curr;
     849          303 :   GCObject **lastnext = &g->tobefnz;
     850              :   /* find last 'next' field in 'tobefnz' list (to add elements in its end) */
     851          303 :   while (*lastnext != NULL)
     852            0 :     lastnext = &gch(*lastnext)->next;
     853         1112 :   while ((curr = *p) != NULL) {  /* traverse all finalizable objects */
     854              :     lua_assert(!isfinalized(curr));
     855              :     lua_assert(testbit(gch(curr)->marked, SEPARATED));
     856          809 :     if (!(iswhite(curr) || all))  /* not being collected? */
     857          375 :       p = &gch(curr)->next;  /* don't bother with it */
     858              :     else {
     859          434 :       l_setbit(gch(curr)->marked, FINALIZEDBIT); /* won't be finalized again */
     860          434 :       *p = gch(curr)->next;  /* remove 'curr' from 'finobj' list */
     861          434 :       gch(curr)->next = *lastnext;  /* link at the end of 'tobefnz' list */
     862          434 :       *lastnext = curr;
     863          434 :       lastnext = &gch(curr)->next;
     864              :     }
     865              :   }
     866          303 : }
     867              : 
     868              : 
     869              : /*
     870              : ** if object 'o' has a finalizer, remove it from 'allgc' list (must
     871              : ** search the list to find it) and link it in 'finobj' list.
     872              : */
     873          568 : void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) {
     874          568 :   global_State *g = G(L);
     875          568 :   if (testbit(gch(o)->marked, SEPARATED) || /* obj. is already separated... */
     876          568 :       isfinalized(o) ||                           /* ... or is finalized... */
     877          568 :       gfasttm(g, mt, TM_GC) == NULL)                /* or has no finalizer? */
     878           98 :     return;  /* nothing to be done */
     879              :   else {  /* move 'o' to 'finobj' list */
     880              :     GCObject **p;
     881          470 :     GCheader *ho = gch(o);
     882          470 :     if (g->sweepgc == &ho->next) {  /* avoid removing current sweep object */
     883              :       lua_assert(issweepphase(g));
     884            0 :       g->sweepgc = sweeptolive(L, g->sweepgc, NULL);
     885              :     }
     886              :     /* search for pointer pointing to 'o' */
     887          556 :     for (p = &g->allgc; *p != o; p = &gch(*p)->next) { /* empty */ }
     888          470 :     *p = ho->next;  /* remove 'o' from root list */
     889          470 :     ho->next = g->finobj;  /* link it in list 'finobj' */
     890          470 :     g->finobj = o;
     891          470 :     l_setbit(ho->marked, SEPARATED);  /* mark it as such */
     892          470 :     if (!keepinvariantout(g))  /* not keeping invariant? */
     893          347 :       makewhite(g, o);  /* "sweep" object */
     894              :     else
     895          123 :       resetoldbit(o);  /* see MOVE OLD rule */
     896              :   }
     897              : }
     898              : 
     899              : /* }====================================================== */
     900              : 
     901              : 
     902              : /*
     903              : ** {======================================================
     904              : ** GC control
     905              : ** =======================================================
     906              : */
     907              : 
     908              : 
     909              : /*
     910              : ** set a reasonable "time" to wait before starting a new GC cycle;
     911              : ** cycle will start when memory use hits threshold
     912              : */
     913          193 : static void setpause (global_State *g, l_mem estimate) {
     914              :   l_mem debt, threshold;
     915          193 :   estimate = estimate / PAUSEADJ;  /* adjust 'estimate' */
     916          386 :   threshold = (g->gcpause < MAX_LMEM / estimate)  /* overflow? */
     917          193 :             ? estimate * g->gcpause  /* no overflow */
     918          193 :             : MAX_LMEM;  /* overflow; truncate to maximum */
     919          193 :   debt = -cast(l_mem, threshold - gettotalbytes(g));
     920          193 :   luaE_setdebt(g, debt);
     921          193 : }
     922              : 
     923              : 
     924              : #define sweepphases  \
     925              :         (bitmask(GCSsweepstring) | bitmask(GCSsweepudata) | bitmask(GCSsweep))
     926              : 
     927              : 
     928              : /*
     929              : ** enter first sweep phase (strings) and prepare pointers for other
     930              : ** sweep phases.  The calls to 'sweeptolive' make pointers point to an
     931              : ** object inside the list (instead of to the header), so that the real
     932              : ** sweep do not need to skip objects created between "now" and the start
     933              : ** of the real sweep.
     934              : ** Returns how many objects it swept.
     935              : */
     936          222 : static int entersweep (lua_State *L) {
     937          222 :   global_State *g = G(L);
     938          222 :   int n = 0;
     939          222 :   g->gcstate = GCSsweepstring;
     940              :   lua_assert(g->sweepgc == NULL && g->sweepfin == NULL);
     941              :   /* prepare to sweep strings, finalizable objects, and regular objects */
     942          222 :   g->sweepstrgc = 0;
     943          222 :   g->sweepfin = sweeptolive(L, &g->finobj, &n);
     944          222 :   g->sweepgc = sweeptolive(L, &g->allgc, &n);
     945          222 :   return n;
     946              : }
     947              : 
     948              : 
     949              : /*
     950              : ** change GC mode
     951              : */
     952            2 : void luaC_changemode (lua_State *L, int mode) {
     953            2 :   global_State *g = G(L);
     954            2 :   if (mode == g->gckind) return;  /* nothing to change */
     955            2 :   if (mode == KGC_GEN) {  /* change to generational mode */
     956              :     /* make sure gray lists are consistent */
     957            1 :     luaC_runtilstate(L, bitmask(GCSpropagate));
     958            1 :     g->GCestimate = gettotalbytes(g);
     959            1 :     g->gckind = KGC_GEN;
     960              :   }
     961              :   else {  /* change to incremental mode */
     962              :     /* sweep all objects to turn them back to white
     963              :        (as white has not changed, nothing extra will be collected) */
     964            1 :     g->gckind = KGC_NORMAL;
     965            1 :     entersweep(L);
     966            1 :     luaC_runtilstate(L, ~sweepphases);
     967              :   }
     968              : }
     969              : 
     970              : 
     971              : /*
     972              : ** call all pending finalizers
     973              : */
     974          102 : static void callallpendingfinalizers (lua_State *L, int propagateerrors) {
     975          102 :   global_State *g = G(L);
     976          527 :   while (g->tobefnz) {
     977          425 :     resetoldbit(g->tobefnz);
     978          425 :     GCTM(L, propagateerrors);
     979              :   }
     980          102 : }
     981              : 
     982              : 
     983           88 : void luaC_freeallobjects (lua_State *L) {
     984           88 :   global_State *g = G(L);
     985              :   int i;
     986           88 :   separatetobefnz(L, 1);  /* separate all objects with finalizers */
     987              :   lua_assert(g->finobj == NULL);
     988           88 :   callallpendingfinalizers(L, 0);
     989           88 :   g->currentwhite = WHITEBITS; /* this "white" makes all objects look dead */
     990           88 :   g->gckind = KGC_NORMAL;
     991           88 :   sweepwholelist(L, &g->finobj);  /* finalizers can create objs. in 'finobj' */
     992           88 :   sweepwholelist(L, &g->allgc);
     993        39448 :   for (i = 0; i < g->strt.size; i++)  /* free all string lists */
     994        39360 :     sweepwholelist(L, &g->strt.hash[i]);
     995              :   lua_assert(g->strt.nuse == 0);
     996           88 : }
     997              : 
     998              : 
     999          215 : static l_mem atomic (lua_State *L) {
    1000          215 :   global_State *g = G(L);
    1001          215 :   l_mem work = -cast(l_mem, g->GCmemtrav);  /* start counting work */
    1002              :   GCObject *origweak, *origall;
    1003              :   lua_assert(!iswhite(obj2gco(g->mainthread)));
    1004          215 :   markobject(g, L);  /* mark running thread */
    1005              :   /* registry and global metatables may be changed by API */
    1006          215 :   markvalue(g, &g->l_registry);
    1007          215 :   markmt(g);  /* mark basic metatables */
    1008              :   /* remark occasional upvalues of (maybe) dead threads */
    1009          215 :   remarkupvals(g);
    1010          215 :   propagateall(g);  /* propagate changes */
    1011          215 :   work += g->GCmemtrav;  /* stop counting (do not (re)count grays) */
    1012              :   /* traverse objects caught by write barrier and by 'remarkupvals' */
    1013          215 :   retraversegrays(g);
    1014          215 :   work -= g->GCmemtrav;  /* restart counting */
    1015          215 :   convergeephemerons(g);
    1016              :   /* at this point, all strongly accessible objects are marked. */
    1017              :   /* clear values from weak tables, before checking finalizers */
    1018          215 :   clearvalues(g, g->weak, NULL);
    1019          215 :   clearvalues(g, g->allweak, NULL);
    1020          215 :   origweak = g->weak; origall = g->allweak;
    1021          215 :   work += g->GCmemtrav;  /* stop counting (objects being finalized) */
    1022          215 :   separatetobefnz(L, 0);  /* separate objects to be finalized */
    1023          215 :   markbeingfnz(g);  /* mark objects that will be finalized */
    1024          215 :   propagateall(g);  /* remark, to propagate `preserveness' */
    1025          215 :   work -= g->GCmemtrav;  /* restart counting */
    1026          215 :   convergeephemerons(g);
    1027              :   /* at this point, all resurrected objects are marked. */
    1028              :   /* remove dead objects from weak tables */
    1029          215 :   clearkeys(g, g->ephemeron, NULL);  /* clear keys from all ephemeron tables */
    1030          215 :   clearkeys(g, g->allweak, NULL);  /* clear keys from all allweak tables */
    1031              :   /* clear values from resurrected weak tables */
    1032          215 :   clearvalues(g, g->weak, origweak);
    1033          215 :   clearvalues(g, g->allweak, origall);
    1034          215 :   g->currentwhite = cast_byte(otherwhite(g));  /* flip current white */
    1035          215 :   work += g->GCmemtrav;  /* complete counting */
    1036          215 :   return work;  /* estimate of memory marked by 'atomic' */
    1037              : }
    1038              : 
    1039              : 
    1040        10521 : static lu_mem singlestep (lua_State *L) {
    1041        10521 :   global_State *g = G(L);
    1042        10521 :   switch (g->gcstate) {
    1043          284 :     case GCSpause: {
    1044              :       /* start to count memory traversed */
    1045          284 :       g->GCmemtrav = g->strt.size * sizeof(GCObject*);
    1046              :       lua_assert(!isgenerational(g));
    1047          284 :       restartcollection(g);
    1048          284 :       g->gcstate = GCSpropagate;
    1049          284 :       return g->GCmemtrav;
    1050              :     }
    1051         8223 :     case GCSpropagate: {
    1052         8223 :       if (g->gray) {
    1053         8008 :         lu_mem oldtrav = g->GCmemtrav;
    1054         8008 :         propagatemark(g);
    1055         8008 :         return g->GCmemtrav - oldtrav;  /* memory traversed in this step */
    1056              :       }
    1057              :       else {  /* no more `gray' objects */
    1058              :         lu_mem work;
    1059              :         int sw;
    1060          215 :         g->gcstate = GCSatomic;  /* finish mark phase */
    1061          215 :         g->GCestimate = g->GCmemtrav;  /* save what was counted */;
    1062          215 :         work = atomic(L);  /* add what was traversed by 'atomic' */
    1063          215 :         g->GCestimate += work;  /* estimate of total memory traversed */ 
    1064          215 :         sw = entersweep(L);
    1065          215 :         return work + sw * GCSWEEPCOST;
    1066              :       }
    1067              :     }
    1068         1105 :     case GCSsweepstring: {
    1069              :       int i;
    1070        84662 :       for (i = 0; i < GCSWEEPMAX && g->sweepstrgc + i < g->strt.size; i++)
    1071        83557 :         sweepwholelist(L, &g->strt.hash[g->sweepstrgc + i]);
    1072         1105 :       g->sweepstrgc += i;
    1073         1105 :       if (g->sweepstrgc >= g->strt.size)  /* no more strings to sweep? */
    1074          218 :         g->gcstate = GCSsweepudata;
    1075         1105 :       return i * GCSWEEPCOST;
    1076              :     }
    1077          304 :     case GCSsweepudata: {
    1078          304 :       if (g->sweepfin) {
    1079           86 :         g->sweepfin = sweeplist(L, g->sweepfin, GCSWEEPMAX);
    1080           86 :         return GCSWEEPMAX*GCSWEEPCOST;
    1081              :       }
    1082              :       else {
    1083          218 :         g->gcstate = GCSsweep;
    1084          218 :         return 0;
    1085              :       }
    1086              :     }
    1087          605 :     case GCSsweep: {
    1088          605 :       if (g->sweepgc) {
    1089          405 :         g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX);
    1090          405 :         return GCSWEEPMAX*GCSWEEPCOST;
    1091              :       }
    1092              :       else {
    1093              :         /* sweep main thread */
    1094          200 :         GCObject *mt = obj2gco(g->mainthread);
    1095          200 :         sweeplist(L, &mt, 1);
    1096          200 :         checkSizes(L);
    1097          200 :         g->gcstate = GCSpause;  /* finish collection */
    1098          200 :         return GCSWEEPCOST;
    1099              :       }
    1100              :     }
    1101            0 :     default: lua_assert(0); return 0;
    1102              :   }
    1103              : }
    1104              : 
    1105              : 
    1106              : /*
    1107              : ** advances the garbage collector until it reaches a state allowed
    1108              : ** by 'statemask'
    1109              : */
    1110          512 : void luaC_runtilstate (lua_State *L, int statesmask) {
    1111          512 :   global_State *g = G(L);
    1112         1275 :   while (!testbit(statesmask, g->gcstate))
    1113          763 :     singlestep(L);
    1114          512 : }
    1115              : 
    1116              : 
    1117           10 : static void generationalcollection (lua_State *L) {
    1118           10 :   global_State *g = G(L);
    1119              :   lua_assert(g->gcstate == GCSpropagate);
    1120           10 :   if (g->GCestimate == 0) {  /* signal for another major collection? */
    1121            0 :     luaC_fullgc(L, 0);  /* perform a full regular collection */
    1122            0 :     g->GCestimate = gettotalbytes(g);  /* update control */
    1123              :   }
    1124              :   else {
    1125           10 :     lu_mem estimate = g->GCestimate;
    1126           10 :     luaC_runtilstate(L, bitmask(GCSpause));  /* run complete (minor) cycle */
    1127           10 :     g->gcstate = GCSpropagate;  /* skip restart */
    1128           10 :     if (gettotalbytes(g) > (estimate / 100) * g->gcmajorinc)
    1129            0 :       g->GCestimate = 0;  /* signal for a major collection */
    1130              :     else
    1131           10 :       g->GCestimate = estimate;  /* keep estimate from last major coll. */
    1132              : 
    1133              :   }
    1134           10 :   setpause(g, gettotalbytes(g));
    1135              :   lua_assert(g->gcstate == GCSpropagate);
    1136           10 : }
    1137              : 
    1138              : 
    1139         1408 : static void incstep (lua_State *L) {
    1140         1408 :   global_State *g = G(L);
    1141         1408 :   l_mem debt = g->GCdebt;
    1142         1408 :   int stepmul = g->gcstepmul;
    1143         1408 :   if (stepmul < 40) stepmul = 40;  /* avoid ridiculous low values (and 0) */
    1144              :   /* convert debt from Kb to 'work units' (avoid zero debt and overflows) */
    1145         1408 :   debt = (debt / STEPMULADJ) + 1;
    1146         1408 :   debt = (debt < MAX_LMEM / stepmul) ? debt * stepmul : MAX_LMEM;
    1147              :   do {  /* always perform at least one single step */
    1148         9758 :     lu_mem work = singlestep(L);  /* do some work */
    1149         9758 :     debt -= work;
    1150         9758 :   } while (debt > -GCSTEPSIZE && g->gcstate != GCSpause);
    1151         1408 :   if (g->gcstate == GCSpause)
    1152          176 :     setpause(g, g->GCestimate);  /* pause until next cycle */
    1153              :   else {
    1154         1232 :     debt = (debt / stepmul) * STEPMULADJ;  /* convert 'work units' to Kb */
    1155         1232 :     luaE_setdebt(g, debt);
    1156              :   }
    1157         1408 : }
    1158              : 
    1159              : 
    1160              : /*
    1161              : ** performs a basic GC step
    1162              : */
    1163         1418 : void luaC_forcestep (lua_State *L) {
    1164         1418 :   global_State *g = G(L);
    1165              :   int i;
    1166         1418 :   if (isgenerational(g)) generationalcollection(L);
    1167         1408 :   else incstep(L);
    1168              :   /* run a few finalizers (or all of them at the end of a collect cycle) */
    1169         1427 :   for (i = 0; g->tobefnz && (i < GCFINALIZENUM || g->gcstate == GCSpause); i++)
    1170            9 :     GCTM(L, 1);  /* call one finalizer */
    1171         1418 : }
    1172              : 
    1173              : 
    1174              : /*
    1175              : ** performs a basic GC step only if collector is running
    1176              : */
    1177         1933 : void luaC_step (lua_State *L) {
    1178         1933 :   global_State *g = G(L);
    1179         1933 :   if (g->gcrunning) luaC_forcestep(L);
    1180          518 :   else luaE_setdebt(g, -GCSTEPSIZE);  /* avoid being called too often */
    1181         1933 : }
    1182              : 
    1183              : 
    1184              : 
    1185              : /*
    1186              : ** performs a full GC cycle; if "isemergency", does not call
    1187              : ** finalizers (which could change stack positions)
    1188              : */
    1189            7 : void luaC_fullgc (lua_State *L, int isemergency) {
    1190            7 :   global_State *g = G(L);
    1191            7 :   int origkind = g->gckind;
    1192              :   lua_assert(origkind != KGC_EMERGENCY);
    1193            7 :   if (isemergency)  /* do not run finalizers during emergency GC */
    1194            0 :     g->gckind = KGC_EMERGENCY;
    1195              :   else {
    1196            7 :     g->gckind = KGC_NORMAL;
    1197            7 :     callallpendingfinalizers(L, 1);
    1198              :   }
    1199            7 :   if (keepinvariant(g)) {  /* may there be some black objects? */
    1200              :     /* must sweep all objects to turn them back to white
    1201              :        (as white has not changed, nothing will be collected) */
    1202            6 :     entersweep(L);
    1203              :   }
    1204              :   /* finish any pending sweep phase to start a new cycle */
    1205            7 :   luaC_runtilstate(L, bitmask(GCSpause));
    1206            7 :   luaC_runtilstate(L, ~bitmask(GCSpause));  /* start new collection */
    1207            7 :   luaC_runtilstate(L, bitmask(GCSpause));  /* run entire collection */
    1208            7 :   if (origkind == KGC_GEN) {  /* generational mode? */
    1209              :     /* generational mode must be kept in propagate phase */
    1210            0 :     luaC_runtilstate(L, bitmask(GCSpropagate));
    1211              :   }
    1212            7 :   g->gckind = origkind;
    1213            7 :   setpause(g, gettotalbytes(g));
    1214            7 :   if (!isemergency)   /* do not run finalizers during emergency GC */
    1215            7 :     callallpendingfinalizers(L, 1);
    1216            7 : }
    1217              : 
    1218              : /* }====================================================== */
    1219              : 
    1220              : 
        

Generated by: LCOV version 2.0-1