diff options
Diffstat (limited to 'lgc.c')
-rw-r--r-- | lgc.c | 357 |
1 files changed, 203 insertions, 154 deletions
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | ** $Id: lgc.c,v 2.43 2008/02/11 15:46:03 roberto Exp roberto $ | 2 | ** $Id: lgc.c,v 2.44 2008/02/19 18:55:09 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 | */ |
@@ -53,6 +53,15 @@ | |||
53 | #define setthreshold(g) (g->GCthreshold = (g->estimate/100) * g->gcpause) | 53 | #define setthreshold(g) (g->GCthreshold = (g->estimate/100) * g->gcpause) |
54 | 54 | ||
55 | 55 | ||
56 | static void reallymarkobject (global_State *g, GCObject *o); | ||
57 | |||
58 | |||
59 | /* | ||
60 | ** {====================================================== | ||
61 | ** Generic functions | ||
62 | ** ======================================================= | ||
63 | */ | ||
64 | |||
56 | static void linktable (Table *h, GCObject **p) { | 65 | static void linktable (Table *h, GCObject **p) { |
57 | h->gclist = *p; | 66 | h->gclist = *p; |
58 | *p = obj2gco(h); | 67 | *p = obj2gco(h); |
@@ -83,6 +92,65 @@ static int iscleared (const TValue *o, int iskey) { | |||
83 | (ttisuserdata(o) && (!iskey && isfinalized(uvalue(o)))); | 92 | (ttisuserdata(o) && (!iskey && isfinalized(uvalue(o)))); |
84 | } | 93 | } |
85 | 94 | ||
95 | void luaC_barrierf (lua_State *L, GCObject *o, GCObject *v) { | ||
96 | global_State *g = G(L); | ||
97 | lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o)); | ||
98 | lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause); | ||
99 | lua_assert(ttype(gch(o)) != LUA_TTABLE); | ||
100 | /* must keep invariant? */ | ||
101 | if (g->gcstate == GCSpropagate) | ||
102 | reallymarkobject(g, v); /* restore invariant */ | ||
103 | else /* don't mind */ | ||
104 | makewhite(g, o); /* mark as white just to avoid other barriers */ | ||
105 | } | ||
106 | |||
107 | |||
108 | void luaC_barrierback (lua_State *L, Table *t) { | ||
109 | global_State *g = G(L); | ||
110 | GCObject *o = obj2gco(t); | ||
111 | lua_assert(isblack(o) && !isdead(g, o)); | ||
112 | lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause); | ||
113 | black2gray(o); /* make table gray (again) */ | ||
114 | t->gclist = g->grayagain; | ||
115 | g->grayagain = o; | ||
116 | } | ||
117 | |||
118 | |||
119 | void luaC_link (lua_State *L, GCObject *o, lu_byte tt) { | ||
120 | global_State *g = G(L); | ||
121 | gch(o)->marked = luaC_white(g); | ||
122 | gch(o)->tt = tt; | ||
123 | gch(o)->next = g->rootgc; | ||
124 | g->rootgc = o; | ||
125 | } | ||
126 | |||
127 | |||
128 | void luaC_linkupval (lua_State *L, UpVal *uv) { | ||
129 | global_State *g = G(L); | ||
130 | GCObject *o = obj2gco(uv); | ||
131 | gch(o)->next = g->rootgc; /* link upvalue into `rootgc' list */ | ||
132 | g->rootgc = o; | ||
133 | if (isgray(o)) { | ||
134 | if (g->gcstate == GCSpropagate) { | ||
135 | gray2black(o); /* closed upvalues need barrier */ | ||
136 | luaC_barrier(L, uv, uv->v); | ||
137 | } | ||
138 | else { /* sweep phase: sweep it (turning it into white) */ | ||
139 | makewhite(g, o); | ||
140 | lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause); | ||
141 | } | ||
142 | } | ||
143 | } | ||
144 | |||
145 | /* }====================================================== */ | ||
146 | |||
147 | |||
148 | |||
149 | /* | ||
150 | ** {====================================================== | ||
151 | ** Mark functions | ||
152 | ** ======================================================= | ||
153 | */ | ||
86 | 154 | ||
87 | static void reallymarkobject (global_State *g, GCObject *o) { | 155 | static void reallymarkobject (global_State *g, GCObject *o) { |
88 | lua_assert(iswhite(o) && !isdead(g, o)); | 156 | lua_assert(iswhite(o) && !isdead(g, o)); |
@@ -129,36 +197,60 @@ static void reallymarkobject (global_State *g, GCObject *o) { | |||
129 | } | 197 | } |
130 | 198 | ||
131 | 199 | ||
132 | /* move 'dead' udata that need finalization to list 'tobefnz' */ | 200 | static void markmt (global_State *g) { |
133 | size_t luaC_separateudata (lua_State *L, int all) { | 201 | int i; |
202 | for (i=0; i<NUM_TAGS; i++) | ||
203 | markobject(g, g->mt[i]); | ||
204 | } | ||
205 | |||
206 | |||
207 | static void markbeingfnz (global_State *g) { | ||
208 | GCObject *u = g->tobefnz; | ||
209 | if (u) { | ||
210 | do { | ||
211 | u = gch(u)->next; | ||
212 | lua_assert(testbit(gch(u)->marked, SEPARATED)); | ||
213 | lua_assert(!iswhite(u)); /* must be marked, if left from previous GC */ | ||
214 | makewhite(g, u); | ||
215 | reallymarkobject(g, u); | ||
216 | } while (u != g->tobefnz); | ||
217 | } | ||
218 | } | ||
219 | |||
220 | |||
221 | /* mark root set */ | ||
222 | static void markroot (lua_State *L) { | ||
134 | global_State *g = G(L); | 223 | global_State *g = G(L); |
135 | size_t deadmem = 0; | 224 | g->gray = NULL; |
136 | GCObject **p = &g->tmudata; | 225 | g->grayagain = NULL; |
137 | GCObject *curr; | 226 | g->weak = g->ephemeron = g->allweak = NULL; |
138 | while ((curr = *p) != NULL) { | 227 | markobject(g, g->mainthread); |
139 | lua_assert(ttisuserdata(gch(curr)) && !isfinalized(gco2u(curr))); | 228 | /* make global table be traversed before main stack */ |
140 | lua_assert(testbit(gch(curr)->marked, SEPARATED)); | 229 | markvalue(g, gt(g->mainthread)); |
141 | if (all) makewhite(g, curr); /* if 'all', collect all objects */ | 230 | markvalue(g, registry(L)); |
142 | if (!iswhite(curr)) /* not being collected? */ | 231 | markmt(g); |
143 | p = &gch(curr)->next; /* don't bother with it */ | 232 | markbeingfnz(g); /* mark any finalizing userdata left from previous cycle */ |
144 | else { | 233 | g->gcstate = GCSpropagate; |
145 | l_setbit(gch(curr)->marked, FINALIZEDBIT); /* won't be finalized again */ | 234 | } |
146 | reallymarkobject(g, curr); /* won't be collected now */ | 235 | |
147 | deadmem += sizeudata(gco2u(curr)); | 236 | |
148 | *p = gch(curr)->next; /* remove 'curr' from 'tmudata' list */ | 237 | static void remarkupvals (global_State *g) { |
149 | /* link 'curr' at the end of 'tobefnz' list */ | 238 | UpVal *uv; |
150 | if (g->tobefnz == NULL) /* list is empty? */ | 239 | for (uv = g->uvhead.u.l.next; uv != &g->uvhead; uv = uv->u.l.next) { |
151 | g->tobefnz = gch(curr)->next = curr; /* creates a circular list */ | 240 | lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv); |
152 | else { | 241 | if (isgray(obj2gco(uv))) |
153 | gch(curr)->next = gch(g->tobefnz)->next; | 242 | markvalue(g, uv->v); |
154 | gch(g->tobefnz)->next = curr; | ||
155 | g->tobefnz = curr; | ||
156 | } | ||
157 | } | ||
158 | } | 243 | } |
159 | return deadmem; | ||
160 | } | 244 | } |
161 | 245 | ||
246 | /* }====================================================== */ | ||
247 | |||
248 | |||
249 | /* | ||
250 | ** {====================================================== | ||
251 | ** Traverse functions | ||
252 | ** ======================================================= | ||
253 | */ | ||
162 | 254 | ||
163 | static void traverseweakvalue (global_State *g, Table *h) { | 255 | static void traverseweakvalue (global_State *g, Table *h) { |
164 | int i = sizenode(h); | 256 | int i = sizenode(h); |
@@ -382,6 +474,14 @@ static size_t propagateall (global_State *g) { | |||
382 | } | 474 | } |
383 | 475 | ||
384 | 476 | ||
477 | static void traverselistofgrays (global_State *g, GCObject **l) { | ||
478 | lua_assert(g->gray == NULL); /* no grays left */ | ||
479 | g->gray = *l; /* now 'l' is new gray list */ | ||
480 | *l = NULL; | ||
481 | propagateall(g); | ||
482 | } | ||
483 | |||
484 | |||
385 | static void convergeephemerons (global_State *g) { | 485 | static void convergeephemerons (global_State *g) { |
386 | int changed; | 486 | int changed; |
387 | do { | 487 | do { |
@@ -399,11 +499,16 @@ static void convergeephemerons (global_State *g) { | |||
399 | } while (changed); | 499 | } while (changed); |
400 | } | 500 | } |
401 | 501 | ||
502 | /* }====================================================== */ | ||
402 | 503 | ||
403 | 504 | ||
404 | /* | 505 | /* |
405 | ** clear collected entries from weaktables | 506 | ** {====================================================== |
507 | ** Sweep Functions | ||
508 | ** ======================================================= | ||
406 | */ | 509 | */ |
510 | |||
511 | /* clear collected entries from weaktables */ | ||
407 | static void cleartable (GCObject *l) { | 512 | static void cleartable (GCObject *l) { |
408 | while (l) { | 513 | while (l) { |
409 | Table *h = gco2t(l); | 514 | Table *h = gco2t(l); |
@@ -445,7 +550,6 @@ static void freeobj (lua_State *L, GCObject *o) { | |||
445 | } | 550 | } |
446 | 551 | ||
447 | 552 | ||
448 | |||
449 | #define sweepwholelist(L,p) sweeplist(L,p,MAX_LUMEM) | 553 | #define sweepwholelist(L,p) sweeplist(L,p,MAX_LUMEM) |
450 | 554 | ||
451 | 555 | ||
@@ -471,6 +575,8 @@ static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count) { | |||
471 | } | 575 | } |
472 | 576 | ||
473 | 577 | ||
578 | /* sweep list where all objects are alive (dead udata were removed | ||
579 | when separated from this list) */ | ||
474 | static GCObject **unmarklist (global_State *g, GCObject **p, lu_mem count) { | 580 | static GCObject **unmarklist (global_State *g, GCObject **p, lu_mem count) { |
475 | for (; *p != NULL && count-- > 0; p = &gch(*p)->next) { | 581 | for (; *p != NULL && count-- > 0; p = &gch(*p)->next) { |
476 | lua_assert(ttisuserdata(gch(*p)) && !isdead(g, *p)); | 582 | lua_assert(ttisuserdata(gch(*p)) && !isdead(g, *p)); |
@@ -479,6 +585,14 @@ static GCObject **unmarklist (global_State *g, GCObject **p, lu_mem count) { | |||
479 | return p; | 585 | return p; |
480 | } | 586 | } |
481 | 587 | ||
588 | /* }====================================================== */ | ||
589 | |||
590 | |||
591 | /* | ||
592 | ** {====================================================== | ||
593 | ** Finalization | ||
594 | ** ======================================================= | ||
595 | */ | ||
482 | 596 | ||
483 | static void checkSizes (lua_State *L) { | 597 | static void checkSizes (lua_State *L) { |
484 | global_State *g = G(L); | 598 | global_State *g = G(L); |
@@ -541,6 +655,63 @@ void luaC_callGCTM (lua_State *L) { | |||
541 | } | 655 | } |
542 | 656 | ||
543 | 657 | ||
658 | /* move 'dead' udata that need finalization to list 'tobefnz' */ | ||
659 | size_t luaC_separateudata (lua_State *L, int all) { | ||
660 | global_State *g = G(L); | ||
661 | size_t deadmem = 0; | ||
662 | GCObject **p = &g->tmudata; | ||
663 | GCObject *curr; | ||
664 | while ((curr = *p) != NULL) { | ||
665 | lua_assert(ttisuserdata(gch(curr)) && !isfinalized(gco2u(curr))); | ||
666 | lua_assert(testbit(gch(curr)->marked, SEPARATED)); | ||
667 | if (all) makewhite(g, curr); /* if 'all', collect all objects */ | ||
668 | if (!iswhite(curr)) /* not being collected? */ | ||
669 | p = &gch(curr)->next; /* don't bother with it */ | ||
670 | else { | ||
671 | l_setbit(gch(curr)->marked, FINALIZEDBIT); /* won't be finalized again */ | ||
672 | reallymarkobject(g, curr); /* won't be collected now */ | ||
673 | deadmem += sizeudata(gco2u(curr)); | ||
674 | *p = gch(curr)->next; /* remove 'curr' from 'tmudata' list */ | ||
675 | /* link 'curr' at the end of 'tobefnz' list */ | ||
676 | if (g->tobefnz == NULL) /* list is empty? */ | ||
677 | g->tobefnz = gch(curr)->next = curr; /* creates a circular list */ | ||
678 | else { | ||
679 | gch(curr)->next = gch(g->tobefnz)->next; | ||
680 | gch(g->tobefnz)->next = curr; | ||
681 | g->tobefnz = curr; | ||
682 | } | ||
683 | } | ||
684 | } | ||
685 | return deadmem; | ||
686 | } | ||
687 | |||
688 | |||
689 | void luaC_checkfinalizer (lua_State *L, Udata *u) { | ||
690 | global_State *g = G(L); | ||
691 | if (testbit(u->uv.marked, SEPARATED) || /* userdata is already separated... */ | ||
692 | isfinalized(&u->uv) || /* ... or is finalized... */ | ||
693 | gfasttm(g, u->uv.metatable, TM_GC) == NULL) /* or has no finalization? */ | ||
694 | return; /* nothing to be done */ | ||
695 | else { /* move 'u' from root list to 'tmudata' list */ | ||
696 | GCObject **p; | ||
697 | for (p = &g->rootgc; *p != obj2gco(u); p = &gch(*p)->next) | ||
698 | lua_assert(*p != NULL); /* 'u' must be in this list */ | ||
699 | *p = u->uv.next; /* remove 'u' from root list */ | ||
700 | u->uv.next = g->tmudata; /* link it in 'tmudata' list */ | ||
701 | g->tmudata = obj2gco(u); | ||
702 | l_setbit(u->uv.marked, SEPARATED); /* mark it as such */ | ||
703 | } | ||
704 | } | ||
705 | |||
706 | /* }====================================================== */ | ||
707 | |||
708 | |||
709 | /* | ||
710 | ** {====================================================== | ||
711 | ** GC control | ||
712 | ** ======================================================= | ||
713 | */ | ||
714 | |||
544 | void luaC_freeall (lua_State *L) { | 715 | void luaC_freeall (lua_State *L) { |
545 | global_State *g = G(L); | 716 | global_State *g = G(L); |
546 | int i; | 717 | int i; |
@@ -557,61 +728,6 @@ void luaC_freeall (lua_State *L) { | |||
557 | } | 728 | } |
558 | 729 | ||
559 | 730 | ||
560 | static void markmt (global_State *g) { | ||
561 | int i; | ||
562 | for (i=0; i<NUM_TAGS; i++) | ||
563 | markobject(g, g->mt[i]); | ||
564 | } | ||
565 | |||
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 | |||
581 | /* mark root set */ | ||
582 | static void markroot (lua_State *L) { | ||
583 | global_State *g = G(L); | ||
584 | g->gray = NULL; | ||
585 | g->grayagain = NULL; | ||
586 | g->weak = g->ephemeron = g->allweak = NULL; | ||
587 | markobject(g, g->mainthread); | ||
588 | /* make global table be traversed before main stack */ | ||
589 | markvalue(g, gt(g->mainthread)); | ||
590 | markvalue(g, registry(L)); | ||
591 | markmt(g); | ||
592 | markbeingfnz(g); /* mark any finalizing userdata left from previous cycle */ | ||
593 | g->gcstate = GCSpropagate; | ||
594 | } | ||
595 | |||
596 | |||
597 | static void remarkupvals (global_State *g) { | ||
598 | UpVal *uv; | ||
599 | for (uv = g->uvhead.u.l.next; uv != &g->uvhead; uv = uv->u.l.next) { | ||
600 | lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv); | ||
601 | if (isgray(obj2gco(uv))) | ||
602 | markvalue(g, uv->v); | ||
603 | } | ||
604 | } | ||
605 | |||
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 | |||
615 | static void atomic (lua_State *L) { | 731 | static void atomic (lua_State *L) { |
616 | global_State *g = G(L); | 732 | global_State *g = G(L); |
617 | size_t udsize; /* total size of userdata to be finalized */ | 733 | size_t udsize; /* total size of userdata to be finalized */ |
@@ -626,8 +742,8 @@ static void atomic (lua_State *L) { | |||
626 | markobject(g, L); /* mark running thread */ | 742 | markobject(g, L); /* mark running thread */ |
627 | markmt(g); /* mark basic metatables (again) */ | 743 | markmt(g); /* mark basic metatables (again) */ |
628 | propagateall(g); | 744 | propagateall(g); |
629 | marklistofgrays(g, &g->ephemeron); /* remark ephemeron tables */ | 745 | traverselistofgrays(g, &g->ephemeron); /* remark ephemeron tables */ |
630 | marklistofgrays(g, &g->grayagain); /* remark gray again */ | 746 | traverselistofgrays(g, &g->grayagain); /* remark gray again */ |
631 | convergeephemerons(g); | 747 | convergeephemerons(g); |
632 | udsize = luaC_separateudata(L, 0); /* separate userdata to be finalized */ | 748 | udsize = luaC_separateudata(L, 0); /* separate userdata to be finalized */ |
633 | udsize += propagateall(g); /* remark, to propagate `preserveness' */ | 749 | udsize += propagateall(g); /* remark, to propagate `preserveness' */ |
@@ -762,73 +878,6 @@ void luaC_fullgc (lua_State *L, int isemergency) { | |||
762 | setthreshold(g); | 878 | setthreshold(g); |
763 | } | 879 | } |
764 | 880 | ||
765 | 881 | /* }====================================================== */ | |
766 | void luaC_barrierf (lua_State *L, GCObject *o, GCObject *v) { | ||
767 | global_State *g = G(L); | ||
768 | lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o)); | ||
769 | lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause); | ||
770 | lua_assert(ttype(gch(o)) != LUA_TTABLE); | ||
771 | /* must keep invariant? */ | ||
772 | if (g->gcstate == GCSpropagate) | ||
773 | reallymarkobject(g, v); /* restore invariant */ | ||
774 | else /* don't mind */ | ||
775 | makewhite(g, o); /* mark as white just to avoid other barriers */ | ||
776 | } | ||
777 | |||
778 | |||
779 | void luaC_barrierback (lua_State *L, Table *t) { | ||
780 | global_State *g = G(L); | ||
781 | GCObject *o = obj2gco(t); | ||
782 | lua_assert(isblack(o) && !isdead(g, o)); | ||
783 | lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause); | ||
784 | black2gray(o); /* make table gray (again) */ | ||
785 | t->gclist = g->grayagain; | ||
786 | g->grayagain = o; | ||
787 | } | ||
788 | |||
789 | |||
790 | void luaC_link (lua_State *L, GCObject *o, lu_byte tt) { | ||
791 | global_State *g = G(L); | ||
792 | gch(o)->marked = luaC_white(g); | ||
793 | gch(o)->tt = tt; | ||
794 | gch(o)->next = g->rootgc; | ||
795 | g->rootgc = o; | ||
796 | } | ||
797 | |||
798 | |||
799 | void luaC_linkupval (lua_State *L, UpVal *uv) { | ||
800 | global_State *g = G(L); | ||
801 | GCObject *o = obj2gco(uv); | ||
802 | gch(o)->next = g->rootgc; /* link upvalue into `rootgc' list */ | ||
803 | g->rootgc = o; | ||
804 | if (isgray(o)) { | ||
805 | if (g->gcstate == GCSpropagate) { | ||
806 | gray2black(o); /* closed upvalues need barrier */ | ||
807 | luaC_barrier(L, uv, uv->v); | ||
808 | } | ||
809 | else { /* sweep phase: sweep it (turning it into white) */ | ||
810 | makewhite(g, o); | ||
811 | lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause); | ||
812 | } | ||
813 | } | ||
814 | } | ||
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 | 882 | ||
834 | 883 | ||