aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2010-11-26 12:32:31 -0200
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2010-11-26 12:32:31 -0200
commit9b7a12c46d85666e0e60efd79b39495589e497e8 (patch)
treec69acde3d3e300fa0db87004045e7599cbbca7a2
parent5b33e398557d7afad4a916bb483e9894e4b9b3d4 (diff)
downloadlua-9b7a12c46d85666e0e60efd79b39495589e497e8.tar.gz
lua-9b7a12c46d85666e0e60efd79b39495589e497e8.tar.bz2
lua-9b7a12c46d85666e0e60efd79b39495589e497e8.zip
finalizers (__gc) for tables
-rw-r--r--lapi.c5
-rw-r--r--lgc.c72
-rw-r--r--lgc.h4
-rw-r--r--lobject.h6
-rw-r--r--lstate.c4
-rw-r--r--lstate.h8
-rw-r--r--ltests.c21
7 files changed, 63 insertions, 57 deletions
diff --git a/lapi.c b/lapi.c
index 1e97cc46..a1796db3 100644
--- a/lapi.c
+++ b/lapi.c
@@ -1,5 +1,5 @@
1/* 1/*
2** $Id: lapi.c,v 2.139 2010/10/25 20:31:11 roberto Exp roberto $ 2** $Id: lapi.c,v 2.141 2010/11/18 19:15:00 roberto Exp roberto $
3** Lua API 3** Lua API
4** See Copyright Notice in lua.h 4** See Copyright Notice in lua.h
5*/ 5*/
@@ -760,13 +760,14 @@ LUA_API int lua_setmetatable (lua_State *L, int objindex) {
760 hvalue(obj)->metatable = mt; 760 hvalue(obj)->metatable = mt;
761 if (mt) 761 if (mt)
762 luaC_objbarrierback(L, gcvalue(obj), mt); 762 luaC_objbarrierback(L, gcvalue(obj), mt);
763 luaC_checkfinalizer(L, gcvalue(obj), mt);
763 break; 764 break;
764 } 765 }
765 case LUA_TUSERDATA: { 766 case LUA_TUSERDATA: {
766 uvalue(obj)->metatable = mt; 767 uvalue(obj)->metatable = mt;
767 if (mt) { 768 if (mt) {
768 luaC_objbarrier(L, rawuvalue(obj), mt); 769 luaC_objbarrier(L, rawuvalue(obj), mt);
769 luaC_checkfinalizer(L, rawuvalue(obj)); 770 luaC_checkfinalizer(L, gcvalue(obj), mt);
770 } 771 }
771 break; 772 break;
772 } 773 }
diff --git a/lgc.c b/lgc.c
index f7072ab7..494791ed 100644
--- a/lgc.c
+++ b/lgc.c
@@ -1,5 +1,5 @@
1/* 1/*
2** $Id: lgc.c,v 2.102 2010/09/03 14:14:01 roberto Exp roberto $ 2** $Id: lgc.c,v 2.104 2010/11/18 19:15:00 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*/
@@ -68,7 +68,7 @@
68#define stringmark(s) ((void)((s) && resetbits((s)->tsv.marked, WHITEBITS))) 68#define stringmark(s) ((void)((s) && resetbits((s)->tsv.marked, WHITEBITS)))
69 69
70 70
71#define isfinalized(u) testbit((u)->marked, FINALIZEDBIT) 71#define isfinalized(x) testbit(gch(x)->marked, FINALIZEDBIT)
72 72
73#define checkdeadkey(n) lua_assert(!ttisdeadkey(gkey(n)) || ttisnil(gval(n))) 73#define checkdeadkey(n) lua_assert(!ttisdeadkey(gkey(n)) || ttisnil(gval(n)))
74 74
@@ -114,12 +114,11 @@ static void removeentry (Node *n) {
114*/ 114*/
115static int iscleared (const TValue *o, int iskey) { 115static int iscleared (const TValue *o, int iskey) {
116 if (!iscollectable(o)) return 0; 116 if (!iscollectable(o)) return 0;
117 if (ttisstring(o)) { 117 else if (ttisstring(o)) {
118 stringmark(rawtsvalue(o)); /* strings are `values', so are never weak */ 118 stringmark(rawtsvalue(o)); /* strings are `values', so are never weak */
119 return 0; 119 return 0;
120 } 120 }
121 return iswhite(gcvalue(o)) || 121 else return iswhite(gcvalue(o)) || (!iskey && isfinalized(gcvalue(o)));
122 (ttisuserdata(o) && (!iskey && isfinalized(uvalue(o))));
123} 122}
124 123
125 124
@@ -687,19 +686,18 @@ static void checkSizes (lua_State *L) {
687} 686}
688 687
689 688
690static Udata *udata2finalize (global_State *g) { 689static GCObject *udata2finalize (global_State *g) {
691 GCObject *o = g->tobefnz; /* get first element */ 690 GCObject *o = g->tobefnz; /* get first element */
692 Udata *u = rawgco2u(o); 691 lua_assert(isfinalized(o));
693 lua_assert(isfinalized(&u->uv));
694 lua_assert(!isold(o)); 692 lua_assert(!isold(o));
695 g->tobefnz = u->uv.next; /* remove it from 'tobefnz' list */ 693 g->tobefnz = gch(o)->next; /* remove it from 'tobefnz' list */
696 u->uv.next = g->allgc; /* return it to 'allgc' list */ 694 gch(o)->next = g->allgc; /* return it to 'allgc' list */
697 g->allgc = o; 695 g->allgc = o;
698 resetbit(u->uv.marked, SEPARATED); /* mark that it is not in 'tobefnz' */ 696 resetbit(gch(o)->marked, SEPARATED); /* mark that it is not in 'tobefnz' */
699 resetoldbit(o); /* see MOVE OLD rule */ 697 resetoldbit(o); /* see MOVE OLD rule */
700 if (!keepinvariant(g)) /* not keeping invariant? */ 698 if (!keepinvariant(g)) /* not keeping invariant? */
701 makewhite(g, o); /* "sweep" object */ 699 makewhite(g, o); /* "sweep" object */
702 return u; 700 return o;
703} 701}
704 702
705 703
@@ -711,8 +709,10 @@ static void dothecall (lua_State *L, void *ud) {
711 709
712static void GCTM (lua_State *L, int propagateerrors) { 710static void GCTM (lua_State *L, int propagateerrors) {
713 global_State *g = G(L); 711 global_State *g = G(L);
714 Udata *udata = udata2finalize(g); 712 const TValue *tm;
715 const TValue *tm = gfasttm(g, udata->uv.metatable, TM_GC); 713 TValue v;
714 setgcovalue(L, &v, udata2finalize(g));
715 tm = luaT_gettmbyobj(L, &v, TM_GC);
716 if (tm != NULL && ttisfunction(tm)) { /* is there a finalizer? */ 716 if (tm != NULL && ttisfunction(tm)) { /* is there a finalizer? */
717 int status; 717 int status;
718 lu_byte oldah = L->allowhook; 718 lu_byte oldah = L->allowhook;
@@ -720,7 +720,7 @@ static void GCTM (lua_State *L, int propagateerrors) {
720 L->allowhook = 0; /* stop debug hooks during GC tag method */ 720 L->allowhook = 0; /* stop debug hooks during GC tag method */
721 stopgc(g); /* avoid GC steps */ 721 stopgc(g); /* avoid GC steps */
722 setobj2s(L, L->top, tm); /* push finalizer... */ 722 setobj2s(L, L->top, tm); /* push finalizer... */
723 setuvalue(L, L->top+1, udata); /* ... and its argument */ 723 setobj2s(L, L->top + 1, &v); /* ... and its argument */
724 L->top += 2; /* and (next line) call the finalizer */ 724 L->top += 2; /* and (next line) call the finalizer */
725 status = luaD_pcall(L, dothecall, NULL, savestack(L, L->top - 2), 0); 725 status = luaD_pcall(L, dothecall, NULL, savestack(L, L->top - 2), 0);
726 L->allowhook = oldah; /* restore hooks */ 726 L->allowhook = oldah; /* restore hooks */
@@ -738,25 +738,25 @@ static void GCTM (lua_State *L, int propagateerrors) {
738 738
739 739
740/* 740/*
741** move all unreachable udata that need finalization from list 'udgc' to 741** move all unreachable objects that need finalization from list 'finobj'
742** list 'tobefnz' 742** to list 'tobefnz'
743*/ 743*/
744void luaC_separateudata (lua_State *L, int all) { 744void luaC_separateudata (lua_State *L, int all) {
745 global_State *g = G(L); 745 global_State *g = G(L);
746 GCObject **p = &g->udgc; 746 GCObject **p = &g->finobj;
747 GCObject *curr; 747 GCObject *curr;
748 GCObject **lastnext = &g->tobefnz; 748 GCObject **lastnext = &g->tobefnz;
749 /* find last 'next' field in 'tobefnz' list (to add elements in its end) */ 749 /* find last 'next' field in 'tobefnz' list (to add elements in its end) */
750 while (*lastnext != NULL) 750 while (*lastnext != NULL)
751 lastnext = &gch(*lastnext)->next; 751 lastnext = &gch(*lastnext)->next;
752 while ((curr = *p) != NULL) { /* traverse all finalizable objects */ 752 while ((curr = *p) != NULL) { /* traverse all finalizable objects */
753 lua_assert(gch(curr)->tt == LUA_TUSERDATA && !isfinalized(gco2u(curr))); 753 lua_assert(!isfinalized(curr));
754 lua_assert(testbit(gch(curr)->marked, SEPARATED)); 754 lua_assert(testbit(gch(curr)->marked, SEPARATED));
755 if (!(all || iswhite(curr))) /* not being collected? */ 755 if (!(all || iswhite(curr))) /* not being collected? */
756 p = &gch(curr)->next; /* don't bother with it */ 756 p = &gch(curr)->next; /* don't bother with it */
757 else { 757 else {
758 l_setbit(gch(curr)->marked, FINALIZEDBIT); /* won't be finalized again */ 758 l_setbit(gch(curr)->marked, FINALIZEDBIT); /* won't be finalized again */
759 *p = gch(curr)->next; /* remove 'curr' from 'udgc' list */ 759 *p = gch(curr)->next; /* remove 'curr' from 'finobj' list */
760 gch(curr)->next = *lastnext; /* link at the end of 'tobefnz' list */ 760 gch(curr)->next = *lastnext; /* link at the end of 'tobefnz' list */
761 *lastnext = curr; 761 *lastnext = curr;
762 lastnext = &gch(curr)->next; 762 lastnext = &gch(curr)->next;
@@ -766,23 +766,23 @@ void luaC_separateudata (lua_State *L, int all) {
766 766
767 767
768/* 768/*
769** if userdata 'u' has a finalizer, remove it from 'allgc' list (must 769** if object 'o' has a finalizer, remove it from 'allgc' list (must
770** search the list to find it) and link it in 'udgc' list. 770** search the list to find it) and link it in 'finobj' list.
771*/ 771*/
772void luaC_checkfinalizer (lua_State *L, Udata *u) { 772void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) {
773 global_State *g = G(L); 773 global_State *g = G(L);
774 if (testbit(u->uv.marked, SEPARATED) || /* udata is already separated... */ 774 if (testbit(gch(o)->marked, SEPARATED) || /* obj. is already separated... */
775 isfinalized(&u->uv) || /* ... or is finalized... */ 775 isfinalized(o) || /* ... or is finalized... */
776 gfasttm(g, u->uv.metatable, TM_GC) == NULL) /* or has no finalizer? */ 776 gfasttm(g, mt, TM_GC) == NULL) /* or has no finalizer? */
777 return; /* nothing to be done */ 777 return; /* nothing to be done */
778 else { /* move 'u' to 'udgc' list */ 778 else { /* move 'o' to 'finobj' list */
779 GCObject **p; 779 GCObject **p;
780 for (p = &g->allgc; *p != obj2gco(u); p = &gch(*p)->next) ; 780 for (p = &g->allgc; *p != o; p = &gch(*p)->next) ;
781 *p = u->uv.next; /* remove 'u' from root list */ 781 *p = gch(o)->next; /* remove 'o' from root list */
782 u->uv.next = g->udgc; /* link it in list 'udgc' */ 782 gch(o)->next = g->finobj; /* link it in list 'finobj' */
783 g->udgc = obj2gco(u); 783 g->finobj = o;
784 l_setbit(u->uv.marked, SEPARATED); /* mark it as such */ 784 l_setbit(gch(o)->marked, SEPARATED); /* mark it as such */
785 resetoldbit(obj2gco(u)); /* see MOVE OLD rule */ 785 resetoldbit(o); /* see MOVE OLD rule */
786 } 786 }
787} 787}
788 788
@@ -837,8 +837,8 @@ void luaC_freeallobjects (lua_State *L) {
837 /* following "white" makes all objects look dead */ 837 /* following "white" makes all objects look dead */
838 g->currentwhite = WHITEBITS; 838 g->currentwhite = WHITEBITS;
839 g->gckind = KGC_NORMAL; 839 g->gckind = KGC_NORMAL;
840 sweepwholelist(L, &g->udgc); 840 sweepwholelist(L, &g->finobj);
841 lua_assert(g->udgc == NULL); 841 lua_assert(g->finobj == NULL);
842 sweepwholelist(L, &g->allgc); 842 sweepwholelist(L, &g->allgc);
843 lua_assert(g->allgc == NULL); 843 lua_assert(g->allgc == NULL);
844 for (i = 0; i < g->strt.size; i++) /* free all string lists */ 844 for (i = 0; i < g->strt.size; i++) /* free all string lists */
@@ -905,7 +905,7 @@ static l_mem singlestep (lua_State *L) {
905 return GCSWEEPCOST; 905 return GCSWEEPCOST;
906 } 906 }
907 else { /* no more strings to sweep */ 907 else { /* no more strings to sweep */
908 g->sweepgc = &g->udgc; /* prepare to sweep userdata */ 908 g->sweepgc = &g->finobj; /* prepare to sweep finalizable objects */
909 g->gcstate = GCSsweepudata; 909 g->gcstate = GCSsweepudata;
910 return 0; 910 return 0;
911 } 911 }
diff --git a/lgc.h b/lgc.h
index c7cfdf7c..4c5d2eca 100644
--- a/lgc.h
+++ b/lgc.h
@@ -1,5 +1,5 @@
1/* 1/*
2** $Id: lgc.h,v 2.43 2010/06/07 16:55:34 roberto Exp roberto $ 2** $Id: lgc.h,v 2.45 2010/11/18 19:15:00 roberto Exp $
3** Garbage Collector 3** Garbage Collector
4** See Copyright Notice in lua.h 4** See Copyright Notice in lua.h
5*/ 5*/
@@ -137,7 +137,7 @@ LUAI_FUNC GCObject *luaC_newobj (lua_State *L, int tt, size_t sz,
137LUAI_FUNC void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v); 137LUAI_FUNC void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v);
138LUAI_FUNC void luaC_barrierback_ (lua_State *L, GCObject *o); 138LUAI_FUNC void luaC_barrierback_ (lua_State *L, GCObject *o);
139LUAI_FUNC void luaC_barrierproto_ (lua_State *L, Proto *p, Closure *c); 139LUAI_FUNC void luaC_barrierproto_ (lua_State *L, Proto *p, Closure *c);
140LUAI_FUNC void luaC_checkfinalizer (lua_State *L, Udata *u); 140LUAI_FUNC void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt);
141LUAI_FUNC void luaC_checkupvalcolor (global_State *g, UpVal *uv); 141LUAI_FUNC void luaC_checkupvalcolor (global_State *g, UpVal *uv);
142LUAI_FUNC void luaC_changemode (lua_State *L, int mode); 142LUAI_FUNC void luaC_changemode (lua_State *L, int mode);
143 143
diff --git a/lobject.h b/lobject.h
index a23cc560..b9e554b4 100644
--- a/lobject.h
+++ b/lobject.h
@@ -1,5 +1,5 @@
1/* 1/*
2** $Id: lobject.h,v 2.41 2010/06/04 13:25:10 roberto Exp roberto $ 2** $Id: lobject.h,v 2.42 2010/07/26 15:53:23 roberto Exp roberto $
3** Type definitions for Lua objects 3** Type definitions for Lua objects
4** See Copyright Notice in lua.h 4** See Copyright Notice in lua.h
5*/ 5*/
@@ -153,6 +153,10 @@ typedef struct lua_TValue {
153#define setbvalue(obj,x) \ 153#define setbvalue(obj,x) \
154 { TValue *i_o=(obj); i_o->value_.b=(x); i_o->tt_=LUA_TBOOLEAN; } 154 { TValue *i_o=(obj); i_o->value_.b=(x); i_o->tt_=LUA_TBOOLEAN; }
155 155
156#define setgcovalue(L,obj,x) \
157 { TValue *i_o=(obj); GCObject *i_g=(x); \
158 i_o->value_.gc=i_g; i_o->tt_=gch(i_g)->tt; }
159
156#define setsvalue(L,obj,x) \ 160#define setsvalue(L,obj,x) \
157 { TValue *i_o=(obj); \ 161 { TValue *i_o=(obj); \
158 i_o->value_.gc=cast(GCObject *, (x)); i_o->tt_=LUA_TSTRING; \ 162 i_o->value_.gc=cast(GCObject *, (x)); i_o->tt_=LUA_TSTRING; \
diff --git a/lstate.c b/lstate.c
index ef4f89cd..fee415b6 100644
--- a/lstate.c
+++ b/lstate.c
@@ -1,5 +1,5 @@
1/* 1/*
2** $Id: lstate.c,v 2.85 2010/04/30 18:36:22 roberto Exp roberto $ 2** $Id: lstate.c,v 2.86 2010/09/03 14:14:01 roberto Exp roberto $
3** Global State 3** Global State
4** See Copyright Notice in lua.h 4** See Copyright Notice in lua.h
5*/ 5*/
@@ -252,7 +252,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
252 g->version = lua_version(NULL); 252 g->version = lua_version(NULL);
253 g->gcstate = GCSpause; 253 g->gcstate = GCSpause;
254 g->allgc = NULL; 254 g->allgc = NULL;
255 g->udgc = NULL; 255 g->finobj = NULL;
256 g->tobefnz = NULL; 256 g->tobefnz = NULL;
257 g->gray = g->grayagain = NULL; 257 g->gray = g->grayagain = NULL;
258 g->weak = g->ephemeron = g->allweak = NULL; 258 g->weak = g->ephemeron = g->allweak = NULL;
diff --git a/lstate.h b/lstate.h
index a8923ca3..40c3ac16 100644
--- a/lstate.h
+++ b/lstate.h
@@ -1,5 +1,5 @@
1/* 1/*
2** $Id: lstate.h,v 2.67 2010/09/30 17:21:31 roberto Exp roberto $ 2** $Id: lstate.h,v 2.68 2010/10/29 17:52:46 roberto Exp roberto $
3** Global State 3** Global State
4** See Copyright Notice in lua.h 4** See Copyright Notice in lua.h
5*/ 5*/
@@ -32,9 +32,9 @@
32** when traversing the respective threads, but the thread may already be 32** when traversing the respective threads, but the thread may already be
33** dead, while the upvalue is still accessible through closures.) 33** dead, while the upvalue is still accessible through closures.)
34** 34**
35** Userdata with finalizers are kept in the list g->udgc. 35** Objects with finalizers are kept in the list g->finobj.
36** 36**
37** The list g->tobefnz links all userdata being finalized. 37** The list g->tobefnz links all objects being finalized.
38 38
39*/ 39*/
40 40
@@ -125,7 +125,7 @@ typedef struct global_State {
125 lu_byte gckind; /* kind of GC running */ 125 lu_byte gckind; /* kind of GC running */
126 int sweepstrgc; /* position of sweep in `strt' */ 126 int sweepstrgc; /* position of sweep in `strt' */
127 GCObject *allgc; /* list of all collectable objects */ 127 GCObject *allgc; /* list of all collectable objects */
128 GCObject *udgc; /* list of collectable userdata with finalizers */ 128 GCObject *finobj; /* list of collectable objects with finalizers */
129 GCObject **sweepgc; /* current position of sweep */ 129 GCObject **sweepgc; /* current position of sweep */
130 GCObject *gray; /* list of gray objects */ 130 GCObject *gray; /* list of gray objects */
131 GCObject *grayagain; /* list of objects to be traversed atomically */ 131 GCObject *grayagain; /* list of objects to be traversed atomically */
diff --git a/ltests.c b/ltests.c
index 19d33ec8..7d629f48 100644
--- a/ltests.c
+++ b/ltests.c
@@ -1,5 +1,5 @@
1/* 1/*
2** $Id: ltests.c,v 2.112 2010/07/28 15:51:59 roberto Exp roberto $ 2** $Id: ltests.c,v 2.113 2010/11/16 17:43:29 roberto Exp roberto $
3** Internal Module for Debugging of the Lua Implementation 3** Internal Module for Debugging of the Lua Implementation
4** See Copyright Notice in lua.h 4** See Copyright Notice in lua.h
5*/ 5*/
@@ -429,20 +429,21 @@ int lua_checkmemory (lua_State *L) {
429 checkobject(g, o); 429 checkobject(g, o);
430 lua_assert(!testbit(o->gch.marked, SEPARATED)); 430 lua_assert(!testbit(o->gch.marked, SEPARATED));
431 } 431 }
432 /* check 'udgc' list */ 432 /* check 'finobj' list */
433 checkold(g, g->udgc); 433 checkold(g, g->finobj);
434 for (o = g->udgc; o != NULL; o = gch(o)->next) { 434 for (o = g->finobj; o != NULL; o = gch(o)->next) {
435 lua_assert(gch(o)->tt == LUA_TUSERDATA && 435 lua_assert(!isdead(g, o) && testbit(o->gch.marked, SEPARATED));
436 !isdead(g, o) && 436 lua_assert(gch(o)->tt == LUA_TUSERDATA ||
437 testbit(o->gch.marked, SEPARATED)); 437 gch(o)->tt == LUA_TTABLE);
438 checkobject(g, o); 438 checkobject(g, o);
439 } 439 }
440 /* check 'tobefnz' list */ 440 /* check 'tobefnz' list */
441 checkold(g, g->tobefnz); 441 checkold(g, g->tobefnz);
442 for (o = g->tobefnz; o != NULL; o = gch(o)->next) { 442 for (o = g->tobefnz; o != NULL; o = gch(o)->next) {
443 lua_assert(gch(o)->tt == LUA_TUSERDATA); 443 lua_assert(!iswhite(o));
444 lua_assert(isblack(o)); 444 lua_assert(!isdead(g, o) && testbit(o->gch.marked, SEPARATED));
445 lua_assert(testbit(o->gch.marked, SEPARATED)); 445 lua_assert(gch(o)->tt == LUA_TUSERDATA ||
446 gch(o)->tt == LUA_TTABLE);
446 } 447 }
447 /* check 'uvhead' list */ 448 /* check 'uvhead' list */
448 for (uv = g->uvhead.u.l.next; uv != &g->uvhead; uv = uv->u.l.next) { 449 for (uv = g->uvhead.u.l.next; uv != &g->uvhead; uv = uv->u.l.next) {