diff options
-rw-r--r-- | lapi.c | 6 | ||||
-rw-r--r-- | lgc.c | 158 | ||||
-rw-r--r-- | lgc.h | 9 | ||||
-rw-r--r-- | lstate.c | 4 | ||||
-rw-r--r-- | lstate.h | 1 | ||||
-rw-r--r-- | testes/gc.lua | 6 |
6 files changed, 134 insertions, 50 deletions
@@ -1141,22 +1141,21 @@ LUA_API int lua_gc (lua_State *L, int what, ...) { | |||
1141 | break; | 1141 | break; |
1142 | } | 1142 | } |
1143 | case LUA_GCGEN: { | 1143 | case LUA_GCGEN: { |
1144 | int oldmode = g->gckind; | ||
1145 | int minormul = va_arg(argp, int); | 1144 | int minormul = va_arg(argp, int); |
1146 | int majormul = va_arg(argp, int); | 1145 | int majormul = va_arg(argp, int); |
1146 | res = isdecGCmodegen(g) ? LUA_GCGEN : LUA_GCINC; | ||
1147 | if (minormul != 0) | 1147 | if (minormul != 0) |
1148 | g->genminormul = minormul; | 1148 | g->genminormul = minormul; |
1149 | if (majormul != 0) | 1149 | if (majormul != 0) |
1150 | setgcparam(g->genmajormul, majormul); | 1150 | setgcparam(g->genmajormul, majormul); |
1151 | luaC_changemode(L, KGC_GEN); | 1151 | luaC_changemode(L, KGC_GEN); |
1152 | res = (oldmode == KGC_GEN) ? LUA_GCGEN : LUA_GCINC; | ||
1153 | break; | 1152 | break; |
1154 | } | 1153 | } |
1155 | case LUA_GCINC: { | 1154 | case LUA_GCINC: { |
1156 | int oldmode = g->gckind; | ||
1157 | int pause = va_arg(argp, int); | 1155 | int pause = va_arg(argp, int); |
1158 | int stepmul = va_arg(argp, int); | 1156 | int stepmul = va_arg(argp, int); |
1159 | int stepsize = va_arg(argp, int); | 1157 | int stepsize = va_arg(argp, int); |
1158 | res = isdecGCmodegen(g) ? LUA_GCGEN : LUA_GCINC; | ||
1160 | if (pause != 0) | 1159 | if (pause != 0) |
1161 | setgcparam(g->gcpause, pause); | 1160 | setgcparam(g->gcpause, pause); |
1162 | if (stepmul != 0) | 1161 | if (stepmul != 0) |
@@ -1164,7 +1163,6 @@ LUA_API int lua_gc (lua_State *L, int what, ...) { | |||
1164 | if (stepsize != 0) | 1163 | if (stepsize != 0) |
1165 | g->gcstepsize = stepsize; | 1164 | g->gcstepsize = stepsize; |
1166 | luaC_changemode(L, KGC_INC); | 1165 | luaC_changemode(L, KGC_INC); |
1167 | res = (oldmode == KGC_GEN) ? LUA_GCGEN : LUA_GCINC; | ||
1168 | break; | 1166 | break; |
1169 | } | 1167 | } |
1170 | default: res = -1; /* invalid option */ | 1168 | default: res = -1; /* invalid option */ |
@@ -101,6 +101,7 @@ | |||
101 | 101 | ||
102 | static void reallymarkobject (global_State *g, GCObject *o); | 102 | static void reallymarkobject (global_State *g, GCObject *o); |
103 | static lu_mem atomic (lua_State *L); | 103 | static lu_mem atomic (lua_State *L); |
104 | static void entersweep (lua_State *L); | ||
104 | 105 | ||
105 | 106 | ||
106 | /* | 107 | /* |
@@ -1162,15 +1163,7 @@ static void youngcollection (lua_State *L, global_State *g) { | |||
1162 | } | 1163 | } |
1163 | 1164 | ||
1164 | 1165 | ||
1165 | /* | 1166 | static void atomic2gen (lua_State *L, global_State *g) { |
1166 | ** Enter generational mode. Must go until the end of an atomic cycle | ||
1167 | ** to ensure that all threads are in the gray list. Then, turn all | ||
1168 | ** objects into old and finishes the collection. | ||
1169 | */ | ||
1170 | static void entergen (lua_State *L, global_State *g) { | ||
1171 | luaC_runtilstate(L, bitmask(GCSpause)); /* prepare to start a new cycle */ | ||
1172 | luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */ | ||
1173 | atomic(L); | ||
1174 | /* sweep all elements making them old */ | 1167 | /* sweep all elements making them old */ |
1175 | sweep2old(L, &g->allgc); | 1168 | sweep2old(L, &g->allgc); |
1176 | /* everything alive now is old */ | 1169 | /* everything alive now is old */ |
@@ -1183,15 +1176,31 @@ static void entergen (lua_State *L, global_State *g) { | |||
1183 | sweep2old(L, &g->tobefnz); | 1176 | sweep2old(L, &g->tobefnz); |
1184 | 1177 | ||
1185 | g->gckind = KGC_GEN; | 1178 | g->gckind = KGC_GEN; |
1179 | g->lastatomic = 0; | ||
1186 | g->GCestimate = gettotalbytes(g); /* base for memory control */ | 1180 | g->GCestimate = gettotalbytes(g); /* base for memory control */ |
1187 | finishgencycle(L, g); | 1181 | finishgencycle(L, g); |
1188 | } | 1182 | } |
1189 | 1183 | ||
1190 | 1184 | ||
1191 | /* | 1185 | /* |
1186 | ** Enter generational mode. Must go until the end of an atomic cycle | ||
1187 | ** to ensure that all threads and weak tables are in the gray lists. | ||
1188 | ** Then, turn all objects into old and finishes the collection. | ||
1189 | */ | ||
1190 | static lu_mem entergen (lua_State *L, global_State *g) { | ||
1191 | lu_mem numobjs; | ||
1192 | luaC_runtilstate(L, bitmask(GCSpause)); /* prepare to start a new cycle */ | ||
1193 | luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */ | ||
1194 | numobjs = atomic(L); /* propagates all and then do the atomic stuff */ | ||
1195 | atomic2gen(L, g); | ||
1196 | return numobjs; | ||
1197 | } | ||
1198 | |||
1199 | |||
1200 | /* | ||
1192 | ** Enter incremental mode. Turn all objects white, make all | 1201 | ** Enter incremental mode. Turn all objects white, make all |
1193 | ** intermediate lists point to NULL (to avoid invalid pointers), | 1202 | ** intermediate lists point to NULL (to avoid invalid pointers), |
1194 | ** and go to pause state. | 1203 | ** and go to the pause state. |
1195 | */ | 1204 | */ |
1196 | static void enterinc (global_State *g) { | 1205 | static void enterinc (global_State *g) { |
1197 | whitelist(g, g->allgc); | 1206 | whitelist(g, g->allgc); |
@@ -1201,6 +1210,7 @@ static void enterinc (global_State *g) { | |||
1201 | g->finobjrold = g->finobjold = g->finobjsur = NULL; | 1210 | g->finobjrold = g->finobjold = g->finobjsur = NULL; |
1202 | g->gcstate = GCSpause; | 1211 | g->gcstate = GCSpause; |
1203 | g->gckind = KGC_INC; | 1212 | g->gckind = KGC_INC; |
1213 | g->lastatomic = 0; | ||
1204 | } | 1214 | } |
1205 | 1215 | ||
1206 | 1216 | ||
@@ -1215,54 +1225,114 @@ void luaC_changemode (lua_State *L, int newmode) { | |||
1215 | else | 1225 | else |
1216 | enterinc(g); /* entering incremental mode */ | 1226 | enterinc(g); /* entering incremental mode */ |
1217 | } | 1227 | } |
1228 | g->lastatomic = 0; | ||
1218 | } | 1229 | } |
1219 | 1230 | ||
1220 | 1231 | ||
1221 | /* | 1232 | /* |
1222 | ** Does a full collection in generational mode. | 1233 | ** Does a full collection in generational mode. |
1223 | */ | 1234 | */ |
1224 | static void fullgen (lua_State *L, global_State *g) { | 1235 | static lu_mem fullgen (lua_State *L, global_State *g) { |
1225 | enterinc(g); | 1236 | enterinc(g); |
1226 | entergen(L, g); | 1237 | return entergen(L, g); |
1238 | } | ||
1239 | |||
1240 | |||
1241 | /* | ||
1242 | ** Set debt for the next minor collection, which will happen when | ||
1243 | ** memory grows 'genminormul'%. | ||
1244 | */ | ||
1245 | static void setminordebt (global_State *g) { | ||
1246 | luaE_setdebt(g, -(cast(l_mem, (gettotalbytes(g) / 100)) * g->genminormul)); | ||
1227 | } | 1247 | } |
1228 | 1248 | ||
1229 | 1249 | ||
1230 | /* | 1250 | /* |
1231 | ** Does a generational "step". If memory grows 'genmajormul'% larger | 1251 | ** Does a major collection after last collection was a "bad collection". |
1232 | ** than last major collection (kept in 'g->GCestimate'), does a major | 1252 | ** |
1233 | ** collection. Otherwise, does a minor collection and set debt to make | 1253 | ** When the program is building a big struture, it allocates lots of |
1234 | ** another collection when memory grows 'genminormul'% larger. | 1254 | ** memory but generates very little garbage. In those scenarios, |
1235 | ** When it does a major collection, it then checks whether it could | 1255 | ** the generational mode just wastes time doing small collections, and |
1236 | ** reclaim at least ?? memory. If not, it sets a long pause for the | 1256 | ** major collections are frequently what we call a "bad collection", a |
1237 | ** next collection. (Therefore, the next collection will be a major | 1257 | ** collection that frees too few objects. To avoid the cost of switching |
1238 | ** one, too.) | 1258 | ** between generational mode and the incremental mode needed for full |
1259 | ** (major) collections, the collector tries to stay in incremental mode | ||
1260 | ** after a bad collection, and to switch back to generational mode only | ||
1261 | ** after a "good" collection (one that traverses less than 9/8 objects | ||
1262 | ** of the previous one). | ||
1263 | ** The collector must choose whether to stay in incremental mode or to | ||
1264 | ** switch back to generational mode before sweeping. At this point, it | ||
1265 | ** does not know the real memory in use, so it cannot use memory to | ||
1266 | ** decide whether to return to generational mode. Instead, it uses the | ||
1267 | ** number of objects traversed (returned by 'atomic') as a proxy. The | ||
1268 | ** field 'g->lastatomic' keeps this count from the last collection. | ||
1269 | ** ('g->lastatomic != 0' also means that the last collection was bad.) | ||
1270 | */ | ||
1271 | static void stepgenfull (lua_State *L, global_State *g) { | ||
1272 | lu_mem newatomic; /* count of traversed objects */ | ||
1273 | lu_mem lastatomic = g->lastatomic; /* count from last collection */ | ||
1274 | if (g->gckind == KGC_GEN) /* still in generational mode? */ | ||
1275 | enterinc(g); /* enter incremental mode */ | ||
1276 | luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */ | ||
1277 | newatomic = atomic(L); /* mark everybody */ | ||
1278 | if (newatomic < lastatomic + (lastatomic >> 3)) { /* good collection? */ | ||
1279 | atomic2gen(L, g); /* return to generational mode */ | ||
1280 | setminordebt(g); | ||
1281 | } | ||
1282 | else { /* another bad collection; stay in incremental mode */ | ||
1283 | g->GCestimate = gettotalbytes(g); /* first estimate */; | ||
1284 | entersweep(L); | ||
1285 | luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */ | ||
1286 | setpause(g); | ||
1287 | g->lastatomic = newatomic; | ||
1288 | } | ||
1289 | } | ||
1290 | |||
1291 | |||
1292 | /* | ||
1293 | ** Does a generational "step". | ||
1294 | ** Usually, this means doing a minor collection and setting the debt to | ||
1295 | ** make another collection when memory grows 'genminormul'% larger. | ||
1296 | ** | ||
1297 | ** However, there are exceptions. If memory grows 'genmajormul'% | ||
1298 | ** larger than it was at the end of the last major collection (kept | ||
1299 | ** in 'g->GCestimate'), the function does a major collection. At the | ||
1300 | ** end, it checks whether the major collection was able to free a | ||
1301 | ** decent amount of memory (at least half the growth in memory since | ||
1302 | ** previous major collection). If so, the collector keeps its state, | ||
1303 | ** and the next collection will probably be minor again. Otherwise, | ||
1304 | ** we have what we call a "bad collection". In that case, set the field | ||
1305 | ** 'g->lastatomic' to signal that fact, so that the next collection will | ||
1306 | ** go to 'stepgenfull'. | ||
1307 | ** | ||
1239 | ** 'GCdebt <= 0' means an explicit call to GC step with "size" zero; | 1308 | ** 'GCdebt <= 0' means an explicit call to GC step with "size" zero; |
1240 | ** in that case, always do a minor collection. | 1309 | ** in that case, do a minor collection. |
1241 | */ | 1310 | */ |
1242 | static void genstep (lua_State *L, global_State *g) { | 1311 | static void genstep (lua_State *L, global_State *g) { |
1243 | lu_mem majorbase = g->GCestimate; /* memory after last major collection */ | 1312 | if (g->lastatomic != 0) /* last collection was a bad one? */ |
1244 | lu_mem majorinc = (majorbase / 100) * getgcparam(g->genmajormul); | 1313 | stepgenfull(L, g); /* do a full step */ |
1245 | lu_mem memnew = gettotalbytes(g); | 1314 | else { |
1246 | if (g->GCdebt > 0 && memnew > majorbase + majorinc) { | 1315 | lu_mem majorbase = g->GCestimate; /* memory after last major collection */ |
1247 | fullgen(L, g); | 1316 | lu_mem majorinc = (majorbase / 100) * getgcparam(g->genmajormul); |
1248 | memnew = gettotalbytes(g); | 1317 | if (g->GCdebt > 0 && gettotalbytes(g) > majorbase + majorinc) { |
1249 | if (memnew < majorbase + (majorinc / 2)) { | 1318 | lu_mem numobjs = fullgen(L, g); /* do a major collection */ |
1250 | /* collected at least half of memory growth since last major | 1319 | if (gettotalbytes(g) < majorbase + (majorinc / 2)) { |
1251 | collection; go back to minor collections */ | 1320 | /* collected at least half of memory growth since last major |
1252 | luaE_setdebt(g, -(cast(l_mem, (memnew / 100)) * g->genminormul)); | 1321 | collection; keep doing minor collections */ |
1322 | setminordebt(g); | ||
1323 | } | ||
1324 | else { /* bad collection */ | ||
1325 | g->lastatomic = numobjs; /* signal that last collection was bad */ | ||
1326 | setpause(g); /* do a long wait for next (major) collection */ | ||
1327 | } | ||
1253 | } | 1328 | } |
1254 | else { | 1329 | else { /* regular case; do a minor collection */ |
1255 | /* memory seems to be growing; do a long wait for next (major) | 1330 | youngcollection(L, g); |
1256 | collection */ | 1331 | setminordebt(g); |
1257 | setpause(g); | 1332 | g->GCestimate = majorbase; /* preserve base value */ |
1258 | } | 1333 | } |
1259 | } | 1334 | } |
1260 | else { | 1335 | lua_assert(isdecGCmodegen(g)); |
1261 | youngcollection(L, g); | ||
1262 | memnew = gettotalbytes(g); | ||
1263 | luaE_setdebt(g, -(cast(l_mem, (memnew / 100)) * g->genminormul)); | ||
1264 | g->GCestimate = majorbase; /* preserve base value */ | ||
1265 | } | ||
1266 | } | 1336 | } |
1267 | 1337 | ||
1268 | /* }====================================================== */ | 1338 | /* }====================================================== */ |
@@ -1493,10 +1563,10 @@ static void incstep (lua_State *L, global_State *g) { | |||
1493 | void luaC_step (lua_State *L) { | 1563 | void luaC_step (lua_State *L) { |
1494 | global_State *g = G(L); | 1564 | global_State *g = G(L); |
1495 | if (g->gcrunning) { /* running? */ | 1565 | if (g->gcrunning) { /* running? */ |
1496 | if (g->gckind == KGC_INC) | 1566 | if(isdecGCmodegen(g)) |
1497 | incstep(L, g); | ||
1498 | else | ||
1499 | genstep(L, g); | 1567 | genstep(L, g); |
1568 | else | ||
1569 | incstep(L, g); | ||
1500 | } | 1570 | } |
1501 | } | 1571 | } |
1502 | 1572 | ||
@@ -123,7 +123,7 @@ | |||
123 | #define LUAI_GENMINORMUL 20 | 123 | #define LUAI_GENMINORMUL 20 |
124 | 124 | ||
125 | /* wait memory to double before starting new cycle */ | 125 | /* wait memory to double before starting new cycle */ |
126 | #define LUAI_GCPAUSE 200 /* 200% */ | 126 | #define LUAI_GCPAUSE 200 |
127 | 127 | ||
128 | /* | 128 | /* |
129 | ** some gc parameters are stored divided by 4 to allow a maximum value | 129 | ** some gc parameters are stored divided by 4 to allow a maximum value |
@@ -139,6 +139,13 @@ | |||
139 | 139 | ||
140 | 140 | ||
141 | /* | 141 | /* |
142 | ** Check whether the declared GC mode is generational. While in | ||
143 | ** generational mode, the collector can go temporarily to incremental | ||
144 | ** mode to improve performance. This is signaled by 'g->lastatomic != 0'. | ||
145 | */ | ||
146 | #define isdecGCmodegen(g) (g->gckind == KGC_GEN || g->lastatomic != 0) | ||
147 | |||
148 | /* | ||
142 | ** Does one step of collection when debt becomes positive. 'pre'/'pos' | 149 | ** Does one step of collection when debt becomes positive. 'pre'/'pos' |
143 | ** allows some adjustments to be done only when needed. macro | 150 | ** allows some adjustments to be done only when needed. macro |
144 | ** 'condchangemem' is used only for heavy tests (forcing a full | 151 | ** 'condchangemem' is used only for heavy tests (forcing a full |
@@ -233,7 +233,8 @@ static void init_registry (lua_State *L, global_State *g) { | |||
233 | 233 | ||
234 | /* | 234 | /* |
235 | ** open parts of the state that may cause memory-allocation errors. | 235 | ** open parts of the state that may cause memory-allocation errors. |
236 | ** ('ttisnil(&g->nilvalue)'' flags that the state was completely build) | 236 | ** ('g->nilvalue' being a nil value flags that the state was completely |
237 | ** build.) | ||
237 | */ | 238 | */ |
238 | static void f_luaopen (lua_State *L, void *ud) { | 239 | static void f_luaopen (lua_State *L, void *ud) { |
239 | global_State *g = G(L); | 240 | global_State *g = G(L); |
@@ -386,6 +387,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { | |||
386 | g->twups = NULL; | 387 | g->twups = NULL; |
387 | g->totalbytes = sizeof(LG); | 388 | g->totalbytes = sizeof(LG); |
388 | g->GCdebt = 0; | 389 | g->GCdebt = 0; |
390 | g->lastatomic = 0; | ||
389 | setivalue(&g->nilvalue, 0); /* to signal that state is not yet built */ | 391 | setivalue(&g->nilvalue, 0); /* to signal that state is not yet built */ |
390 | setgcparam(g->gcpause, LUAI_GCPAUSE); | 392 | setgcparam(g->gcpause, LUAI_GCPAUSE); |
391 | setgcparam(g->gcstepmul, LUAI_GCMUL); | 393 | setgcparam(g->gcstepmul, LUAI_GCMUL); |
@@ -193,6 +193,7 @@ typedef struct global_State { | |||
193 | l_mem totalbytes; /* number of bytes currently allocated - GCdebt */ | 193 | l_mem totalbytes; /* number of bytes currently allocated - GCdebt */ |
194 | l_mem GCdebt; /* bytes allocated not yet compensated by the collector */ | 194 | l_mem GCdebt; /* bytes allocated not yet compensated by the collector */ |
195 | lu_mem GCestimate; /* an estimate of the non-garbage memory in use */ | 195 | lu_mem GCestimate; /* an estimate of the non-garbage memory in use */ |
196 | lu_mem lastatomic; /* see function 'genstep' in file 'lgc.c' */ | ||
196 | stringtable strt; /* hash table for strings */ | 197 | stringtable strt; /* hash table for strings */ |
197 | TValue l_registry; | 198 | TValue l_registry; |
198 | TValue nilvalue; /* a nil value */ | 199 | TValue nilvalue; /* a nil value */ |
diff --git a/testes/gc.lua b/testes/gc.lua index 84e8ffb7..05bf564e 100644 --- a/testes/gc.lua +++ b/testes/gc.lua | |||
@@ -11,6 +11,12 @@ collectgarbage() | |||
11 | 11 | ||
12 | local oldmode = collectgarbage("incremental") | 12 | local oldmode = collectgarbage("incremental") |
13 | 13 | ||
14 | -- changing modes should return previous mode | ||
15 | assert(collectgarbage("generational") == "incremental") | ||
16 | assert(collectgarbage("generational") == "generational") | ||
17 | assert(collectgarbage("incremental") == "generational") | ||
18 | assert(collectgarbage("incremental") == "incremental") | ||
19 | |||
14 | 20 | ||
15 | local function gcinfo () | 21 | local function gcinfo () |
16 | return collectgarbage"count" * 1024 | 22 | return collectgarbage"count" * 1024 |