aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lapi.c6
-rw-r--r--lgc.c158
-rw-r--r--lgc.h9
-rw-r--r--lstate.c4
-rw-r--r--lstate.h1
-rw-r--r--testes/gc.lua6
6 files changed, 134 insertions, 50 deletions
diff --git a/lapi.c b/lapi.c
index 8ff7bfbd..4026497e 100644
--- a/lapi.c
+++ b/lapi.c
@@ -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 */
diff --git a/lgc.c b/lgc.c
index 0c3386e7..bf0460d5 100644
--- a/lgc.c
+++ b/lgc.c
@@ -101,6 +101,7 @@
101 101
102static void reallymarkobject (global_State *g, GCObject *o); 102static void reallymarkobject (global_State *g, GCObject *o);
103static lu_mem atomic (lua_State *L); 103static lu_mem atomic (lua_State *L);
104static 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/* 1166static 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*/
1170static 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*/
1190static 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*/
1196static void enterinc (global_State *g) { 1205static 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*/
1224static void fullgen (lua_State *L, global_State *g) { 1235static 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*/
1245static 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*/
1271static 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*/
1242static void genstep (lua_State *L, global_State *g) { 1311static 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) {
1493void luaC_step (lua_State *L) { 1563void 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
diff --git a/lgc.h b/lgc.h
index 6b1c2861..9ba7ecb0 100644
--- a/lgc.h
+++ b/lgc.h
@@ -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
diff --git a/lstate.c b/lstate.c
index 7f6475a8..6c35ea1a 100644
--- a/lstate.c
+++ b/lstate.c
@@ -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*/
238static void f_luaopen (lua_State *L, void *ud) { 239static 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);
diff --git a/lstate.h b/lstate.h
index 05a74dda..11bf18fd 100644
--- a/lstate.h
+++ b/lstate.h
@@ -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
12local oldmode = collectgarbage("incremental") 12local oldmode = collectgarbage("incremental")
13 13
14-- changing modes should return previous mode
15assert(collectgarbage("generational") == "incremental")
16assert(collectgarbage("generational") == "generational")
17assert(collectgarbage("incremental") == "generational")
18assert(collectgarbage("incremental") == "incremental")
19
14 20
15local function gcinfo () 21local function gcinfo ()
16 return collectgarbage"count" * 1024 22 return collectgarbage"count" * 1024