diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2008-02-19 15:55:09 -0300 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2008-02-19 15:55:09 -0300 |
commit | e2b366c7601aeecda04f3b7ac5a2bd6eb80521bb (patch) | |
tree | 580c94105007087a5029fcb02a0e3cb3be306b54 /lgc.c | |
parent | fa19baab7ffd96cecb1defe4ad81084c612d2704 (diff) | |
download | lua-e2b366c7601aeecda04f3b7ac5a2bd6eb80521bb.tar.gz lua-e2b366c7601aeecda04f3b7ac5a2bd6eb80521bb.tar.bz2 lua-e2b366c7601aeecda04f3b7ac5a2bd6eb80521bb.zip |
userdata with finalizers are kept in a separated list
Diffstat (limited to 'lgc.c')
-rw-r--r-- | lgc.c | 237 |
1 files changed, 135 insertions, 102 deletions
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | ** $Id: lgc.c,v 2.42 2007/10/31 15:41:19 roberto Exp roberto $ | 2 | ** $Id: lgc.c,v 2.43 2008/02/11 15:46:03 roberto Exp roberto $ |
3 | ** Garbage Collector | 3 | ** Garbage Collector |
4 | ** See Copyright Notice in lua.h | 4 | ** See Copyright Notice in lua.h |
5 | */ | 5 | */ |
@@ -29,24 +29,22 @@ | |||
29 | #define GCFINALIZECOST 100 | 29 | #define GCFINALIZECOST 100 |
30 | 30 | ||
31 | 31 | ||
32 | #define maskmarks cast_byte(~(bitmask(BLACKBIT)|WHITEBITS)) | 32 | #define maskcolors cast_byte(~(bitmask(BLACKBIT)|WHITEBITS)) |
33 | 33 | ||
34 | #define makewhite(g,x) \ | 34 | #define makewhite(g,x) \ |
35 | ((x)->gch.marked = cast_byte(((x)->gch.marked & maskmarks) | luaC_white(g))) | 35 | (gch(x)->marked = cast_byte((gch(x)->marked & maskcolors) | luaC_white(g))) |
36 | 36 | ||
37 | #define white2gray(x) reset2bits((x)->gch.marked, WHITE0BIT, WHITE1BIT) | 37 | #define white2gray(x) resetbits(gch(x)->marked, WHITEBITS) |
38 | #define black2gray(x) resetbit((x)->gch.marked, BLACKBIT) | 38 | #define black2gray(x) resetbit(gch(x)->marked, BLACKBIT) |
39 | 39 | ||
40 | #define stringmark(s) reset2bits((s)->tsv.marked, WHITE0BIT, WHITE1BIT) | 40 | #define stringmark(s) resetbits((s)->tsv.marked, WHITEBITS) |
41 | 41 | ||
42 | 42 | ||
43 | #define isfinalized(u) testbit((u)->marked, FINALIZEDBIT) | 43 | #define isfinalized(u) testbit((u)->marked, FINALIZEDBIT) |
44 | #define markfinalized(u) l_setbit((u)->marked, FINALIZEDBIT) | ||
45 | |||
46 | 44 | ||
47 | 45 | ||
48 | #define markvalue(g,o) { checkconsistency(o); \ | 46 | #define markvalue(g,o) { checkconsistency(o); \ |
49 | if (iscollectable(o) && iswhite(gcvalue(o))) reallymarkobject(g,gcvalue(o)); } | 47 | if (valiswhite(o)) reallymarkobject(g,gcvalue(o)); } |
50 | 48 | ||
51 | #define markobject(g,t) { if ((t) && iswhite(obj2gco(t))) \ | 49 | #define markobject(g,t) { if ((t) && iswhite(obj2gco(t))) \ |
52 | reallymarkobject(g, obj2gco(t)); } | 50 | reallymarkobject(g, obj2gco(t)); } |
@@ -89,7 +87,7 @@ static int iscleared (const TValue *o, int iskey) { | |||
89 | static void reallymarkobject (global_State *g, GCObject *o) { | 87 | static void reallymarkobject (global_State *g, GCObject *o) { |
90 | lua_assert(iswhite(o) && !isdead(g, o)); | 88 | lua_assert(iswhite(o) && !isdead(g, o)); |
91 | white2gray(o); | 89 | white2gray(o); |
92 | switch (o->gch.tt) { | 90 | switch (gch(o)->tt) { |
93 | case LUA_TSTRING: { | 91 | case LUA_TSTRING: { |
94 | return; | 92 | return; |
95 | } | 93 | } |
@@ -113,7 +111,7 @@ static void reallymarkobject (global_State *g, GCObject *o) { | |||
113 | break; | 111 | break; |
114 | } | 112 | } |
115 | case LUA_TTABLE: { | 113 | case LUA_TTABLE: { |
116 | linktable(gco2h(o), &g->gray); | 114 | linktable(gco2t(o), &g->gray); |
117 | break; | 115 | break; |
118 | } | 116 | } |
119 | case LUA_TTHREAD: { | 117 | case LUA_TTHREAD: { |
@@ -131,42 +129,30 @@ static void reallymarkobject (global_State *g, GCObject *o) { | |||
131 | } | 129 | } |
132 | 130 | ||
133 | 131 | ||
134 | static void marktmu (global_State *g) { | 132 | /* move 'dead' udata that need finalization to list 'tobefnz' */ |
135 | GCObject *u = g->tmudata; | ||
136 | if (u) { | ||
137 | do { | ||
138 | u = u->gch.next; | ||
139 | makewhite(g, u); /* may be marked, if left from previous GC */ | ||
140 | reallymarkobject(g, u); | ||
141 | } while (u != g->tmudata); | ||
142 | } | ||
143 | } | ||
144 | |||
145 | |||
146 | /* move `dead' udata that need finalization to list `tmudata' */ | ||
147 | size_t luaC_separateudata (lua_State *L, int all) { | 133 | size_t luaC_separateudata (lua_State *L, int all) { |
148 | global_State *g = G(L); | 134 | global_State *g = G(L); |
149 | size_t deadmem = 0; | 135 | size_t deadmem = 0; |
150 | GCObject **p = &g->mainthread->next; | 136 | GCObject **p = &g->tmudata; |
151 | GCObject *curr; | 137 | GCObject *curr; |
152 | while ((curr = *p) != NULL) { | 138 | while ((curr = *p) != NULL) { |
153 | if (!(iswhite(curr) || all) || isfinalized(gco2u(curr))) | 139 | lua_assert(ttisuserdata(gch(curr)) && !isfinalized(gco2u(curr))); |
154 | p = &curr->gch.next; /* don't bother with them */ | 140 | lua_assert(testbit(gch(curr)->marked, SEPARATED)); |
155 | else if (fasttm(L, gco2u(curr)->metatable, TM_GC) == NULL) { | 141 | if (all) makewhite(g, curr); /* if 'all', collect all objects */ |
156 | markfinalized(gco2u(curr)); /* don't need finalization */ | 142 | if (!iswhite(curr)) /* not being collected? */ |
157 | p = &curr->gch.next; | 143 | p = &gch(curr)->next; /* don't bother with it */ |
158 | } | 144 | else { |
159 | else { /* must call its gc method */ | 145 | l_setbit(gch(curr)->marked, FINALIZEDBIT); /* won't be finalized again */ |
146 | reallymarkobject(g, curr); /* won't be collected now */ | ||
160 | deadmem += sizeudata(gco2u(curr)); | 147 | deadmem += sizeudata(gco2u(curr)); |
161 | markfinalized(gco2u(curr)); | 148 | *p = gch(curr)->next; /* remove 'curr' from 'tmudata' list */ |
162 | *p = curr->gch.next; | 149 | /* link 'curr' at the end of 'tobefnz' list */ |
163 | /* link `curr' at the end of `tmudata' list */ | 150 | if (g->tobefnz == NULL) /* list is empty? */ |
164 | if (g->tmudata == NULL) /* list is empty? */ | 151 | g->tobefnz = gch(curr)->next = curr; /* creates a circular list */ |
165 | g->tmudata = curr->gch.next = curr; /* creates a circular list */ | ||
166 | else { | 152 | else { |
167 | curr->gch.next = g->tmudata->gch.next; | 153 | gch(curr)->next = gch(g->tobefnz)->next; |
168 | g->tmudata->gch.next = curr; | 154 | gch(g->tobefnz)->next = curr; |
169 | g->tmudata = curr; | 155 | g->tobefnz = curr; |
170 | } | 156 | } |
171 | } | 157 | } |
172 | } | 158 | } |
@@ -195,7 +181,7 @@ static int traverseephemeron (global_State *g, Table *h) { | |||
195 | int hasclears = 0; | 181 | int hasclears = 0; |
196 | int i = h->sizearray; | 182 | int i = h->sizearray; |
197 | while (i--) { /* mark array part (numeric keys are 'strong') */ | 183 | while (i--) { /* mark array part (numeric keys are 'strong') */ |
198 | if (iscollectable(&h->array[i]) && iswhite(gcvalue(&h->array[i]))) { | 184 | if (valiswhite(&h->array[i])) { |
199 | marked = 1; | 185 | marked = 1; |
200 | reallymarkobject(g, gcvalue(&h->array[i])); | 186 | reallymarkobject(g, gcvalue(&h->array[i])); |
201 | } | 187 | } |
@@ -206,7 +192,7 @@ static int traverseephemeron (global_State *g, Table *h) { | |||
206 | lua_assert(ttype(gkey(n)) != LUA_TDEADKEY || ttisnil(gval(n))); | 192 | lua_assert(ttype(gkey(n)) != LUA_TDEADKEY || ttisnil(gval(n))); |
207 | if (ttisnil(gval(n))) /* entry is empty? */ | 193 | if (ttisnil(gval(n))) /* entry is empty? */ |
208 | removeentry(n); /* remove it */ | 194 | removeentry(n); /* remove it */ |
209 | else if (iscollectable(gval(n)) && iswhite(gcvalue(gval(n)))) { | 195 | else if (valiswhite(gval(n))) { |
210 | /* value is not marked yet */ | 196 | /* value is not marked yet */ |
211 | if (iscleared(key2tval(n), 1)) /* key is not marked (yet)? */ | 197 | if (iscleared(key2tval(n), 1)) /* key is not marked (yet)? */ |
212 | hasclears = 1; /* may have to propagate mark from key to value */ | 198 | hasclears = 1; /* may have to propagate mark from key to value */ |
@@ -256,10 +242,8 @@ static void traversetable (global_State *g, Table *h) { | |||
256 | traverseweakvalue(g, h); | 242 | traverseweakvalue(g, h); |
257 | else if (!weakvalue) /* strong values? */ | 243 | else if (!weakvalue) /* strong values? */ |
258 | traverseephemeron(g, h); | 244 | traverseephemeron(g, h); |
259 | else { | 245 | else |
260 | lua_assert(weakkey && weakvalue); /* nothing to traverse now */ | 246 | linktable(h, &g->allweak); /* nothing to traverse now */ |
261 | linktable(h, &g->allweak); | ||
262 | } | ||
263 | return; | 247 | return; |
264 | } /* else go through */ | 248 | } /* else go through */ |
265 | } | 249 | } |
@@ -350,9 +334,9 @@ static l_mem propagatemark (global_State *g) { | |||
350 | GCObject *o = g->gray; | 334 | GCObject *o = g->gray; |
351 | lua_assert(isgray(o)); | 335 | lua_assert(isgray(o)); |
352 | gray2black(o); | 336 | gray2black(o); |
353 | switch (o->gch.tt) { | 337 | switch (gch(o)->tt) { |
354 | case LUA_TTABLE: { | 338 | case LUA_TTABLE: { |
355 | Table *h = gco2h(o); | 339 | Table *h = gco2t(o); |
356 | g->gray = h->gclist; | 340 | g->gray = h->gclist; |
357 | traversetable(g, h); | 341 | traversetable(g, h); |
358 | return sizeof(Table) + sizeof(TValue) * h->sizearray + | 342 | return sizeof(Table) + sizeof(TValue) * h->sizearray + |
@@ -406,8 +390,8 @@ static void convergeephemerons (global_State *g) { | |||
406 | g->ephemeron = NULL; | 390 | g->ephemeron = NULL; |
407 | changed = 0; | 391 | changed = 0; |
408 | while ((w = next) != NULL) { | 392 | while ((w = next) != NULL) { |
409 | next = gco2h(w)->gclist; | 393 | next = gco2t(w)->gclist; |
410 | if (traverseephemeron(g, gco2h(w))) { | 394 | if (traverseephemeron(g, gco2t(w))) { |
411 | changed = 1; | 395 | changed = 1; |
412 | propagateall(g); | 396 | propagateall(g); |
413 | } | 397 | } |
@@ -422,7 +406,7 @@ static void convergeephemerons (global_State *g) { | |||
422 | */ | 406 | */ |
423 | static void cleartable (GCObject *l) { | 407 | static void cleartable (GCObject *l) { |
424 | while (l) { | 408 | while (l) { |
425 | Table *h = gco2h(l); | 409 | Table *h = gco2t(l); |
426 | int i = h->sizearray; | 410 | int i = h->sizearray; |
427 | while (i--) { | 411 | while (i--) { |
428 | TValue *o = &h->array[i]; | 412 | TValue *o = &h->array[i]; |
@@ -444,25 +428,18 @@ static void cleartable (GCObject *l) { | |||
444 | 428 | ||
445 | 429 | ||
446 | static void freeobj (lua_State *L, GCObject *o) { | 430 | static void freeobj (lua_State *L, GCObject *o) { |
447 | switch (o->gch.tt) { | 431 | switch (gch(o)->tt) { |
448 | case LUA_TPROTO: luaF_freeproto(L, gco2p(o)); break; | 432 | case LUA_TPROTO: luaF_freeproto(L, gco2p(o)); break; |
449 | case LUA_TFUNCTION: luaF_freeclosure(L, gco2cl(o)); break; | 433 | case LUA_TFUNCTION: luaF_freeclosure(L, gco2cl(o)); break; |
450 | case LUA_TUPVAL: luaF_freeupval(L, gco2uv(o)); break; | 434 | case LUA_TUPVAL: luaF_freeupval(L, gco2uv(o)); break; |
451 | case LUA_TTABLE: luaH_free(L, gco2h(o)); break; | 435 | case LUA_TTABLE: luaH_free(L, gco2t(o)); break; |
452 | case LUA_TTHREAD: { | 436 | case LUA_TTHREAD: luaE_freethread(L, gco2th(o)); break; |
453 | lua_assert(gco2th(o) != L && gco2th(o) != G(L)->mainthread); | 437 | case LUA_TUSERDATA: luaM_freemem(L, o, sizeudata(gco2u(o))); break; |
454 | luaE_freethread(L, gco2th(o)); | ||
455 | break; | ||
456 | } | ||
457 | case LUA_TSTRING: { | 438 | case LUA_TSTRING: { |
458 | G(L)->strt.nuse--; | 439 | G(L)->strt.nuse--; |
459 | luaM_freemem(L, o, sizestring(gco2ts(o))); | 440 | luaM_freemem(L, o, sizestring(gco2ts(o))); |
460 | break; | 441 | break; |
461 | } | 442 | } |
462 | case LUA_TUSERDATA: { | ||
463 | luaM_freemem(L, o, sizeudata(gco2u(o))); | ||
464 | break; | ||
465 | } | ||
466 | default: lua_assert(0); | 443 | default: lua_assert(0); |
467 | } | 444 | } |
468 | } | 445 | } |
@@ -477,18 +454,16 @@ static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count) { | |||
477 | global_State *g = G(L); | 454 | global_State *g = G(L); |
478 | int deadmask = otherwhite(g); | 455 | int deadmask = otherwhite(g); |
479 | while ((curr = *p) != NULL && count-- > 0) { | 456 | while ((curr = *p) != NULL && count-- > 0) { |
480 | if (curr->gch.tt == LUA_TTHREAD) /* sweep open upvalues of each thread */ | 457 | if (ttisthread(gch(curr))) /* sweep open upvalues of each thread */ |
481 | sweepwholelist(L, &gco2th(curr)->openupval); | 458 | sweepwholelist(L, &gco2th(curr)->openupval); |
482 | if ((curr->gch.marked ^ WHITEBITS) & deadmask) { /* not dead? */ | 459 | if ((gch(curr)->marked ^ WHITEBITS) & deadmask) { /* not dead? */ |
483 | lua_assert(!isdead(g, curr) || testbit(curr->gch.marked, FIXEDBIT)); | 460 | lua_assert(!isdead(g, curr) || testbit(gch(curr)->marked, FIXEDBIT)); |
484 | makewhite(g, curr); /* make it white (for next cycle) */ | 461 | makewhite(g, curr); /* make it white (for next cycle) */ |
485 | p = &curr->gch.next; | 462 | p = &gch(curr)->next; |
486 | } | 463 | } |
487 | else { /* must erase `curr' */ | 464 | else { /* must erase `curr' */ |
488 | lua_assert(isdead(g, curr) || deadmask == bitmask(SFIXEDBIT)); | 465 | lua_assert(isdead(g, curr) || deadmask == bitmask(SFIXEDBIT)); |
489 | *p = curr->gch.next; | 466 | *p = gch(curr)->next; /* remove 'curr' from list */ |
490 | if (curr == g->rootgc) /* is the first element of the list? */ | ||
491 | g->rootgc = curr->gch.next; /* adjust first */ | ||
492 | freeobj(L, curr); | 467 | freeobj(L, curr); |
493 | } | 468 | } |
494 | } | 469 | } |
@@ -496,6 +471,15 @@ static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count) { | |||
496 | } | 471 | } |
497 | 472 | ||
498 | 473 | ||
474 | static GCObject **unmarklist (global_State *g, GCObject **p, lu_mem count) { | ||
475 | for (; *p != NULL && count-- > 0; p = &gch(*p)->next) { | ||
476 | lua_assert(ttisuserdata(gch(*p)) && !isdead(g, *p)); | ||
477 | makewhite(g, *p); | ||
478 | } | ||
479 | return p; | ||
480 | } | ||
481 | |||
482 | |||
499 | static void checkSizes (lua_State *L) { | 483 | static void checkSizes (lua_State *L) { |
500 | global_State *g = G(L); | 484 | global_State *g = G(L); |
501 | if (g->strt.nuse < cast(lu_int32, g->strt.size)) | 485 | if (g->strt.nuse < cast(lu_int32, g->strt.size)) |
@@ -505,15 +489,16 @@ static void checkSizes (lua_State *L) { | |||
505 | 489 | ||
506 | 490 | ||
507 | static Udata *udata2finalize (global_State *g) { | 491 | static Udata *udata2finalize (global_State *g) { |
508 | GCObject *o = g->tmudata->gch.next; /* get first element */ | 492 | GCObject *o = gch(g->tobefnz)->next; /* get first element */ |
509 | Udata *udata = rawgco2u(o); | 493 | Udata *udata = rawgco2u(o); |
510 | /* remove udata from `tmudata' */ | 494 | /* remove udata from `tobefnz' */ |
511 | if (o == g->tmudata) /* last element? */ | 495 | if (o == g->tobefnz) /* last element? */ |
512 | g->tmudata = NULL; | 496 | g->tobefnz = NULL; |
513 | else | 497 | else |
514 | g->tmudata->gch.next = udata->uv.next; | 498 | gch(g->tobefnz)->next = udata->uv.next; |
515 | udata->uv.next = g->mainthread->next; /* return it to `root' list */ | 499 | udata->uv.next = g->mainthread->next; /* return it to `root' list */ |
516 | g->mainthread->next = o; | 500 | g->mainthread->next = o; |
501 | resetbit(udata->uv.marked, SEPARATED); /* mark it as such */ | ||
517 | makewhite(g, o); | 502 | makewhite(g, o); |
518 | return udata; | 503 | return udata; |
519 | } | 504 | } |
@@ -522,8 +507,8 @@ static Udata *udata2finalize (global_State *g) { | |||
522 | static void GCTM (lua_State *L) { | 507 | static void GCTM (lua_State *L) { |
523 | global_State *g = G(L); | 508 | global_State *g = G(L); |
524 | Udata *udata = udata2finalize(g); | 509 | Udata *udata = udata2finalize(g); |
525 | const TValue *tm = fasttm(L, udata->uv.metatable, TM_GC); | 510 | const TValue *tm = gfasttm(g, udata->uv.metatable, TM_GC); |
526 | if (tm != NULL) { | 511 | if (tm != NULL && ttisfunction(tm)) { |
527 | lu_byte oldah = L->allowhook; | 512 | lu_byte oldah = L->allowhook; |
528 | lu_mem oldt = g->GCthreshold; | 513 | lu_mem oldt = g->GCthreshold; |
529 | L->allowhook = 0; /* stop debug hooks during GC tag method */ | 514 | L->allowhook = 0; /* stop debug hooks during GC tag method */ |
@@ -543,15 +528,15 @@ static void GCTM (lua_State *L) { | |||
543 | */ | 528 | */ |
544 | void luaC_callGCTM (lua_State *L) { | 529 | void luaC_callGCTM (lua_State *L) { |
545 | global_State *g = G(L); | 530 | global_State *g = G(L); |
546 | GCObject *last = g->tmudata; | 531 | GCObject *last = g->tobefnz; |
547 | GCObject *curr; | 532 | GCObject *curr; |
548 | if (last == NULL) return; /* empty list? */ | 533 | if (last == NULL) return; /* empty list? */ |
549 | do { | 534 | do { |
550 | curr = g->tmudata->gch.next; /* element to be collected */ | 535 | curr = gch(g->tobefnz)->next; /* element to be collected */ |
551 | GCTM(L); | 536 | GCTM(L); |
552 | } while (curr != last); /* go only until original last */ | 537 | } while (curr != last); /* go only until original last */ |
553 | /* do not finalize new udata created during previous finalizations */ | 538 | /* do not finalize new udata created during previous finalizations */ |
554 | while (g->tmudata) | 539 | while (g->tobefnz) |
555 | udata2finalize(g); /* simply remove them from list */ | 540 | udata2finalize(g); /* simply remove them from list */ |
556 | } | 541 | } |
557 | 542 | ||
@@ -559,10 +544,16 @@ void luaC_callGCTM (lua_State *L) { | |||
559 | void luaC_freeall (lua_State *L) { | 544 | void luaC_freeall (lua_State *L) { |
560 | global_State *g = G(L); | 545 | global_State *g = G(L); |
561 | int i; | 546 | int i; |
562 | g->currentwhite = WHITEBITS | bitmask(SFIXEDBIT); /* mask to collect all elements */ | 547 | lua_assert(g->tobefnz == NULL); |
548 | /* mask to collect all elements */ | ||
549 | g->currentwhite = WHITEBITS | bitmask(SFIXEDBIT); | ||
563 | sweepwholelist(L, &g->rootgc); | 550 | sweepwholelist(L, &g->rootgc); |
551 | lua_assert(g->rootgc == obj2gco(L)); | ||
552 | sweepwholelist(L, &g->tmudata); | ||
553 | lua_assert(g->tmudata == NULL); | ||
564 | for (i = 0; i < g->strt.size; i++) /* free all string lists */ | 554 | for (i = 0; i < g->strt.size; i++) /* free all string lists */ |
565 | sweepwholelist(L, &g->strt.hash[i]); | 555 | sweepwholelist(L, &g->strt.hash[i]); |
556 | lua_assert(g->strt.nuse == 0); | ||
566 | } | 557 | } |
567 | 558 | ||
568 | 559 | ||
@@ -573,6 +564,20 @@ static void markmt (global_State *g) { | |||
573 | } | 564 | } |
574 | 565 | ||
575 | 566 | ||
567 | static void markbeingfnz (global_State *g) { | ||
568 | GCObject *u = g->tobefnz; | ||
569 | if (u) { | ||
570 | do { | ||
571 | u = gch(u)->next; | ||
572 | lua_assert(testbit(gch(u)->marked, SEPARATED)); | ||
573 | lua_assert(!iswhite(u)); /* must be marked, if left from previous GC */ | ||
574 | makewhite(g, u); | ||
575 | reallymarkobject(g, u); | ||
576 | } while (u != g->tobefnz); | ||
577 | } | ||
578 | } | ||
579 | |||
580 | |||
576 | /* mark root set */ | 581 | /* mark root set */ |
577 | static void markroot (lua_State *L) { | 582 | static void markroot (lua_State *L) { |
578 | global_State *g = G(L); | 583 | global_State *g = G(L); |
@@ -584,6 +589,7 @@ static void markroot (lua_State *L) { | |||
584 | markvalue(g, gt(g->mainthread)); | 589 | markvalue(g, gt(g->mainthread)); |
585 | markvalue(g, registry(L)); | 590 | markvalue(g, registry(L)); |
586 | markmt(g); | 591 | markmt(g); |
592 | markbeingfnz(g); /* mark any finalizing userdata left from previous cycle */ | ||
587 | g->gcstate = GCSpropagate; | 593 | g->gcstate = GCSpropagate; |
588 | } | 594 | } |
589 | 595 | ||
@@ -598,6 +604,14 @@ static void remarkupvals (global_State *g) { | |||
598 | } | 604 | } |
599 | 605 | ||
600 | 606 | ||
607 | static void marklistofgrays (global_State *g, GCObject **l) { | ||
608 | lua_assert(g->gray == NULL); /* no grays left */ | ||
609 | g->gray = *l; /* now 'l' is new gray list */ | ||
610 | *l = NULL; | ||
611 | propagateall(g); | ||
612 | } | ||
613 | |||
614 | |||
601 | static void atomic (lua_State *L) { | 615 | static void atomic (lua_State *L) { |
602 | global_State *g = G(L); | 616 | global_State *g = G(L); |
603 | size_t udsize; /* total size of userdata to be finalized */ | 617 | size_t udsize; /* total size of userdata to be finalized */ |
@@ -612,17 +626,10 @@ static void atomic (lua_State *L) { | |||
612 | markobject(g, L); /* mark running thread */ | 626 | markobject(g, L); /* mark running thread */ |
613 | markmt(g); /* mark basic metatables (again) */ | 627 | markmt(g); /* mark basic metatables (again) */ |
614 | propagateall(g); | 628 | propagateall(g); |
615 | /* remark ephemeron tables */ | 629 | marklistofgrays(g, &g->ephemeron); /* remark ephemeron tables */ |
616 | g->gray = g->ephemeron; | 630 | marklistofgrays(g, &g->grayagain); /* remark gray again */ |
617 | g->ephemeron = NULL; | ||
618 | propagateall(g); | ||
619 | /* remark gray again */ | ||
620 | g->gray = g->grayagain; | ||
621 | g->grayagain = NULL; | ||
622 | propagateall(g); | ||
623 | convergeephemerons(g); | 631 | convergeephemerons(g); |
624 | udsize = luaC_separateudata(L, 0); /* separate userdata to be finalized */ | 632 | udsize = luaC_separateudata(L, 0); /* separate userdata to be finalized */ |
625 | marktmu(g); /* mark `preserved' userdata */ | ||
626 | udsize += propagateall(g); /* remark, to propagate `preserveness' */ | 633 | udsize += propagateall(g); /* remark, to propagate `preserveness' */ |
627 | convergeephemerons(g); | 634 | convergeephemerons(g); |
628 | /* remove collected objects from weak tables */ | 635 | /* remove collected objects from weak tables */ |
@@ -632,7 +639,6 @@ static void atomic (lua_State *L) { | |||
632 | /* flip current white */ | 639 | /* flip current white */ |
633 | g->currentwhite = cast_byte(otherwhite(g)); | 640 | g->currentwhite = cast_byte(otherwhite(g)); |
634 | g->sweepstrgc = 0; | 641 | g->sweepstrgc = 0; |
635 | g->sweepgc = &g->rootgc; | ||
636 | g->gcstate = GCSsweepstring; | 642 | g->gcstate = GCSsweepstring; |
637 | g->estimate = g->totalbytes - udsize; /* first estimate */ | 643 | g->estimate = g->totalbytes - udsize; /* first estimate */ |
638 | } | 644 | } |
@@ -660,10 +666,20 @@ static l_mem singlestep (lua_State *L) { | |||
660 | } | 666 | } |
661 | case GCSsweepstring: { | 667 | case GCSsweepstring: { |
662 | correctestimate(g, sweepwholelist(L, &g->strt.hash[g->sweepstrgc++])); | 668 | correctestimate(g, sweepwholelist(L, &g->strt.hash[g->sweepstrgc++])); |
663 | if (g->sweepstrgc >= g->strt.size) /* nothing more to sweep? */ | 669 | if (g->sweepstrgc >= g->strt.size) { /* nothing more to sweep? */ |
664 | g->gcstate = GCSsweep; /* end sweep-string phase */ | 670 | g->sweepgc = &g->tmudata; |
671 | g->gcstate = GCSsweeptmu; /* end sweep-string phase */ | ||
672 | } | ||
665 | return GCSWEEPCOST; | 673 | return GCSWEEPCOST; |
666 | } | 674 | } |
675 | case GCSsweeptmu: { | ||
676 | g->sweepgc = unmarklist(g, g->sweepgc, GCSWEEPMAX); | ||
677 | if (*g->sweepgc == NULL) { /* nothing more to sweep? */ | ||
678 | g->sweepgc = &g->rootgc; | ||
679 | g->gcstate = GCSsweep; /* sweep all other objects */ | ||
680 | } | ||
681 | return GCSWEEPMAX*GCSWEEPCOST; | ||
682 | } | ||
667 | case GCSsweep: { | 683 | case GCSsweep: { |
668 | correctestimate(g, g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX)); | 684 | correctestimate(g, g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX)); |
669 | if (*g->sweepgc == NULL) /* nothing more to sweep? */ | 685 | if (*g->sweepgc == NULL) /* nothing more to sweep? */ |
@@ -671,7 +687,7 @@ static l_mem singlestep (lua_State *L) { | |||
671 | return GCSWEEPMAX*GCSWEEPCOST; | 687 | return GCSWEEPMAX*GCSWEEPCOST; |
672 | } | 688 | } |
673 | case GCSfinalize: { | 689 | case GCSfinalize: { |
674 | if (g->tmudata) { | 690 | if (g->tobefnz) { |
675 | GCTM(L); | 691 | GCTM(L); |
676 | if (g->estimate > GCFINALIZECOST) | 692 | if (g->estimate > GCFINALIZECOST) |
677 | g->estimate -= GCFINALIZECOST; | 693 | g->estimate -= GCFINALIZECOST; |
@@ -721,19 +737,17 @@ void luaC_fullgc (lua_State *L, int isemergency) { | |||
721 | lua_assert(g->gckind == KGC_NORMAL); | 737 | lua_assert(g->gckind == KGC_NORMAL); |
722 | g->gckind = isemergency ? KGC_EMERGENCY : KGC_FORCED; | 738 | g->gckind = isemergency ? KGC_EMERGENCY : KGC_FORCED; |
723 | if (g->gcstate <= GCSpropagate) { | 739 | if (g->gcstate <= GCSpropagate) { |
724 | /* reset sweep marks to sweep all elements (returning them to white) */ | ||
725 | g->sweepstrgc = 0; | ||
726 | g->sweepgc = &g->rootgc; | ||
727 | /* reset other collector lists */ | 740 | /* reset other collector lists */ |
728 | g->gray = NULL; | 741 | g->gray = NULL; |
729 | g->grayagain = NULL; | 742 | g->grayagain = NULL; |
730 | g->weak = g->ephemeron = g->allweak = NULL; | 743 | g->weak = g->ephemeron = g->allweak = NULL; |
744 | g->sweepstrgc = 0; | ||
731 | g->gcstate = GCSsweepstring; | 745 | g->gcstate = GCSsweepstring; |
732 | } | 746 | } |
733 | lua_assert(g->gcstate != GCSpause && g->gcstate != GCSpropagate); | 747 | lua_assert(g->gcstate != GCSpause && g->gcstate != GCSpropagate); |
734 | /* finish any pending sweep phase */ | 748 | /* finish any pending sweep phase */ |
735 | while (g->gcstate != GCSfinalize) { | 749 | while (g->gcstate != GCSfinalize) { |
736 | lua_assert(g->gcstate == GCSsweepstring || g->gcstate == GCSsweep); | 750 | lua_assert(issweep(g)); |
737 | singlestep(L); | 751 | singlestep(L); |
738 | } | 752 | } |
739 | markroot(L); | 753 | markroot(L); |
@@ -753,7 +767,7 @@ void luaC_barrierf (lua_State *L, GCObject *o, GCObject *v) { | |||
753 | global_State *g = G(L); | 767 | global_State *g = G(L); |
754 | lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o)); | 768 | lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o)); |
755 | lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause); | 769 | lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause); |
756 | lua_assert(ttype(&o->gch) != LUA_TTABLE); | 770 | lua_assert(ttype(gch(o)) != LUA_TTABLE); |
757 | /* must keep invariant? */ | 771 | /* must keep invariant? */ |
758 | if (g->gcstate == GCSpropagate) | 772 | if (g->gcstate == GCSpropagate) |
759 | reallymarkobject(g, v); /* restore invariant */ | 773 | reallymarkobject(g, v); /* restore invariant */ |
@@ -775,17 +789,17 @@ void luaC_barrierback (lua_State *L, Table *t) { | |||
775 | 789 | ||
776 | void luaC_link (lua_State *L, GCObject *o, lu_byte tt) { | 790 | void luaC_link (lua_State *L, GCObject *o, lu_byte tt) { |
777 | global_State *g = G(L); | 791 | global_State *g = G(L); |
778 | o->gch.next = g->rootgc; | 792 | gch(o)->marked = luaC_white(g); |
793 | gch(o)->tt = tt; | ||
794 | gch(o)->next = g->rootgc; | ||
779 | g->rootgc = o; | 795 | g->rootgc = o; |
780 | o->gch.marked = luaC_white(g); | ||
781 | o->gch.tt = tt; | ||
782 | } | 796 | } |
783 | 797 | ||
784 | 798 | ||
785 | void luaC_linkupval (lua_State *L, UpVal *uv) { | 799 | void luaC_linkupval (lua_State *L, UpVal *uv) { |
786 | global_State *g = G(L); | 800 | global_State *g = G(L); |
787 | GCObject *o = obj2gco(uv); | 801 | GCObject *o = obj2gco(uv); |
788 | o->gch.next = g->rootgc; /* link upvalue into `rootgc' list */ | 802 | gch(o)->next = g->rootgc; /* link upvalue into `rootgc' list */ |
789 | g->rootgc = o; | 803 | g->rootgc = o; |
790 | if (isgray(o)) { | 804 | if (isgray(o)) { |
791 | if (g->gcstate == GCSpropagate) { | 805 | if (g->gcstate == GCSpropagate) { |
@@ -799,3 +813,22 @@ void luaC_linkupval (lua_State *L, UpVal *uv) { | |||
799 | } | 813 | } |
800 | } | 814 | } |
801 | 815 | ||
816 | |||
817 | void luaC_checkfinalizer (lua_State *L, Udata *u) { | ||
818 | global_State *g = G(L); | ||
819 | if (testbit(u->uv.marked, SEPARATED) || /* userdata is already separated... */ | ||
820 | isfinalized(&u->uv) || /* ... or is finalized... */ | ||
821 | gfasttm(g, u->uv.metatable, TM_GC) == NULL) /* or has no finalization? */ | ||
822 | return; /* nothing to be done */ | ||
823 | else { /* move 'u' from root list to tobefnz list */ | ||
824 | GCObject **p; | ||
825 | for (p = &g->rootgc; *p != obj2gco(u); p = &gch(*p)->next) | ||
826 | lua_assert(*p != NULL); /* 'u' must be in this list */ | ||
827 | *p = u->uv.next; /* remove 'u' from root list */ | ||
828 | u->uv.next = g->tmudata; /* link it in tobefnz list */ | ||
829 | g->tmudata = obj2gco(u); | ||
830 | l_setbit(u->uv.marked, SEPARATED); /* mark it as such */ | ||
831 | } | ||
832 | } | ||
833 | |||
834 | |||