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