| 1 | /* |
|---|
| 2 | ** $Id: lgc.c,v 2.38.1.1 2007/12/27 13:02:25 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 | #define GCSTEPSIZE 1024u |
|---|
| 27 | #define GCSWEEPMAX 40 |
|---|
| 28 | #define GCSWEEPCOST 10 |
|---|
| 29 | #define GCFINALIZECOST 100 |
|---|
| 30 | |
|---|
| 31 | |
|---|
| 32 | #define maskmarks cast_byte(~(bitmask(BLACKBIT)|WHITEBITS)) |
|---|
| 33 | |
|---|
| 34 | #define makewhite(g,x) \ |
|---|
| 35 | ((x)->gch.marked = cast_byte(((x)->gch.marked & maskmarks) | luaC_white(g))) |
|---|
| 36 | |
|---|
| 37 | #define white2gray(x) reset2bits((x)->gch.marked, WHITE0BIT, WHITE1BIT) |
|---|
| 38 | #define black2gray(x) resetbit((x)->gch.marked, BLACKBIT) |
|---|
| 39 | |
|---|
| 40 | #define stringmark(s) reset2bits((s)->tsv.marked, WHITE0BIT, WHITE1BIT) |
|---|
| 41 | |
|---|
| 42 | |
|---|
| 43 | #define isfinalized(u) testbit((u)->marked, FINALIZEDBIT) |
|---|
| 44 | #define markfinalized(u) l_setbit((u)->marked, FINALIZEDBIT) |
|---|
| 45 | |
|---|
| 46 | |
|---|
| 47 | #define KEYWEAK bitmask(KEYWEAKBIT) |
|---|
| 48 | #define VALUEWEAK bitmask(VALUEWEAKBIT) |
|---|
| 49 | |
|---|
| 50 | |
|---|
| 51 | |
|---|
| 52 | #define markvalue(g,o) { checkconsistency(o); \ |
|---|
| 53 | if (iscollectable(o) && iswhite(gcvalue(o))) reallymarkobject(g,gcvalue(o)); } |
|---|
| 54 | |
|---|
| 55 | #define markobject(g,t) { if (iswhite(obj2gco(t))) \ |
|---|
| 56 | reallymarkobject(g, obj2gco(t)); } |
|---|
| 57 | |
|---|
| 58 | |
|---|
| 59 | #define setthreshold(g) (g->GCthreshold = (g->estimate/100) * g->gcpause) |
|---|
| 60 | |
|---|
| 61 | |
|---|
| 62 | static void removeentry (Node *n) { |
|---|
| 63 | lua_assert(ttisnil(gval(n))); |
|---|
| 64 | if (iscollectable(gkey(n))) |
|---|
| 65 | setttype(gkey(n), LUA_TDEADKEY); /* dead key; remove it */ |
|---|
| 66 | } |
|---|
| 67 | |
|---|
| 68 | |
|---|
| 69 | static void reallymarkobject (global_State *g, GCObject *o) { |
|---|
| 70 | lua_assert(iswhite(o) && !isdead(g, o)); |
|---|
| 71 | white2gray(o); |
|---|
| 72 | switch (o->gch.tt) { |
|---|
| 73 | case LUA_TSTRING: { |
|---|
| 74 | return; |
|---|
| 75 | } |
|---|
| 76 | case LUA_TUSERDATA: { |
|---|
| 77 | Table *mt = gco2u(o)->metatable; |
|---|
| 78 | gray2black(o); /* udata are never gray */ |
|---|
| 79 | if (mt) markobject(g, mt); |
|---|
| 80 | markobject(g, gco2u(o)->env); |
|---|
| 81 | return; |
|---|
| 82 | } |
|---|
| 83 | case LUA_TUPVAL: { |
|---|
| 84 | UpVal *uv = gco2uv(o); |
|---|
| 85 | markvalue(g, uv->v); |
|---|
| 86 | if (uv->v == &uv->u.value) /* closed? */ |
|---|
| 87 | gray2black(o); /* open upvalues are never black */ |
|---|
| 88 | return; |
|---|
| 89 | } |
|---|
| 90 | case LUA_TFUNCTION: { |
|---|
| 91 | gco2cl(o)->c.gclist = g->gray; |
|---|
| 92 | g->gray = o; |
|---|
| 93 | break; |
|---|
| 94 | } |
|---|
| 95 | case LUA_TTABLE: { |
|---|
| 96 | gco2h(o)->gclist = g->gray; |
|---|
| 97 | g->gray = o; |
|---|
| 98 | break; |
|---|
| 99 | } |
|---|
| 100 | case LUA_TTHREAD: { |
|---|
| 101 | gco2th(o)->gclist = g->gray; |
|---|
| 102 | g->gray = o; |
|---|
| 103 | break; |
|---|
| 104 | } |
|---|
| 105 | case LUA_TPROTO: { |
|---|
| 106 | gco2p(o)->gclist = g->gray; |
|---|
| 107 | g->gray = o; |
|---|
| 108 | break; |
|---|
| 109 | } |
|---|
| 110 | default: lua_assert(0); |
|---|
| 111 | } |
|---|
| 112 | } |
|---|
| 113 | |
|---|
| 114 | |
|---|
| 115 | static void marktmu (global_State *g) { |
|---|
| 116 | GCObject *u = g->tmudata; |
|---|
| 117 | if (u) { |
|---|
| 118 | do { |
|---|
| 119 | u = u->gch.next; |
|---|
| 120 | makewhite(g, u); /* may be marked, if left from previous GC */ |
|---|
| 121 | reallymarkobject(g, u); |
|---|
| 122 | } while (u != g->tmudata); |
|---|
| 123 | } |
|---|
| 124 | } |
|---|
| 125 | |
|---|
| 126 | |
|---|
| 127 | /* move `dead' udata that need finalization to list `tmudata' */ |
|---|
| 128 | size_t luaC_separateudata (lua_State *L, int all) { |
|---|
| 129 | global_State *g = G(L); |
|---|
| 130 | size_t deadmem = 0; |
|---|
| 131 | GCObject **p = &g->mainthread->next; |
|---|
| 132 | GCObject *curr; |
|---|
| 133 | while ((curr = *p) != NULL) { |
|---|
| 134 | if (!(iswhite(curr) || all) || isfinalized(gco2u(curr))) |
|---|
| 135 | p = &curr->gch.next; /* don't bother with them */ |
|---|
| 136 | else if (fasttm(L, gco2u(curr)->metatable, TM_GC) == NULL) { |
|---|
| 137 | markfinalized(gco2u(curr)); /* don't need finalization */ |
|---|
| 138 | p = &curr->gch.next; |
|---|
| 139 | } |
|---|
| 140 | else { /* must call its gc method */ |
|---|
| 141 | deadmem += sizeudata(gco2u(curr)); |
|---|
| 142 | markfinalized(gco2u(curr)); |
|---|
| 143 | *p = curr->gch.next; |
|---|
| 144 | /* link `curr' at the end of `tmudata' list */ |
|---|
| 145 | if (g->tmudata == NULL) /* list is empty? */ |
|---|
| 146 | g->tmudata = curr->gch.next = curr; /* creates a circular list */ |
|---|
| 147 | else { |
|---|
| 148 | curr->gch.next = g->tmudata->gch.next; |
|---|
| 149 | g->tmudata->gch.next = curr; |
|---|
| 150 | g->tmudata = curr; |
|---|
| 151 | } |
|---|
| 152 | } |
|---|
| 153 | } |
|---|
| 154 | return deadmem; |
|---|
| 155 | } |
|---|
| 156 | |
|---|
| 157 | |
|---|
| 158 | static int traversetable (global_State *g, Table *h) { |
|---|
| 159 | int i; |
|---|
| 160 | int weakkey = 0; |
|---|
| 161 | int weakvalue = 0; |
|---|
| 162 | const TValue *mode; |
|---|
| 163 | if (h->metatable) |
|---|
| 164 | markobject(g, h->metatable); |
|---|
| 165 | mode = gfasttm(g, h->metatable, TM_MODE); |
|---|
| 166 | if (mode && ttisstring(mode)) { /* is there a weak mode? */ |
|---|
| 167 | weakkey = (strchr(svalue(mode), 'k') != NULL); |
|---|
| 168 | weakvalue = (strchr(svalue(mode), 'v') != NULL); |
|---|
| 169 | if (weakkey || weakvalue) { /* is really weak? */ |
|---|
| 170 | h->marked &= ~(KEYWEAK | VALUEWEAK); /* clear bits */ |
|---|
| 171 | h->marked |= cast_byte((weakkey << KEYWEAKBIT) | |
|---|
| 172 | (weakvalue << VALUEWEAKBIT)); |
|---|
| 173 | h->gclist = g->weak; /* must be cleared after GC, ... */ |
|---|
| 174 | g->weak = obj2gco(h); /* ... so put in the appropriate list */ |
|---|
| 175 | } |
|---|
| 176 | } |
|---|
| 177 | if (weakkey && weakvalue) return 1; |
|---|
| 178 | if (!weakvalue) { |
|---|
| 179 | i = h->sizearray; |
|---|
| 180 | while (i--) |
|---|
| 181 | markvalue(g, &h->array[i]); |
|---|
| 182 | } |
|---|
| 183 | i = sizenode(h); |
|---|
| 184 | while (i--) { |
|---|
| 185 | Node *n = gnode(h, i); |
|---|
| 186 | lua_assert(ttype(gkey(n)) != LUA_TDEADKEY || ttisnil(gval(n))); |
|---|
| 187 | if (ttisnil(gval(n))) |
|---|
| 188 | removeentry(n); /* remove empty entries */ |
|---|
| 189 | else { |
|---|
| 190 | lua_assert(!ttisnil(gkey(n))); |
|---|
| 191 | if (!weakkey) markvalue(g, gkey(n)); |
|---|
| 192 | if (!weakvalue) markvalue(g, gval(n)); |
|---|
| 193 | } |
|---|
| 194 | } |
|---|
| 195 | return weakkey || weakvalue; |
|---|
| 196 | } |
|---|
| 197 | |
|---|
| 198 | |
|---|
| 199 | /* |
|---|
| 200 | ** All marks are conditional because a GC may happen while the |
|---|
| 201 | ** prototype is still being created |
|---|
| 202 | */ |
|---|
| 203 | static void traverseproto (global_State *g, Proto *f) { |
|---|
| 204 | int i; |
|---|
| 205 | if (f->source) stringmark(f->source); |
|---|
| 206 | for (i=0; i<f->sizek; i++) /* mark literals */ |
|---|
| 207 | markvalue(g, &f->k[i]); |
|---|
| 208 | for (i=0; i<f->sizeupvalues; i++) { /* mark upvalue names */ |
|---|
| 209 | if (f->upvalues[i]) |
|---|
| 210 | stringmark(f->upvalues[i]); |
|---|
| 211 | } |
|---|
| 212 | for (i=0; i<f->sizep; i++) { /* mark nested protos */ |
|---|
| 213 | if (f->p[i]) |
|---|
| 214 | markobject(g, f->p[i]); |
|---|
| 215 | } |
|---|
| 216 | for (i=0; i<f->sizelocvars; i++) { /* mark local-variable names */ |
|---|
| 217 | if (f->locvars[i].varname) |
|---|
| 218 | stringmark(f->locvars[i].varname); |
|---|
| 219 | } |
|---|
| 220 | } |
|---|
| 221 | |
|---|
| 222 | |
|---|
| 223 | |
|---|
| 224 | static void traverseclosure (global_State *g, Closure *cl) { |
|---|
| 225 | markobject(g, cl->c.env); |
|---|
| 226 | if (cl->c.isC) { |
|---|
| 227 | int i; |
|---|
| 228 | for (i=0; i<cl->c.nupvalues; i++) /* mark its upvalues */ |
|---|
| 229 | markvalue(g, &cl->c.upvalue[i]); |
|---|
| 230 | } |
|---|
| 231 | else { |
|---|
| 232 | int i; |
|---|
| 233 | lua_assert(cl->l.nupvalues == cl->l.p->nups); |
|---|
| 234 | markobject(g, cl->l.p); |
|---|
| 235 | for (i=0; i<cl->l.nupvalues; i++) /* mark its upvalues */ |
|---|
| 236 | markobject(g, cl->l.upvals[i]); |
|---|
| 237 | } |
|---|
| 238 | } |
|---|
| 239 | |
|---|
| 240 | |
|---|
| 241 | static void checkstacksizes (lua_State *L, StkId max) { |
|---|
| 242 | int ci_used = cast_int(L->ci - L->base_ci); /* number of `ci' in use */ |
|---|
| 243 | int s_used = cast_int(max - L->stack); /* part of stack in use */ |
|---|
| 244 | if (L->size_ci > LUAI_MAXCALLS) /* handling overflow? */ |
|---|
| 245 | return; /* do not touch the stacks */ |
|---|
| 246 | if (4*ci_used < L->size_ci && 2*BASIC_CI_SIZE < L->size_ci) |
|---|
| 247 | luaD_reallocCI(L, L->size_ci/2); /* still big enough... */ |
|---|
| 248 | condhardstacktests(luaD_reallocCI(L, ci_used + 1)); |
|---|
| 249 | if (4*s_used < L->stacksize && |
|---|
| 250 | 2*(BASIC_STACK_SIZE+EXTRA_STACK) < L->stacksize) |
|---|
| 251 | luaD_reallocstack(L, L->stacksize/2); /* still big enough... */ |
|---|
| 252 | condhardstacktests(luaD_reallocstack(L, s_used)); |
|---|
| 253 | } |
|---|
| 254 | |
|---|
| 255 | |
|---|
| 256 | static void traversestack (global_State *g, lua_State *l) { |
|---|
| 257 | StkId o, lim; |
|---|
| 258 | CallInfo *ci; |
|---|
| 259 | markvalue(g, gt(l)); |
|---|
| 260 | lim = l->top; |
|---|
| 261 | for (ci = l->base_ci; ci <= l->ci; ci++) { |
|---|
| 262 | lua_assert(ci->top <= l->stack_last); |
|---|
| 263 | if (lim < ci->top) lim = ci->top; |
|---|
| 264 | } |
|---|
| 265 | for (o = l->stack; o < l->top; o++) |
|---|
| 266 | markvalue(g, o); |
|---|
| 267 | for (; o <= lim; o++) |
|---|
| 268 | setnilvalue(o); |
|---|
| 269 | checkstacksizes(l, lim); |
|---|
| 270 | } |
|---|
| 271 | |
|---|
| 272 | |
|---|
| 273 | /* |
|---|
| 274 | ** traverse one gray object, turning it to black. |
|---|
| 275 | ** Returns `quantity' traversed. |
|---|
| 276 | */ |
|---|
| 277 | static l_mem propagatemark (global_State *g) { |
|---|
| 278 | GCObject *o = g->gray; |
|---|
| 279 | lua_assert(isgray(o)); |
|---|
| 280 | gray2black(o); |
|---|
| 281 | switch (o->gch.tt) { |
|---|
| 282 | case LUA_TTABLE: { |
|---|
| 283 | Table *h = gco2h(o); |
|---|
| 284 | g->gray = h->gclist; |
|---|
| 285 | if (traversetable(g, h)) /* table is weak? */ |
|---|
| 286 | black2gray(o); /* keep it gray */ |
|---|
| 287 | return sizeof(Table) + sizeof(TValue) * h->sizearray + |
|---|
| 288 | sizeof(Node) * sizenode(h); |
|---|
| 289 | } |
|---|
| 290 | case LUA_TFUNCTION: { |
|---|
| 291 | Closure *cl = gco2cl(o); |
|---|
| 292 | g->gray = cl->c.gclist; |
|---|
| 293 | traverseclosure(g, cl); |
|---|
| 294 | return (cl->c.isC) ? sizeCclosure(cl->c.nupvalues) : |
|---|
| 295 | sizeLclosure(cl->l.nupvalues); |
|---|
| 296 | } |
|---|
| 297 | case LUA_TTHREAD: { |
|---|
| 298 | lua_State *th = gco2th(o); |
|---|
| 299 | g->gray = th->gclist; |
|---|
| 300 | th->gclist = g->grayagain; |
|---|
| 301 | g->grayagain = o; |
|---|
| 302 | black2gray(o); |
|---|
| 303 | traversestack(g, th); |
|---|
| 304 | return sizeof(lua_State) + sizeof(TValue) * th->stacksize + |
|---|
| 305 | sizeof(CallInfo) * th->size_ci; |
|---|
| 306 | } |
|---|
| 307 | case LUA_TPROTO: { |
|---|
| 308 | Proto *p = gco2p(o); |
|---|
| 309 | g->gray = p->gclist; |
|---|
| 310 | traverseproto(g, p); |
|---|
| 311 | return sizeof(Proto) + sizeof(Instruction) * p->sizecode + |
|---|
| 312 | sizeof(Proto *) * p->sizep + |
|---|
| 313 | sizeof(TValue) * p->sizek + |
|---|
| 314 | sizeof(int) * p->sizelineinfo + |
|---|
| 315 | sizeof(LocVar) * p->sizelocvars + |
|---|
| 316 | sizeof(TString *) * p->sizeupvalues; |
|---|
| 317 | } |
|---|
| 318 | default: lua_assert(0); return 0; |
|---|
| 319 | } |
|---|
| 320 | } |
|---|
| 321 | |
|---|
| 322 | |
|---|
| 323 | static size_t propagateall (global_State *g) { |
|---|
| 324 | size_t m = 0; |
|---|
| 325 | while (g->gray) m += propagatemark(g); |
|---|
| 326 | return m; |
|---|
| 327 | } |
|---|
| 328 | |
|---|
| 329 | |
|---|
| 330 | /* |
|---|
| 331 | ** The next function tells whether a key or value can be cleared from |
|---|
| 332 | ** a weak table. Non-collectable objects are never removed from weak |
|---|
| 333 | ** tables. Strings behave as `values', so are never removed too. for |
|---|
| 334 | ** other objects: if really collected, cannot keep them; for userdata |
|---|
| 335 | ** being finalized, keep them in keys, but not in values |
|---|
| 336 | */ |
|---|
| 337 | static int iscleared (const TValue *o, int iskey) { |
|---|
| 338 | if (!iscollectable(o)) return 0; |
|---|
| 339 | if (ttisstring(o)) { |
|---|
| 340 | stringmark(rawtsvalue(o)); /* strings are `values', so are never weak */ |
|---|
| 341 | return 0; |
|---|
| 342 | } |
|---|
| 343 | return iswhite(gcvalue(o)) || |
|---|
| 344 | (ttisuserdata(o) && (!iskey && isfinalized(uvalue(o)))); |
|---|
| 345 | } |
|---|
| 346 | |
|---|
| 347 | |
|---|
| 348 | /* |
|---|
| 349 | ** clear collected entries from weaktables |
|---|
| 350 | */ |
|---|
| 351 | static void cleartable (GCObject *l) { |
|---|
| 352 | while (l) { |
|---|
| 353 | Table *h = gco2h(l); |
|---|
| 354 | int i = h->sizearray; |
|---|
| 355 | lua_assert(testbit(h->marked, VALUEWEAKBIT) || |
|---|
| 356 | testbit(h->marked, KEYWEAKBIT)); |
|---|
| 357 | if (testbit(h->marked, VALUEWEAKBIT)) { |
|---|
| 358 | while (i--) { |
|---|
| 359 | TValue *o = &h->array[i]; |
|---|
| 360 | if (iscleared(o, 0)) /* value was collected? */ |
|---|
| 361 | setnilvalue(o); /* remove value */ |
|---|
| 362 | } |
|---|
| 363 | } |
|---|
| 364 | i = sizenode(h); |
|---|
| 365 | while (i--) { |
|---|
| 366 | Node *n = gnode(h, i); |
|---|
| 367 | if (!ttisnil(gval(n)) && /* non-empty entry? */ |
|---|
| 368 | (iscleared(key2tval(n), 1) || iscleared(gval(n), 0))) { |
|---|
| 369 | setnilvalue(gval(n)); /* remove value ... */ |
|---|
| 370 | removeentry(n); /* remove entry from table */ |
|---|
| 371 | } |
|---|
| 372 | } |
|---|
| 373 | l = h->gclist; |
|---|
| 374 | } |
|---|
| 375 | } |
|---|
| 376 | |
|---|
| 377 | |
|---|
| 378 | static void freeobj (lua_State *L, GCObject *o) { |
|---|
| 379 | switch (o->gch.tt) { |
|---|
| 380 | case LUA_TPROTO: luaF_freeproto(L, gco2p(o)); break; |
|---|
| 381 | case LUA_TFUNCTION: luaF_freeclosure(L, gco2cl(o)); break; |
|---|
| 382 | case LUA_TUPVAL: luaF_freeupval(L, gco2uv(o)); break; |
|---|
| 383 | case LUA_TTABLE: luaH_free(L, gco2h(o)); break; |
|---|
| 384 | case LUA_TTHREAD: { |
|---|
| 385 | lua_assert(gco2th(o) != L && gco2th(o) != G(L)->mainthread); |
|---|
| 386 | luaE_freethread(L, gco2th(o)); |
|---|
| 387 | break; |
|---|
| 388 | } |
|---|
| 389 | case LUA_TSTRING: { |
|---|
| 390 | G(L)->strt.nuse--; |
|---|
| 391 | luaM_freemem(L, o, sizestring(gco2ts(o))); |
|---|
| 392 | break; |
|---|
| 393 | } |
|---|
| 394 | case LUA_TUSERDATA: { |
|---|
| 395 | luaM_freemem(L, o, sizeudata(gco2u(o))); |
|---|
| 396 | break; |
|---|
| 397 | } |
|---|
| 398 | default: lua_assert(0); |
|---|
| 399 | } |
|---|
| 400 | } |
|---|
| 401 | |
|---|
| 402 | |
|---|
| 403 | |
|---|
| 404 | #define sweepwholelist(L,p) sweeplist(L,p,MAX_LUMEM) |
|---|
| 405 | |
|---|
| 406 | |
|---|
| 407 | static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count) { |
|---|
| 408 | GCObject *curr; |
|---|
| 409 | global_State *g = G(L); |
|---|
| 410 | int deadmask = otherwhite(g); |
|---|
| 411 | while ((curr = *p) != NULL && count-- > 0) { |
|---|
| 412 | if (curr->gch.tt == LUA_TTHREAD) /* sweep open upvalues of each thread */ |
|---|
| 413 | sweepwholelist(L, &gco2th(curr)->openupval); |
|---|
| 414 | if ((curr->gch.marked ^ WHITEBITS) & deadmask) { /* not dead? */ |
|---|
| 415 | lua_assert(!isdead(g, curr) || testbit(curr->gch.marked, FIXEDBIT)); |
|---|
| 416 | makewhite(g, curr); /* make it white (for next cycle) */ |
|---|
| 417 | p = &curr->gch.next; |
|---|
| 418 | } |
|---|
| 419 | else { /* must erase `curr' */ |
|---|
| 420 | lua_assert(isdead(g, curr) || deadmask == bitmask(SFIXEDBIT)); |
|---|
| 421 | *p = curr->gch.next; |
|---|
| 422 | if (curr == g->rootgc) /* is the first element of the list? */ |
|---|
| 423 | g->rootgc = curr->gch.next; /* adjust first */ |
|---|
| 424 | freeobj(L, curr); |
|---|
| 425 | } |
|---|
| 426 | } |
|---|
| 427 | return p; |
|---|
| 428 | } |
|---|
| 429 | |
|---|
| 430 | |
|---|
| 431 | static void checkSizes (lua_State *L) { |
|---|
| 432 | global_State *g = G(L); |
|---|
| 433 | /* check size of string hash */ |
|---|
| 434 | if (g->strt.nuse < cast(lu_int32, g->strt.size/4) && |
|---|
| 435 | g->strt.size > MINSTRTABSIZE*2) |
|---|
| 436 | luaS_resize(L, g->strt.size/2); /* table is too big */ |
|---|
| 437 | /* check size of buffer */ |
|---|
| 438 | if (luaZ_sizebuffer(&g->buff) > LUA_MINBUFFER*2) { /* buffer too big? */ |
|---|
| 439 | size_t newsize = luaZ_sizebuffer(&g->buff) / 2; |
|---|
| 440 | luaZ_resizebuffer(L, &g->buff, newsize); |
|---|
| 441 | } |
|---|
| 442 | } |
|---|
| 443 | |
|---|
| 444 | |
|---|
| 445 | static void GCTM (lua_State *L) { |
|---|
| 446 | global_State *g = G(L); |
|---|
| 447 | GCObject *o = g->tmudata->gch.next; /* get first element */ |
|---|
| 448 | Udata *udata = rawgco2u(o); |
|---|
| 449 | const TValue *tm; |
|---|
| 450 | /* remove udata from `tmudata' */ |
|---|
| 451 | if (o == g->tmudata) /* last element? */ |
|---|
| 452 | g->tmudata = NULL; |
|---|
| 453 | else |
|---|
| 454 | g->tmudata->gch.next = udata->uv.next; |
|---|
| 455 | udata->uv.next = g->mainthread->next; /* return it to `root' list */ |
|---|
| 456 | g->mainthread->next = o; |
|---|
| 457 | makewhite(g, o); |
|---|
| 458 | tm = fasttm(L, udata->uv.metatable, TM_GC); |
|---|
| 459 | if (tm != NULL) { |
|---|
| 460 | lu_byte oldah = L->allowhook; |
|---|
| 461 | lu_mem oldt = g->GCthreshold; |
|---|
| 462 | L->allowhook = 0; /* stop debug hooks during GC tag method */ |
|---|
| 463 | g->GCthreshold = 2*g->totalbytes; /* avoid GC steps */ |
|---|
| 464 | setobj2s(L, L->top, tm); |
|---|
| 465 | setuvalue(L, L->top+1, udata); |
|---|
| 466 | L->top += 2; |
|---|
| 467 | luaD_call(L, L->top - 2, 0); |
|---|
| 468 | L->allowhook = oldah; /* restore hooks */ |
|---|
| 469 | g->GCthreshold = oldt; /* restore threshold */ |
|---|
| 470 | } |
|---|
| 471 | } |
|---|
| 472 | |
|---|
| 473 | |
|---|
| 474 | /* |
|---|
| 475 | ** Call all GC tag methods |
|---|
| 476 | */ |
|---|
| 477 | void luaC_callGCTM (lua_State *L) { |
|---|
| 478 | while (G(L)->tmudata) |
|---|
| 479 | GCTM(L); |
|---|
| 480 | } |
|---|
| 481 | |
|---|
| 482 | |
|---|
| 483 | void luaC_freeall (lua_State *L) { |
|---|
| 484 | global_State *g = G(L); |
|---|
| 485 | int i; |
|---|
| 486 | g->currentwhite = WHITEBITS | bitmask(SFIXEDBIT); /* mask to collect all elements */ |
|---|
| 487 | sweepwholelist(L, &g->rootgc); |
|---|
| 488 | for (i = 0; i < g->strt.size; i++) /* free all string lists */ |
|---|
| 489 | sweepwholelist(L, &g->strt.hash[i]); |
|---|
| 490 | } |
|---|
| 491 | |
|---|
| 492 | |
|---|
| 493 | static void markmt (global_State *g) { |
|---|
| 494 | int i; |
|---|
| 495 | for (i=0; i<NUM_TAGS; i++) |
|---|
| 496 | if (g->mt[i]) markobject(g, g->mt[i]); |
|---|
| 497 | } |
|---|
| 498 | |
|---|
| 499 | |
|---|
| 500 | /* mark root set */ |
|---|
| 501 | static void markroot (lua_State *L) { |
|---|
| 502 | global_State *g = G(L); |
|---|
| 503 | g->gray = NULL; |
|---|
| 504 | g->grayagain = NULL; |
|---|
| 505 | g->weak = NULL; |
|---|
| 506 | markobject(g, g->mainthread); |
|---|
| 507 | /* make global table be traversed before main stack */ |
|---|
| 508 | markvalue(g, gt(g->mainthread)); |
|---|
| 509 | markvalue(g, registry(L)); |
|---|
| 510 | markmt(g); |
|---|
| 511 | g->gcstate = GCSpropagate; |
|---|
| 512 | } |
|---|
| 513 | |
|---|
| 514 | |
|---|
| 515 | static void remarkupvals (global_State *g) { |
|---|
| 516 | UpVal *uv; |
|---|
| 517 | for (uv = g->uvhead.u.l.next; uv != &g->uvhead; uv = uv->u.l.next) { |
|---|
| 518 | lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv); |
|---|
| 519 | if (isgray(obj2gco(uv))) |
|---|
| 520 | markvalue(g, uv->v); |
|---|
| 521 | } |
|---|
| 522 | } |
|---|
| 523 | |
|---|
| 524 | |
|---|
| 525 | static void atomic (lua_State *L) { |
|---|
| 526 | global_State *g = G(L); |
|---|
| 527 | size_t udsize; /* total size of userdata to be finalized */ |
|---|
| 528 | /* remark occasional upvalues of (maybe) dead threads */ |
|---|
| 529 | remarkupvals(g); |
|---|
| 530 | /* traverse objects cautch by write barrier and by 'remarkupvals' */ |
|---|
| 531 | propagateall(g); |
|---|
| 532 | /* remark weak tables */ |
|---|
| 533 | g->gray = g->weak; |
|---|
| 534 | g->weak = NULL; |
|---|
| 535 | lua_assert(!iswhite(obj2gco(g->mainthread))); |
|---|
| 536 | markobject(g, L); /* mark running thread */ |
|---|
| 537 | markmt(g); /* mark basic metatables (again) */ |
|---|
| 538 | propagateall(g); |
|---|
| 539 | /* remark gray again */ |
|---|
| 540 | g->gray = g->grayagain; |
|---|
| 541 | g->grayagain = NULL; |
|---|
| 542 | propagateall(g); |
|---|
| 543 | udsize = luaC_separateudata(L, 0); /* separate userdata to be finalized */ |
|---|
| 544 | marktmu(g); /* mark `preserved' userdata */ |
|---|
| 545 | udsize += propagateall(g); /* remark, to propagate `preserveness' */ |
|---|
| 546 | cleartable(g->weak); /* remove collected objects from weak tables */ |
|---|
| 547 | /* flip current white */ |
|---|
| 548 | g->currentwhite = cast_byte(otherwhite(g)); |
|---|
| 549 | g->sweepstrgc = 0; |
|---|
| 550 | g->sweepgc = &g->rootgc; |
|---|
| 551 | g->gcstate = GCSsweepstring; |
|---|
| 552 | g->estimate = g->totalbytes - udsize; /* first estimate */ |
|---|
| 553 | } |
|---|
| 554 | |
|---|
| 555 | |
|---|
| 556 | static l_mem singlestep (lua_State *L) { |
|---|
| 557 | global_State *g = G(L); |
|---|
| 558 | /*lua_checkmemory(L);*/ |
|---|
| 559 | switch (g->gcstate) { |
|---|
| 560 | case GCSpause: { |
|---|
| 561 | markroot(L); /* start a new collection */ |
|---|
| 562 | return 0; |
|---|
| 563 | } |
|---|
| 564 | case GCSpropagate: { |
|---|
| 565 | if (g->gray) |
|---|
| 566 | return propagatemark(g); |
|---|
| 567 | else { /* no more `gray' objects */ |
|---|
| 568 | atomic(L); /* finish mark phase */ |
|---|
| 569 | return 0; |
|---|
| 570 | } |
|---|
| 571 | } |
|---|
| 572 | case GCSsweepstring: { |
|---|
| 573 | lu_mem old = g->totalbytes; |
|---|
| 574 | sweepwholelist(L, &g->strt.hash[g->sweepstrgc++]); |
|---|
| 575 | if (g->sweepstrgc >= g->strt.size) /* nothing more to sweep? */ |
|---|
| 576 | g->gcstate = GCSsweep; /* end sweep-string phase */ |
|---|
| 577 | lua_assert(old >= g->totalbytes); |
|---|
| 578 | g->estimate -= old - g->totalbytes; |
|---|
| 579 | return GCSWEEPCOST; |
|---|
| 580 | } |
|---|
| 581 | case GCSsweep: { |
|---|
| 582 | lu_mem old = g->totalbytes; |
|---|
| 583 | g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX); |
|---|
| 584 | if (*g->sweepgc == NULL) { /* nothing more to sweep? */ |
|---|
| 585 | checkSizes(L); |
|---|
| 586 | g->gcstate = GCSfinalize; /* end sweep phase */ |
|---|
| 587 | } |
|---|
| 588 | lua_assert(old >= g->totalbytes); |
|---|
| 589 | g->estimate -= old - g->totalbytes; |
|---|
| 590 | return GCSWEEPMAX*GCSWEEPCOST; |
|---|
| 591 | } |
|---|
| 592 | case GCSfinalize: { |
|---|
| 593 | if (g->tmudata) { |
|---|
| 594 | GCTM(L); |
|---|
| 595 | if (g->estimate > GCFINALIZECOST) |
|---|
| 596 | g->estimate -= GCFINALIZECOST; |
|---|
| 597 | return GCFINALIZECOST; |
|---|
| 598 | } |
|---|
| 599 | else { |
|---|
| 600 | g->gcstate = GCSpause; /* end collection */ |
|---|
| 601 | g->gcdept = 0; |
|---|
| 602 | return 0; |
|---|
| 603 | } |
|---|
| 604 | } |
|---|
| 605 | default: lua_assert(0); return 0; |
|---|
| 606 | } |
|---|
| 607 | } |
|---|
| 608 | |
|---|
| 609 | |
|---|
| 610 | void luaC_step (lua_State *L) { |
|---|
| 611 | global_State *g = G(L); |
|---|
| 612 | l_mem lim = (GCSTEPSIZE/100) * g->gcstepmul; |
|---|
| 613 | if (lim == 0) |
|---|
| 614 | lim = (MAX_LUMEM-1)/2; /* no limit */ |
|---|
| 615 | g->gcdept += g->totalbytes - g->GCthreshold; |
|---|
| 616 | do { |
|---|
| 617 | lim -= singlestep(L); |
|---|
| 618 | if (g->gcstate == GCSpause) |
|---|
| 619 | break; |
|---|
| 620 | } while (lim > 0); |
|---|
| 621 | if (g->gcstate != GCSpause) { |
|---|
| 622 | if (g->gcdept < GCSTEPSIZE) |
|---|
| 623 | g->GCthreshold = g->totalbytes + GCSTEPSIZE; /* - lim/g->gcstepmul;*/ |
|---|
| 624 | else { |
|---|
| 625 | g->gcdept -= GCSTEPSIZE; |
|---|
| 626 | g->GCthreshold = g->totalbytes; |
|---|
| 627 | } |
|---|
| 628 | } |
|---|
| 629 | else { |
|---|
| 630 | lua_assert(g->totalbytes >= g->estimate); |
|---|
| 631 | setthreshold(g); |
|---|
| 632 | } |
|---|
| 633 | } |
|---|
| 634 | |
|---|
| 635 | |
|---|
| 636 | void luaC_fullgc (lua_State *L) { |
|---|
| 637 | global_State *g = G(L); |
|---|
| 638 | if (g->gcstate <= GCSpropagate) { |
|---|
| 639 | /* reset sweep marks to sweep all elements (returning them to white) */ |
|---|
| 640 | g->sweepstrgc = 0; |
|---|
| 641 | g->sweepgc = &g->rootgc; |
|---|
| 642 | /* reset other collector lists */ |
|---|
| 643 | g->gray = NULL; |
|---|
| 644 | g->grayagain = NULL; |
|---|
| 645 | g->weak = NULL; |
|---|
| 646 | g->gcstate = GCSsweepstring; |
|---|
| 647 | } |
|---|
| 648 | lua_assert(g->gcstate != GCSpause && g->gcstate != GCSpropagate); |
|---|
| 649 | /* finish any pending sweep phase */ |
|---|
| 650 | while (g->gcstate != GCSfinalize) { |
|---|
| 651 | lua_assert(g->gcstate == GCSsweepstring || g->gcstate == GCSsweep); |
|---|
| 652 | singlestep(L); |
|---|
| 653 | } |
|---|
| 654 | markroot(L); |
|---|
| 655 | while (g->gcstate != GCSpause) { |
|---|
| 656 | singlestep(L); |
|---|
| 657 | } |
|---|
| 658 | setthreshold(g); |
|---|
| 659 | } |
|---|
| 660 | |
|---|
| 661 | |
|---|
| 662 | void luaC_barrierf (lua_State *L, GCObject *o, GCObject *v) { |
|---|
| 663 | global_State *g = G(L); |
|---|
| 664 | lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o)); |
|---|
| 665 | lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause); |
|---|
| 666 | lua_assert(ttype(&o->gch) != LUA_TTABLE); |
|---|
| 667 | /* must keep invariant? */ |
|---|
| 668 | if (g->gcstate == GCSpropagate) |
|---|
| 669 | reallymarkobject(g, v); /* restore invariant */ |
|---|
| 670 | else /* don't mind */ |
|---|
| 671 | makewhite(g, o); /* mark as white just to avoid other barriers */ |
|---|
| 672 | } |
|---|
| 673 | |
|---|
| 674 | |
|---|
| 675 | void luaC_barrierback (lua_State *L, Table *t) { |
|---|
| 676 | global_State *g = G(L); |
|---|
| 677 | GCObject *o = obj2gco(t); |
|---|
| 678 | lua_assert(isblack(o) && !isdead(g, o)); |
|---|
| 679 | lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause); |
|---|
| 680 | black2gray(o); /* make table gray (again) */ |
|---|
| 681 | t->gclist = g->grayagain; |
|---|
| 682 | g->grayagain = o; |
|---|
| 683 | } |
|---|
| 684 | |
|---|
| 685 | |
|---|
| 686 | void luaC_link (lua_State *L, GCObject *o, lu_byte tt) { |
|---|
| 687 | global_State *g = G(L); |
|---|
| 688 | o->gch.next = g->rootgc; |
|---|
| 689 | g->rootgc = o; |
|---|
| 690 | o->gch.marked = luaC_white(g); |
|---|
| 691 | o->gch.tt = tt; |
|---|
| 692 | } |
|---|
| 693 | |
|---|
| 694 | |
|---|
| 695 | void luaC_linkupval (lua_State *L, UpVal *uv) { |
|---|
| 696 | global_State *g = G(L); |
|---|
| 697 | GCObject *o = obj2gco(uv); |
|---|
| 698 | o->gch.next = g->rootgc; /* link upvalue into `rootgc' list */ |
|---|
| 699 | g->rootgc = o; |
|---|
| 700 | if (isgray(o)) { |
|---|
| 701 | if (g->gcstate == GCSpropagate) { |
|---|
| 702 | gray2black(o); /* closed upvalues need barrier */ |
|---|
| 703 | luaC_barrier(L, uv, uv->v); |
|---|
| 704 | } |
|---|
| 705 | else { /* sweep phase: sweep it (turning it into white) */ |
|---|
| 706 | makewhite(g, o); |
|---|
| 707 | lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause); |
|---|
| 708 | } |
|---|
| 709 | } |
|---|
| 710 | } |
|---|
| 711 | |
|---|