diff options
-rw-r--r-- | lgc.c | 59 | ||||
-rw-r--r-- | lstate.c | 2 | ||||
-rw-r--r-- | lstate.h | 18 | ||||
-rw-r--r-- | testes/gengc.lua | 16 |
4 files changed, 76 insertions, 19 deletions
@@ -859,6 +859,8 @@ static GCObject *udata2finalize (global_State *g) { | |||
859 | resetbit(o->marked, FINALIZEDBIT); /* object is "normal" again */ | 859 | resetbit(o->marked, FINALIZEDBIT); /* object is "normal" again */ |
860 | if (issweepphase(g)) | 860 | if (issweepphase(g)) |
861 | makewhite(g, o); /* "sweep" object */ | 861 | makewhite(g, o); /* "sweep" object */ |
862 | else if (getage(o) == G_OLD1) | ||
863 | g->firstold1 = o; /* it is the first OLD1 object in the list */ | ||
862 | return o; | 864 | return o; |
863 | } | 865 | } |
864 | 866 | ||
@@ -957,6 +959,27 @@ static void separatetobefnz (global_State *g, int all) { | |||
957 | 959 | ||
958 | 960 | ||
959 | /* | 961 | /* |
962 | ** If pointer 'p' points to 'o', move it to the next element. | ||
963 | */ | ||
964 | static void checkpointer (GCObject **p, GCObject *o) { | ||
965 | if (o == *p) | ||
966 | *p = o->next; | ||
967 | } | ||
968 | |||
969 | |||
970 | /* | ||
971 | ** Correct pointers to objects inside 'allgc' list when | ||
972 | ** object 'o' is being removed from the list. | ||
973 | */ | ||
974 | static void correctpointers (global_State *g, GCObject *o) { | ||
975 | checkpointer(&g->survival, o); | ||
976 | checkpointer(&g->old1, o); | ||
977 | checkpointer(&g->reallyold, o); | ||
978 | checkpointer(&g->firstold1, o); | ||
979 | } | ||
980 | |||
981 | |||
982 | /* | ||
960 | ** if object 'o' has a finalizer, remove it from 'allgc' list (must | 983 | ** if object 'o' has a finalizer, remove it from 'allgc' list (must |
961 | ** search the list to find it) and link it in 'finobj' list. | 984 | ** search the list to find it) and link it in 'finobj' list. |
962 | */ | 985 | */ |
@@ -972,14 +995,8 @@ void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) { | |||
972 | if (g->sweepgc == &o->next) /* should not remove 'sweepgc' object */ | 995 | if (g->sweepgc == &o->next) /* should not remove 'sweepgc' object */ |
973 | g->sweepgc = sweeptolive(L, g->sweepgc); /* change 'sweepgc' */ | 996 | g->sweepgc = sweeptolive(L, g->sweepgc); /* change 'sweepgc' */ |
974 | } | 997 | } |
975 | else { /* correct pointers into 'allgc' list, if needed */ | 998 | else |
976 | if (o == g->survival) | 999 | correctpointers(g, o); |
977 | g->survival = o->next; | ||
978 | if (o == g->old1) | ||
979 | g->old1 = o->next; | ||
980 | if (o == g->reallyold) | ||
981 | g->reallyold = o->next; | ||
982 | } | ||
983 | /* search for pointer pointing to 'o' */ | 1000 | /* search for pointer pointing to 'o' */ |
984 | for (p = &g->allgc; *p != o; p = &(*p)->next) { /* empty */ } | 1001 | for (p = &g->allgc; *p != o; p = &(*p)->next) { /* empty */ } |
985 | *p = o->next; /* remove 'o' from 'allgc' list */ | 1002 | *p = o->next; /* remove 'o' from 'allgc' list */ |
@@ -1033,7 +1050,7 @@ static void sweep2old (lua_State *L, GCObject **p) { | |||
1033 | ** will also remove objects turned white here from any gray list. | 1050 | ** will also remove objects turned white here from any gray list. |
1034 | */ | 1051 | */ |
1035 | static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p, | 1052 | static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p, |
1036 | GCObject *limit) { | 1053 | GCObject *limit, GCObject **pfirstold1) { |
1037 | static const lu_byte nextage[] = { | 1054 | static const lu_byte nextage[] = { |
1038 | G_SURVIVAL, /* from G_NEW */ | 1055 | G_SURVIVAL, /* from G_NEW */ |
1039 | G_OLD1, /* from G_SURVIVAL */ | 1056 | G_OLD1, /* from G_SURVIVAL */ |
@@ -1056,8 +1073,11 @@ static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p, | |||
1056 | int marked = curr->marked & maskgcbits; /* erase GC bits */ | 1073 | int marked = curr->marked & maskgcbits; /* erase GC bits */ |
1057 | curr->marked = cast_byte(marked | G_SURVIVAL | white); | 1074 | curr->marked = cast_byte(marked | G_SURVIVAL | white); |
1058 | } | 1075 | } |
1059 | else /* all other objects will be old, and so keep their color */ | 1076 | else { /* all other objects will be old, and so keep their color */ |
1060 | setage(curr, nextage[getage(curr)]); | 1077 | setage(curr, nextage[getage(curr)]); |
1078 | if (getage(curr) == G_OLD1 && *pfirstold1 == NULL) | ||
1079 | *pfirstold1 = curr; /* first OLD1 object in the list */ | ||
1080 | } | ||
1061 | p = &curr->next; /* go to next element */ | 1081 | p = &curr->next; /* go to next element */ |
1062 | } | 1082 | } |
1063 | } | 1083 | } |
@@ -1169,30 +1189,34 @@ static void finishgencycle (lua_State *L, global_State *g) { | |||
1169 | */ | 1189 | */ |
1170 | static void youngcollection (lua_State *L, global_State *g) { | 1190 | static void youngcollection (lua_State *L, global_State *g) { |
1171 | GCObject **psurvival; /* to point to first non-dead survival object */ | 1191 | GCObject **psurvival; /* to point to first non-dead survival object */ |
1192 | GCObject *dummy; /* dummy out parameter to 'sweepgen' */ | ||
1172 | lua_assert(g->gcstate == GCSpropagate); | 1193 | lua_assert(g->gcstate == GCSpropagate); |
1173 | markold(g, g->allgc, g->reallyold); | 1194 | if (g->firstold1) { /* are there OLD1 objects? */ |
1195 | markold(g, g->firstold1, g->reallyold); /* mark them */ | ||
1196 | g->firstold1 = NULL; /* no more OLD1 objects (for now) */ | ||
1197 | } | ||
1174 | markold(g, g->finobj, g->finobjrold); | 1198 | markold(g, g->finobj, g->finobjrold); |
1175 | atomic(L); | 1199 | atomic(L); |
1176 | 1200 | ||
1177 | /* sweep nursery and get a pointer to its last live element */ | 1201 | /* sweep nursery and get a pointer to its last live element */ |
1178 | g->gcstate = GCSswpallgc; | 1202 | g->gcstate = GCSswpallgc; |
1179 | psurvival = sweepgen(L, g, &g->allgc, g->survival); | 1203 | psurvival = sweepgen(L, g, &g->allgc, g->survival, &g->firstold1); |
1180 | /* sweep 'survival' */ | 1204 | /* sweep 'survival' */ |
1181 | sweepgen(L, g, psurvival, g->old1); | 1205 | sweepgen(L, g, psurvival, g->old1, &g->firstold1); |
1182 | g->reallyold = g->old1; | 1206 | g->reallyold = g->old1; |
1183 | g->old1 = *psurvival; /* 'survival' survivals are old now */ | 1207 | g->old1 = *psurvival; /* 'survival' survivals are old now */ |
1184 | g->survival = g->allgc; /* all news are survivals */ | 1208 | g->survival = g->allgc; /* all news are survivals */ |
1185 | 1209 | ||
1186 | /* repeat for 'finobj' lists */ | 1210 | /* repeat for 'finobj' lists */ |
1187 | psurvival = sweepgen(L, g, &g->finobj, g->finobjsur); | 1211 | dummy = NULL; /* no 'firstold1' optimization for 'finobj' lists */ |
1212 | psurvival = sweepgen(L, g, &g->finobj, g->finobjsur, &dummy); | ||
1188 | /* sweep 'survival' */ | 1213 | /* sweep 'survival' */ |
1189 | sweepgen(L, g, psurvival, g->finobjold1); | 1214 | sweepgen(L, g, psurvival, g->finobjold1, &dummy); |
1190 | g->finobjrold = g->finobjold1; | 1215 | g->finobjrold = g->finobjold1; |
1191 | g->finobjold1 = *psurvival; /* 'survival' survivals are old now */ | 1216 | g->finobjold1 = *psurvival; /* 'survival' survivals are old now */ |
1192 | g->finobjsur = g->finobj; /* all news are survivals */ | 1217 | g->finobjsur = g->finobj; /* all news are survivals */ |
1193 | 1218 | ||
1194 | sweepgen(L, g, &g->tobefnz, NULL); | 1219 | sweepgen(L, g, &g->tobefnz, NULL, &dummy); |
1195 | |||
1196 | finishgencycle(L, g); | 1220 | finishgencycle(L, g); |
1197 | } | 1221 | } |
1198 | 1222 | ||
@@ -1203,6 +1227,7 @@ static void atomic2gen (lua_State *L, global_State *g) { | |||
1203 | sweep2old(L, &g->allgc); | 1227 | sweep2old(L, &g->allgc); |
1204 | /* everything alive now is old */ | 1228 | /* everything alive now is old */ |
1205 | g->reallyold = g->old1 = g->survival = g->allgc; | 1229 | g->reallyold = g->old1 = g->survival = g->allgc; |
1230 | g->firstold1 = NULL; /* there are no OLD1 objects anywhere */ | ||
1206 | 1231 | ||
1207 | /* repeat for 'finobj' lists */ | 1232 | /* repeat for 'finobj' lists */ |
1208 | sweep2old(L, &g->finobj); | 1233 | sweep2old(L, &g->finobj); |
@@ -413,7 +413,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { | |||
413 | g->gckind = KGC_INC; | 413 | g->gckind = KGC_INC; |
414 | g->gcemergency = 0; | 414 | g->gcemergency = 0; |
415 | g->finobj = g->tobefnz = g->fixedgc = NULL; | 415 | g->finobj = g->tobefnz = g->fixedgc = NULL; |
416 | g->survival = g->old1 = g->reallyold = NULL; | 416 | g->firstold1 = g->survival = g->old1 = g->reallyold = NULL; |
417 | g->finobjsur = g->finobjold1 = g->finobjrold = NULL; | 417 | g->finobjsur = g->finobjold1 = g->finobjrold = NULL; |
418 | g->sweepgc = NULL; | 418 | g->sweepgc = NULL; |
419 | g->gray = g->grayagain = NULL; | 419 | g->gray = g->grayagain = NULL; |
@@ -46,6 +46,15 @@ | |||
46 | ** lists. Moreover, barriers can age young objects in young lists as | 46 | ** lists. Moreover, barriers can age young objects in young lists as |
47 | ** OLD0, which then become OLD1. However, a list never contains | 47 | ** OLD0, which then become OLD1. However, a list never contains |
48 | ** elements younger than their main ages. | 48 | ** elements younger than their main ages. |
49 | ** | ||
50 | ** The generational collector also uses a pointer 'firstold1', which | ||
51 | ** points to the first OLD1 object in the list. It is used to optimize | ||
52 | ** 'markold'. (Potentially OLD1 objects can be anywhere between 'allgc' | ||
53 | ** and 'reallyold', but often the list has no OLD1 objects or they are | ||
54 | ** after 'old1'.) Note the difference between it and 'old1': | ||
55 | ** 'firstold1': no OLD1 objects before this point; there can be all | ||
56 | ** ages after it. | ||
57 | ** 'old1': no objects younger than OLD1 after this point. | ||
49 | */ | 58 | */ |
50 | 59 | ||
51 | /* | 60 | /* |
@@ -54,7 +63,7 @@ | |||
54 | ** can become gray have such a field. The field is not the same | 63 | ** can become gray have such a field. The field is not the same |
55 | ** in all objects, but it always has this name.) Any gray object | 64 | ** in all objects, but it always has this name.) Any gray object |
56 | ** must belong to one of these lists, and all objects in these lists | 65 | ** must belong to one of these lists, and all objects in these lists |
57 | ** must be gray: | 66 | ** must be gray (with one exception explained below): |
58 | ** | 67 | ** |
59 | ** 'gray': regular gray objects, still waiting to be visited. | 68 | ** 'gray': regular gray objects, still waiting to be visited. |
60 | ** 'grayagain': objects that must be revisited at the atomic phase. | 69 | ** 'grayagain': objects that must be revisited at the atomic phase. |
@@ -65,6 +74,12 @@ | |||
65 | ** 'weak': tables with weak values to be cleared; | 74 | ** 'weak': tables with weak values to be cleared; |
66 | ** 'ephemeron': ephemeron tables with white->white entries; | 75 | ** 'ephemeron': ephemeron tables with white->white entries; |
67 | ** 'allweak': tables with weak keys and/or weak values to be cleared. | 76 | ** 'allweak': tables with weak keys and/or weak values to be cleared. |
77 | ** | ||
78 | ** The exception to that "gray rule" is the TOUCHED2 objects in | ||
79 | ** generational mode. Those objects stay in a gray list (because they | ||
80 | ** must be visited again at the end of the cycle), but they are marked | ||
81 | ** black (because assignments to them must activate barriers, to move | ||
82 | ** them back to TOUCHED1). | ||
68 | */ | 83 | */ |
69 | 84 | ||
70 | 85 | ||
@@ -266,6 +281,7 @@ typedef struct global_State { | |||
266 | GCObject *survival; /* start of objects that survived one GC cycle */ | 281 | GCObject *survival; /* start of objects that survived one GC cycle */ |
267 | GCObject *old1; /* start of old1 objects */ | 282 | GCObject *old1; /* start of old1 objects */ |
268 | GCObject *reallyold; /* objects more than one cycle old ("really old") */ | 283 | GCObject *reallyold; /* objects more than one cycle old ("really old") */ |
284 | GCObject *firstold1; /* first OLD1 object in the list (if any) */ | ||
269 | GCObject *finobjsur; /* list of survival objects with finalizers */ | 285 | GCObject *finobjsur; /* list of survival objects with finalizers */ |
270 | GCObject *finobjold1; /* list of old1 objects with finalizers */ | 286 | GCObject *finobjold1; /* list of old1 objects with finalizers */ |
271 | GCObject *finobjrold; /* list of really old objects with finalizers */ | 287 | GCObject *finobjrold; /* list of really old objects with finalizers */ |
diff --git a/testes/gengc.lua b/testes/gengc.lua index 7a7dabdd..93b5afd7 100644 --- a/testes/gengc.lua +++ b/testes/gengc.lua | |||
@@ -37,6 +37,22 @@ do | |||
37 | end | 37 | end |
38 | 38 | ||
39 | 39 | ||
40 | do | ||
41 | -- ensure that 'firstold1' is corrected when object is removed from | ||
42 | -- the 'allgc' list | ||
43 | local function foo () end | ||
44 | local old = {10} | ||
45 | collectgarbage() -- make 'old' old | ||
46 | assert(not T or T.gcage(old) == "old") | ||
47 | setmetatable(old, {}) -- new table becomes OLD0 (barrier) | ||
48 | assert(not T or T.gcage(getmetatable(old)) == "old0") | ||
49 | collectgarbage("step", 0) -- new table becomes OLD1 and firstold1 | ||
50 | assert(not T or T.gcage(getmetatable(old)) == "old1") | ||
51 | setmetatable(getmetatable(old), {__gc = foo}) -- get it out of allgc list | ||
52 | collectgarbage("step", 0) -- should not seg. fault | ||
53 | end | ||
54 | |||
55 | |||
40 | do -- bug in 5.4.0 | 56 | do -- bug in 5.4.0 |
41 | -- When an object aged OLD1 is finalized, it is moved from the list | 57 | -- When an object aged OLD1 is finalized, it is moved from the list |
42 | -- 'finobj' to the *beginning* of the list 'allgc', but that part of the | 58 | -- 'finobj' to the *beginning* of the list 'allgc', but that part of the |