diff options
Diffstat (limited to 'src/lua/lgc.c')
-rw-r--r-- | src/lua/lgc.c | 1616 |
1 files changed, 1616 insertions, 0 deletions
diff --git a/src/lua/lgc.c b/src/lua/lgc.c new file mode 100644 index 0000000..f26c921 --- /dev/null +++ b/src/lua/lgc.c | |||
@@ -0,0 +1,1616 @@ | |||
1 | /* | ||
2 | ** $Id: lgc.c $ | ||
3 | ** Garbage Collector | ||
4 | ** See Copyright Notice in lua.h | ||
5 | */ | ||
6 | |||
7 | #define lgc_c | ||
8 | #define LUA_CORE | ||
9 | |||
10 | #include "lprefix.h" | ||
11 | |||
12 | #include <stdio.h> | ||
13 | #include <string.h> | ||
14 | |||
15 | |||
16 | #include "lua.h" | ||
17 | |||
18 | #include "ldebug.h" | ||
19 | #include "ldo.h" | ||
20 | #include "lfunc.h" | ||
21 | #include "lgc.h" | ||
22 | #include "lmem.h" | ||
23 | #include "lobject.h" | ||
24 | #include "lstate.h" | ||
25 | #include "lstring.h" | ||
26 | #include "ltable.h" | ||
27 | #include "ltm.h" | ||
28 | |||
29 | |||
30 | /* | ||
31 | ** Maximum number of elements to sweep in each single step. | ||
32 | ** (Large enough to dissipate fixed overheads but small enough | ||
33 | ** to allow small steps for the collector.) | ||
34 | */ | ||
35 | #define GCSWEEPMAX 100 | ||
36 | |||
37 | /* | ||
38 | ** Maximum number of finalizers to call in each single step. | ||
39 | */ | ||
40 | #define GCFINMAX 10 | ||
41 | |||
42 | |||
43 | /* | ||
44 | ** Cost of calling one finalizer. | ||
45 | */ | ||
46 | #define GCFINALIZECOST 50 | ||
47 | |||
48 | |||
49 | /* | ||
50 | ** The equivalent, in bytes, of one unit of "work" (visiting a slot, | ||
51 | ** sweeping an object, etc.) | ||
52 | */ | ||
53 | #define WORK2MEM sizeof(TValue) | ||
54 | |||
55 | |||
56 | /* | ||
57 | ** macro to adjust 'pause': 'pause' is actually used like | ||
58 | ** 'pause / PAUSEADJ' (value chosen by tests) | ||
59 | */ | ||
60 | #define PAUSEADJ 100 | ||
61 | |||
62 | |||
63 | /* mask to erase all color bits (plus gen. related stuff) */ | ||
64 | #define maskcolors (~(bitmask(BLACKBIT) | WHITEBITS | AGEBITS)) | ||
65 | |||
66 | |||
67 | /* macro to erase all color bits then sets only the current white bit */ | ||
68 | #define makewhite(g,x) \ | ||
69 | (x->marked = cast_byte((x->marked & maskcolors) | luaC_white(g))) | ||
70 | |||
71 | #define white2gray(x) resetbits(x->marked, WHITEBITS) | ||
72 | #define black2gray(x) resetbit(x->marked, BLACKBIT) | ||
73 | |||
74 | |||
75 | #define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x))) | ||
76 | |||
77 | #define keyiswhite(n) (keyiscollectable(n) && iswhite(gckey(n))) | ||
78 | |||
79 | |||
80 | #define checkconsistency(obj) \ | ||
81 | lua_longassert(!iscollectable(obj) || righttt(obj)) | ||
82 | |||
83 | /* | ||
84 | ** Protected access to objects in values | ||
85 | */ | ||
86 | #define gcvalueN(o) (iscollectable(o) ? gcvalue(o) : NULL) | ||
87 | |||
88 | |||
89 | #define markvalue(g,o) { checkconsistency(o); \ | ||
90 | if (valiswhite(o)) reallymarkobject(g,gcvalue(o)); } | ||
91 | |||
92 | #define markkey(g, n) { if keyiswhite(n) reallymarkobject(g,gckey(n)); } | ||
93 | |||
94 | #define markobject(g,t) { if (iswhite(t)) reallymarkobject(g, obj2gco(t)); } | ||
95 | |||
96 | /* | ||
97 | ** mark an object that can be NULL (either because it is really optional, | ||
98 | ** or it was stripped as debug info, or inside an uncompleted structure) | ||
99 | */ | ||
100 | #define markobjectN(g,t) { if (t) markobject(g,t); } | ||
101 | |||
102 | static void reallymarkobject (global_State *g, GCObject *o); | ||
103 | static lu_mem atomic (lua_State *L); | ||
104 | static void entersweep (lua_State *L); | ||
105 | |||
106 | |||
107 | /* | ||
108 | ** {====================================================== | ||
109 | ** Generic functions | ||
110 | ** ======================================================= | ||
111 | */ | ||
112 | |||
113 | |||
114 | /* | ||
115 | ** one after last element in a hash array | ||
116 | */ | ||
117 | #define gnodelast(h) gnode(h, cast_sizet(sizenode(h))) | ||
118 | |||
119 | |||
120 | static GCObject **getgclist (GCObject *o) { | ||
121 | switch (o->tt) { | ||
122 | case LUA_VTABLE: return &gco2t(o)->gclist; | ||
123 | case LUA_VLCL: return &gco2lcl(o)->gclist; | ||
124 | case LUA_VCCL: return &gco2ccl(o)->gclist; | ||
125 | case LUA_VTHREAD: return &gco2th(o)->gclist; | ||
126 | case LUA_VPROTO: return &gco2p(o)->gclist; | ||
127 | case LUA_VUSERDATA: { | ||
128 | Udata *u = gco2u(o); | ||
129 | lua_assert(u->nuvalue > 0); | ||
130 | return &u->gclist; | ||
131 | } | ||
132 | default: lua_assert(0); return 0; | ||
133 | } | ||
134 | } | ||
135 | |||
136 | |||
137 | /* | ||
138 | ** Link a collectable object 'o' with a known type into list pointed by 'p'. | ||
139 | */ | ||
140 | #define linkgclist(o,p) ((o)->gclist = (p), (p) = obj2gco(o)) | ||
141 | |||
142 | |||
143 | /* | ||
144 | ** Link a generic collectable object 'o' into list pointed by 'p'. | ||
145 | */ | ||
146 | #define linkobjgclist(o,p) (*getgclist(o) = (p), (p) = obj2gco(o)) | ||
147 | |||
148 | |||
149 | |||
150 | /* | ||
151 | ** Clear keys for empty entries in tables. If entry is empty | ||
152 | ** and its key is not marked, mark its entry as dead. This allows the | ||
153 | ** collection of the key, but keeps its entry in the table (its removal | ||
154 | ** could break a chain). The main feature of a dead key is that it must | ||
155 | ** be different from any other value, to do not disturb searches. | ||
156 | ** Other places never manipulate dead keys, because its associated empty | ||
157 | ** value is enough to signal that the entry is logically empty. | ||
158 | */ | ||
159 | static void clearkey (Node *n) { | ||
160 | lua_assert(isempty(gval(n))); | ||
161 | if (keyiswhite(n)) | ||
162 | setdeadkey(n); /* unused and unmarked key; remove it */ | ||
163 | } | ||
164 | |||
165 | |||
166 | /* | ||
167 | ** tells whether a key or value can be cleared from a weak | ||
168 | ** table. Non-collectable objects are never removed from weak | ||
169 | ** tables. Strings behave as 'values', so are never removed too. for | ||
170 | ** other objects: if really collected, cannot keep them; for objects | ||
171 | ** being finalized, keep them in keys, but not in values | ||
172 | */ | ||
173 | static int iscleared (global_State *g, const GCObject *o) { | ||
174 | if (o == NULL) return 0; /* non-collectable value */ | ||
175 | else if (novariant(o->tt) == LUA_TSTRING) { | ||
176 | markobject(g, o); /* strings are 'values', so are never weak */ | ||
177 | return 0; | ||
178 | } | ||
179 | else return iswhite(o); | ||
180 | } | ||
181 | |||
182 | |||
183 | /* | ||
184 | ** barrier that moves collector forward, that is, mark the white object | ||
185 | ** 'v' being pointed by the black object 'o'. (If in sweep phase, clear | ||
186 | ** the black object to white [sweep it] to avoid other barrier calls for | ||
187 | ** this same object.) In the generational mode, 'v' must also become | ||
188 | ** old, if 'o' is old; however, it cannot be changed directly to OLD, | ||
189 | ** because it may still point to non-old objects. So, it is marked as | ||
190 | ** OLD0. In the next cycle it will become OLD1, and in the next it | ||
191 | ** will finally become OLD (regular old). | ||
192 | */ | ||
193 | void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) { | ||
194 | global_State *g = G(L); | ||
195 | lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o)); | ||
196 | if (keepinvariant(g)) { /* must keep invariant? */ | ||
197 | reallymarkobject(g, v); /* restore invariant */ | ||
198 | if (isold(o)) { | ||
199 | lua_assert(!isold(v)); /* white object could not be old */ | ||
200 | setage(v, G_OLD0); /* restore generational invariant */ | ||
201 | } | ||
202 | } | ||
203 | else { /* sweep phase */ | ||
204 | lua_assert(issweepphase(g)); | ||
205 | makewhite(g, o); /* mark main obj. as white to avoid other barriers */ | ||
206 | } | ||
207 | } | ||
208 | |||
209 | |||
210 | /* | ||
211 | ** barrier that moves collector backward, that is, mark the black object | ||
212 | ** pointing to a white object as gray again. | ||
213 | */ | ||
214 | void luaC_barrierback_ (lua_State *L, GCObject *o) { | ||
215 | global_State *g = G(L); | ||
216 | lua_assert(isblack(o) && !isdead(g, o)); | ||
217 | lua_assert(g->gckind != KGC_GEN || (isold(o) && getage(o) != G_TOUCHED1)); | ||
218 | if (getage(o) != G_TOUCHED2) /* not already in gray list? */ | ||
219 | linkobjgclist(o, g->grayagain); /* link it in 'grayagain' */ | ||
220 | black2gray(o); /* make object gray (again) */ | ||
221 | setage(o, G_TOUCHED1); /* touched in current cycle */ | ||
222 | } | ||
223 | |||
224 | |||
225 | void luaC_fix (lua_State *L, GCObject *o) { | ||
226 | global_State *g = G(L); | ||
227 | lua_assert(g->allgc == o); /* object must be 1st in 'allgc' list! */ | ||
228 | white2gray(o); /* they will be gray forever */ | ||
229 | setage(o, G_OLD); /* and old forever */ | ||
230 | g->allgc = o->next; /* remove object from 'allgc' list */ | ||
231 | o->next = g->fixedgc; /* link it to 'fixedgc' list */ | ||
232 | g->fixedgc = o; | ||
233 | } | ||
234 | |||
235 | |||
236 | /* | ||
237 | ** create a new collectable object (with given type and size) and link | ||
238 | ** it to 'allgc' list. | ||
239 | */ | ||
240 | GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) { | ||
241 | global_State *g = G(L); | ||
242 | GCObject *o = cast(GCObject *, luaM_newobject(L, novariant(tt), sz)); | ||
243 | o->marked = luaC_white(g); | ||
244 | o->tt = tt; | ||
245 | o->next = g->allgc; | ||
246 | g->allgc = o; | ||
247 | return o; | ||
248 | } | ||
249 | |||
250 | /* }====================================================== */ | ||
251 | |||
252 | |||
253 | |||
254 | /* | ||
255 | ** {====================================================== | ||
256 | ** Mark functions | ||
257 | ** ======================================================= | ||
258 | */ | ||
259 | |||
260 | |||
261 | /* | ||
262 | ** Mark an object. Userdata, strings, and closed upvalues are visited | ||
263 | ** and turned black here. Other objects are marked gray and added | ||
264 | ** to appropriate list to be visited (and turned black) later. (Open | ||
265 | ** upvalues are already linked in 'headuv' list. They are kept gray | ||
266 | ** to avoid barriers, as their values will be revisited by the thread.) | ||
267 | */ | ||
268 | static void reallymarkobject (global_State *g, GCObject *o) { | ||
269 | white2gray(o); | ||
270 | switch (o->tt) { | ||
271 | case LUA_VSHRSTR: | ||
272 | case LUA_VLNGSTR: { | ||
273 | gray2black(o); | ||
274 | break; | ||
275 | } | ||
276 | case LUA_VUPVAL: { | ||
277 | UpVal *uv = gco2upv(o); | ||
278 | if (!upisopen(uv)) /* open upvalues are kept gray */ | ||
279 | gray2black(o); | ||
280 | markvalue(g, uv->v); /* mark its content */ | ||
281 | break; | ||
282 | } | ||
283 | case LUA_VUSERDATA: { | ||
284 | Udata *u = gco2u(o); | ||
285 | if (u->nuvalue == 0) { /* no user values? */ | ||
286 | markobjectN(g, u->metatable); /* mark its metatable */ | ||
287 | gray2black(o); /* nothing else to mark */ | ||
288 | break; | ||
289 | } | ||
290 | /* else... */ | ||
291 | } /* FALLTHROUGH */ | ||
292 | case LUA_VLCL: case LUA_VCCL: case LUA_VTABLE: | ||
293 | case LUA_VTHREAD: case LUA_VPROTO: { | ||
294 | linkobjgclist(o, g->gray); | ||
295 | break; | ||
296 | } | ||
297 | default: lua_assert(0); break; | ||
298 | } | ||
299 | } | ||
300 | |||
301 | |||
302 | /* | ||
303 | ** mark metamethods for basic types | ||
304 | */ | ||
305 | static void markmt (global_State *g) { | ||
306 | int i; | ||
307 | for (i=0; i < LUA_NUMTAGS; i++) | ||
308 | markobjectN(g, g->mt[i]); | ||
309 | } | ||
310 | |||
311 | |||
312 | /* | ||
313 | ** mark all objects in list of being-finalized | ||
314 | */ | ||
315 | static lu_mem markbeingfnz (global_State *g) { | ||
316 | GCObject *o; | ||
317 | lu_mem count = 0; | ||
318 | for (o = g->tobefnz; o != NULL; o = o->next) { | ||
319 | count++; | ||
320 | markobject(g, o); | ||
321 | } | ||
322 | return count; | ||
323 | } | ||
324 | |||
325 | |||
326 | /* | ||
327 | ** Mark all values stored in marked open upvalues from non-marked threads. | ||
328 | ** (Values from marked threads were already marked when traversing the | ||
329 | ** thread.) Remove from the list threads that no longer have upvalues and | ||
330 | ** not-marked threads. | ||
331 | */ | ||
332 | static int remarkupvals (global_State *g) { | ||
333 | lua_State *thread; | ||
334 | lua_State **p = &g->twups; | ||
335 | int work = 0; | ||
336 | while ((thread = *p) != NULL) { | ||
337 | work++; | ||
338 | lua_assert(!isblack(thread)); /* threads are never black */ | ||
339 | if (isgray(thread) && thread->openupval != NULL) | ||
340 | p = &thread->twups; /* keep marked thread with upvalues in the list */ | ||
341 | else { /* thread is not marked or without upvalues */ | ||
342 | UpVal *uv; | ||
343 | *p = thread->twups; /* remove thread from the list */ | ||
344 | thread->twups = thread; /* mark that it is out of list */ | ||
345 | for (uv = thread->openupval; uv != NULL; uv = uv->u.open.next) { | ||
346 | work++; | ||
347 | if (!iswhite(uv)) /* upvalue already visited? */ | ||
348 | markvalue(g, uv->v); /* mark its value */ | ||
349 | } | ||
350 | } | ||
351 | } | ||
352 | return work; | ||
353 | } | ||
354 | |||
355 | |||
356 | /* | ||
357 | ** mark root set and reset all gray lists, to start a new collection | ||
358 | */ | ||
359 | static void restartcollection (global_State *g) { | ||
360 | g->gray = g->grayagain = NULL; | ||
361 | g->weak = g->allweak = g->ephemeron = NULL; | ||
362 | markobject(g, g->mainthread); | ||
363 | markvalue(g, &g->l_registry); | ||
364 | markmt(g); | ||
365 | markbeingfnz(g); /* mark any finalizing object left from previous cycle */ | ||
366 | } | ||
367 | |||
368 | /* }====================================================== */ | ||
369 | |||
370 | |||
371 | /* | ||
372 | ** {====================================================== | ||
373 | ** Traverse functions | ||
374 | ** ======================================================= | ||
375 | */ | ||
376 | |||
377 | /* | ||
378 | ** Traverse a table with weak values and link it to proper list. During | ||
379 | ** propagate phase, keep it in 'grayagain' list, to be revisited in the | ||
380 | ** atomic phase. In the atomic phase, if table has any white value, | ||
381 | ** put it in 'weak' list, to be cleared. | ||
382 | */ | ||
383 | static void traverseweakvalue (global_State *g, Table *h) { | ||
384 | Node *n, *limit = gnodelast(h); | ||
385 | /* if there is array part, assume it may have white values (it is not | ||
386 | worth traversing it now just to check) */ | ||
387 | int hasclears = (h->alimit > 0); | ||
388 | for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */ | ||
389 | if (isempty(gval(n))) /* entry is empty? */ | ||
390 | clearkey(n); /* clear its key */ | ||
391 | else { | ||
392 | lua_assert(!keyisnil(n)); | ||
393 | markkey(g, n); | ||
394 | if (!hasclears && iscleared(g, gcvalueN(gval(n)))) /* a white value? */ | ||
395 | hasclears = 1; /* table will have to be cleared */ | ||
396 | } | ||
397 | } | ||
398 | if (g->gcstate == GCSatomic && hasclears) | ||
399 | linkgclist(h, g->weak); /* has to be cleared later */ | ||
400 | else | ||
401 | linkgclist(h, g->grayagain); /* must retraverse it in atomic phase */ | ||
402 | } | ||
403 | |||
404 | |||
405 | /* | ||
406 | ** Traverse an ephemeron table and link it to proper list. Returns true | ||
407 | ** iff any object was marked during this traversal (which implies that | ||
408 | ** convergence has to continue). During propagation phase, keep table | ||
409 | ** in 'grayagain' list, to be visited again in the atomic phase. In | ||
410 | ** the atomic phase, if table has any white->white entry, it has to | ||
411 | ** be revisited during ephemeron convergence (as that key may turn | ||
412 | ** black). Otherwise, if it has any white key, table has to be cleared | ||
413 | ** (in the atomic phase). In generational mode, it (like all visited | ||
414 | ** tables) must be kept in some gray list for post-processing. | ||
415 | */ | ||
416 | static int traverseephemeron (global_State *g, Table *h, int inv) { | ||
417 | int marked = 0; /* true if an object is marked in this traversal */ | ||
418 | int hasclears = 0; /* true if table has white keys */ | ||
419 | int hasww = 0; /* true if table has entry "white-key -> white-value" */ | ||
420 | unsigned int i; | ||
421 | unsigned int asize = luaH_realasize(h); | ||
422 | unsigned int nsize = sizenode(h); | ||
423 | /* traverse array part */ | ||
424 | for (i = 0; i < asize; i++) { | ||
425 | if (valiswhite(&h->array[i])) { | ||
426 | marked = 1; | ||
427 | reallymarkobject(g, gcvalue(&h->array[i])); | ||
428 | } | ||
429 | } | ||
430 | /* traverse hash part; if 'inv', traverse descending | ||
431 | (see 'convergeephemerons') */ | ||
432 | for (i = 0; i < nsize; i++) { | ||
433 | Node *n = inv ? gnode(h, nsize - 1 - i) : gnode(h, i); | ||
434 | if (isempty(gval(n))) /* entry is empty? */ | ||
435 | clearkey(n); /* clear its key */ | ||
436 | else if (iscleared(g, gckeyN(n))) { /* key is not marked (yet)? */ | ||
437 | hasclears = 1; /* table must be cleared */ | ||
438 | if (valiswhite(gval(n))) /* value not marked yet? */ | ||
439 | hasww = 1; /* white-white entry */ | ||
440 | } | ||
441 | else if (valiswhite(gval(n))) { /* value not marked yet? */ | ||
442 | marked = 1; | ||
443 | reallymarkobject(g, gcvalue(gval(n))); /* mark it now */ | ||
444 | } | ||
445 | } | ||
446 | /* link table into proper list */ | ||
447 | if (g->gcstate == GCSpropagate) | ||
448 | linkgclist(h, g->grayagain); /* must retraverse it in atomic phase */ | ||
449 | else if (hasww) /* table has white->white entries? */ | ||
450 | linkgclist(h, g->ephemeron); /* have to propagate again */ | ||
451 | else if (hasclears) /* table has white keys? */ | ||
452 | linkgclist(h, g->allweak); /* may have to clean white keys */ | ||
453 | else if (g->gckind == KGC_GEN) | ||
454 | linkgclist(h, g->grayagain); /* keep it in some list */ | ||
455 | else | ||
456 | gray2black(h); | ||
457 | return marked; | ||
458 | } | ||
459 | |||
460 | |||
461 | static void traversestrongtable (global_State *g, Table *h) { | ||
462 | Node *n, *limit = gnodelast(h); | ||
463 | unsigned int i; | ||
464 | unsigned int asize = luaH_realasize(h); | ||
465 | for (i = 0; i < asize; i++) /* traverse array part */ | ||
466 | markvalue(g, &h->array[i]); | ||
467 | for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */ | ||
468 | if (isempty(gval(n))) /* entry is empty? */ | ||
469 | clearkey(n); /* clear its key */ | ||
470 | else { | ||
471 | lua_assert(!keyisnil(n)); | ||
472 | markkey(g, n); | ||
473 | markvalue(g, gval(n)); | ||
474 | } | ||
475 | } | ||
476 | if (g->gckind == KGC_GEN) { | ||
477 | linkgclist(h, g->grayagain); /* keep it in some gray list */ | ||
478 | black2gray(h); | ||
479 | } | ||
480 | } | ||
481 | |||
482 | |||
483 | static lu_mem traversetable (global_State *g, Table *h) { | ||
484 | const char *weakkey, *weakvalue; | ||
485 | const TValue *mode = gfasttm(g, h->metatable, TM_MODE); | ||
486 | markobjectN(g, h->metatable); | ||
487 | if (mode && ttisstring(mode) && /* is there a weak mode? */ | ||
488 | (cast_void(weakkey = strchr(svalue(mode), 'k')), | ||
489 | cast_void(weakvalue = strchr(svalue(mode), 'v')), | ||
490 | (weakkey || weakvalue))) { /* is really weak? */ | ||
491 | black2gray(h); /* keep table gray */ | ||
492 | if (!weakkey) /* strong keys? */ | ||
493 | traverseweakvalue(g, h); | ||
494 | else if (!weakvalue) /* strong values? */ | ||
495 | traverseephemeron(g, h, 0); | ||
496 | else /* all weak */ | ||
497 | linkgclist(h, g->allweak); /* nothing to traverse now */ | ||
498 | } | ||
499 | else /* not weak */ | ||
500 | traversestrongtable(g, h); | ||
501 | return 1 + h->alimit + 2 * allocsizenode(h); | ||
502 | } | ||
503 | |||
504 | |||
505 | static int traverseudata (global_State *g, Udata *u) { | ||
506 | int i; | ||
507 | markobjectN(g, u->metatable); /* mark its metatable */ | ||
508 | for (i = 0; i < u->nuvalue; i++) | ||
509 | markvalue(g, &u->uv[i].uv); | ||
510 | if (g->gckind == KGC_GEN) { | ||
511 | linkgclist(u, g->grayagain); /* keep it in some gray list */ | ||
512 | black2gray(u); | ||
513 | } | ||
514 | return 1 + u->nuvalue; | ||
515 | } | ||
516 | |||
517 | |||
518 | /* | ||
519 | ** Traverse a prototype. (While a prototype is being build, its | ||
520 | ** arrays can be larger than needed; the extra slots are filled with | ||
521 | ** NULL, so the use of 'markobjectN') | ||
522 | */ | ||
523 | static int traverseproto (global_State *g, Proto *f) { | ||
524 | int i; | ||
525 | markobjectN(g, f->source); | ||
526 | for (i = 0; i < f->sizek; i++) /* mark literals */ | ||
527 | markvalue(g, &f->k[i]); | ||
528 | for (i = 0; i < f->sizeupvalues; i++) /* mark upvalue names */ | ||
529 | markobjectN(g, f->upvalues[i].name); | ||
530 | for (i = 0; i < f->sizep; i++) /* mark nested protos */ | ||
531 | markobjectN(g, f->p[i]); | ||
532 | for (i = 0; i < f->sizelocvars; i++) /* mark local-variable names */ | ||
533 | markobjectN(g, f->locvars[i].varname); | ||
534 | return 1 + f->sizek + f->sizeupvalues + f->sizep + f->sizelocvars; | ||
535 | } | ||
536 | |||
537 | |||
538 | static int traverseCclosure (global_State *g, CClosure *cl) { | ||
539 | int i; | ||
540 | for (i = 0; i < cl->nupvalues; i++) /* mark its upvalues */ | ||
541 | markvalue(g, &cl->upvalue[i]); | ||
542 | return 1 + cl->nupvalues; | ||
543 | } | ||
544 | |||
545 | /* | ||
546 | ** Traverse a Lua closure, marking its prototype and its upvalues. | ||
547 | ** (Both can be NULL while closure is being created.) | ||
548 | */ | ||
549 | static int traverseLclosure (global_State *g, LClosure *cl) { | ||
550 | int i; | ||
551 | markobjectN(g, cl->p); /* mark its prototype */ | ||
552 | for (i = 0; i < cl->nupvalues; i++) { /* visit its upvalues */ | ||
553 | UpVal *uv = cl->upvals[i]; | ||
554 | markobjectN(g, uv); /* mark upvalue */ | ||
555 | } | ||
556 | return 1 + cl->nupvalues; | ||
557 | } | ||
558 | |||
559 | |||
560 | /* | ||
561 | ** Traverse a thread, marking the elements in the stack up to its top | ||
562 | ** and cleaning the rest of the stack in the final traversal. | ||
563 | ** That ensures that the entire stack have valid (non-dead) objects. | ||
564 | */ | ||
565 | static int traversethread (global_State *g, lua_State *th) { | ||
566 | UpVal *uv; | ||
567 | StkId o = th->stack; | ||
568 | if (o == NULL) | ||
569 | return 1; /* stack not completely built yet */ | ||
570 | lua_assert(g->gcstate == GCSatomic || | ||
571 | th->openupval == NULL || isintwups(th)); | ||
572 | for (; o < th->top; o++) /* mark live elements in the stack */ | ||
573 | markvalue(g, s2v(o)); | ||
574 | for (uv = th->openupval; uv != NULL; uv = uv->u.open.next) | ||
575 | markobject(g, uv); /* open upvalues cannot be collected */ | ||
576 | if (g->gcstate == GCSatomic) { /* final traversal? */ | ||
577 | StkId lim = th->stack + th->stacksize; /* real end of stack */ | ||
578 | for (; o < lim; o++) /* clear not-marked stack slice */ | ||
579 | setnilvalue(s2v(o)); | ||
580 | /* 'remarkupvals' may have removed thread from 'twups' list */ | ||
581 | if (!isintwups(th) && th->openupval != NULL) { | ||
582 | th->twups = g->twups; /* link it back to the list */ | ||
583 | g->twups = th; | ||
584 | } | ||
585 | } | ||
586 | else if (!g->gcemergency) | ||
587 | luaD_shrinkstack(th); /* do not change stack in emergency cycle */ | ||
588 | return 1 + th->stacksize; | ||
589 | } | ||
590 | |||
591 | |||
592 | /* | ||
593 | ** traverse one gray object, turning it to black (except for threads, | ||
594 | ** which are always gray). | ||
595 | */ | ||
596 | static lu_mem propagatemark (global_State *g) { | ||
597 | GCObject *o = g->gray; | ||
598 | gray2black(o); | ||
599 | g->gray = *getgclist(o); /* remove from 'gray' list */ | ||
600 | switch (o->tt) { | ||
601 | case LUA_VTABLE: return traversetable(g, gco2t(o)); | ||
602 | case LUA_VUSERDATA: return traverseudata(g, gco2u(o)); | ||
603 | case LUA_VLCL: return traverseLclosure(g, gco2lcl(o)); | ||
604 | case LUA_VCCL: return traverseCclosure(g, gco2ccl(o)); | ||
605 | case LUA_VPROTO: return traverseproto(g, gco2p(o)); | ||
606 | case LUA_VTHREAD: { | ||
607 | lua_State *th = gco2th(o); | ||
608 | linkgclist(th, g->grayagain); /* insert into 'grayagain' list */ | ||
609 | black2gray(o); | ||
610 | return traversethread(g, th); | ||
611 | } | ||
612 | default: lua_assert(0); return 0; | ||
613 | } | ||
614 | } | ||
615 | |||
616 | |||
617 | static lu_mem propagateall (global_State *g) { | ||
618 | lu_mem tot = 0; | ||
619 | while (g->gray) | ||
620 | tot += propagatemark(g); | ||
621 | return tot; | ||
622 | } | ||
623 | |||
624 | |||
625 | /* | ||
626 | ** Traverse all ephemeron tables propagating marks from keys to values. | ||
627 | ** Repeat until it converges, that is, nothing new is marked. 'dir' | ||
628 | ** inverts the direction of the traversals, trying to speed up | ||
629 | ** convergence on chains in the same table. | ||
630 | ** | ||
631 | */ | ||
632 | static void convergeephemerons (global_State *g) { | ||
633 | int changed; | ||
634 | int dir = 0; | ||
635 | do { | ||
636 | GCObject *w; | ||
637 | GCObject *next = g->ephemeron; /* get ephemeron list */ | ||
638 | g->ephemeron = NULL; /* tables may return to this list when traversed */ | ||
639 | changed = 0; | ||
640 | while ((w = next) != NULL) { /* for each ephemeron table */ | ||
641 | next = gco2t(w)->gclist; /* list is rebuilt during loop */ | ||
642 | if (traverseephemeron(g, gco2t(w), dir)) { /* marked some value? */ | ||
643 | propagateall(g); /* propagate changes */ | ||
644 | changed = 1; /* will have to revisit all ephemeron tables */ | ||
645 | } | ||
646 | } | ||
647 | dir = !dir; /* invert direction next time */ | ||
648 | } while (changed); /* repeat until no more changes */ | ||
649 | } | ||
650 | |||
651 | /* }====================================================== */ | ||
652 | |||
653 | |||
654 | /* | ||
655 | ** {====================================================== | ||
656 | ** Sweep Functions | ||
657 | ** ======================================================= | ||
658 | */ | ||
659 | |||
660 | |||
661 | /* | ||
662 | ** clear entries with unmarked keys from all weaktables in list 'l' | ||
663 | */ | ||
664 | static void clearbykeys (global_State *g, GCObject *l) { | ||
665 | for (; l; l = gco2t(l)->gclist) { | ||
666 | Table *h = gco2t(l); | ||
667 | Node *limit = gnodelast(h); | ||
668 | Node *n; | ||
669 | for (n = gnode(h, 0); n < limit; n++) { | ||
670 | if (iscleared(g, gckeyN(n))) /* unmarked key? */ | ||
671 | setempty(gval(n)); /* remove entry */ | ||
672 | if (isempty(gval(n))) /* is entry empty? */ | ||
673 | clearkey(n); /* clear its key */ | ||
674 | } | ||
675 | } | ||
676 | } | ||
677 | |||
678 | |||
679 | /* | ||
680 | ** clear entries with unmarked values from all weaktables in list 'l' up | ||
681 | ** to element 'f' | ||
682 | */ | ||
683 | static void clearbyvalues (global_State *g, GCObject *l, GCObject *f) { | ||
684 | for (; l != f; l = gco2t(l)->gclist) { | ||
685 | Table *h = gco2t(l); | ||
686 | Node *n, *limit = gnodelast(h); | ||
687 | unsigned int i; | ||
688 | unsigned int asize = luaH_realasize(h); | ||
689 | for (i = 0; i < asize; i++) { | ||
690 | TValue *o = &h->array[i]; | ||
691 | if (iscleared(g, gcvalueN(o))) /* value was collected? */ | ||
692 | setempty(o); /* remove entry */ | ||
693 | } | ||
694 | for (n = gnode(h, 0); n < limit; n++) { | ||
695 | if (iscleared(g, gcvalueN(gval(n)))) /* unmarked value? */ | ||
696 | setempty(gval(n)); /* remove entry */ | ||
697 | if (isempty(gval(n))) /* is entry empty? */ | ||
698 | clearkey(n); /* clear its key */ | ||
699 | } | ||
700 | } | ||
701 | } | ||
702 | |||
703 | |||
704 | static void freeupval (lua_State *L, UpVal *uv) { | ||
705 | if (upisopen(uv)) | ||
706 | luaF_unlinkupval(uv); | ||
707 | luaM_free(L, uv); | ||
708 | } | ||
709 | |||
710 | |||
711 | static void freeobj (lua_State *L, GCObject *o) { | ||
712 | switch (o->tt) { | ||
713 | case LUA_VPROTO: | ||
714 | luaF_freeproto(L, gco2p(o)); | ||
715 | break; | ||
716 | case LUA_VUPVAL: | ||
717 | freeupval(L, gco2upv(o)); | ||
718 | break; | ||
719 | case LUA_VLCL: | ||
720 | luaM_freemem(L, o, sizeLclosure(gco2lcl(o)->nupvalues)); | ||
721 | break; | ||
722 | case LUA_VCCL: | ||
723 | luaM_freemem(L, o, sizeCclosure(gco2ccl(o)->nupvalues)); | ||
724 | break; | ||
725 | case LUA_VTABLE: | ||
726 | luaH_free(L, gco2t(o)); | ||
727 | break; | ||
728 | case LUA_VTHREAD: | ||
729 | luaE_freethread(L, gco2th(o)); | ||
730 | break; | ||
731 | case LUA_VUSERDATA: { | ||
732 | Udata *u = gco2u(o); | ||
733 | luaM_freemem(L, o, sizeudata(u->nuvalue, u->len)); | ||
734 | break; | ||
735 | } | ||
736 | case LUA_VSHRSTR: | ||
737 | luaS_remove(L, gco2ts(o)); /* remove it from hash table */ | ||
738 | luaM_freemem(L, o, sizelstring(gco2ts(o)->shrlen)); | ||
739 | break; | ||
740 | case LUA_VLNGSTR: | ||
741 | luaM_freemem(L, o, sizelstring(gco2ts(o)->u.lnglen)); | ||
742 | break; | ||
743 | default: lua_assert(0); | ||
744 | } | ||
745 | } | ||
746 | |||
747 | |||
748 | /* | ||
749 | ** sweep at most 'countin' elements from a list of GCObjects erasing dead | ||
750 | ** objects, where a dead object is one marked with the old (non current) | ||
751 | ** white; change all non-dead objects back to white, preparing for next | ||
752 | ** collection cycle. Return where to continue the traversal or NULL if | ||
753 | ** list is finished. ('*countout' gets the number of elements traversed.) | ||
754 | */ | ||
755 | static GCObject **sweeplist (lua_State *L, GCObject **p, int countin, | ||
756 | int *countout) { | ||
757 | global_State *g = G(L); | ||
758 | int ow = otherwhite(g); | ||
759 | int i; | ||
760 | int white = luaC_white(g); /* current white */ | ||
761 | for (i = 0; *p != NULL && i < countin; i++) { | ||
762 | GCObject *curr = *p; | ||
763 | int marked = curr->marked; | ||
764 | if (isdeadm(ow, marked)) { /* is 'curr' dead? */ | ||
765 | *p = curr->next; /* remove 'curr' from list */ | ||
766 | freeobj(L, curr); /* erase 'curr' */ | ||
767 | } | ||
768 | else { /* change mark to 'white' */ | ||
769 | curr->marked = cast_byte((marked & maskcolors) | white); | ||
770 | p = &curr->next; /* go to next element */ | ||
771 | } | ||
772 | } | ||
773 | if (countout) | ||
774 | *countout = i; /* number of elements traversed */ | ||
775 | return (*p == NULL) ? NULL : p; | ||
776 | } | ||
777 | |||
778 | |||
779 | /* | ||
780 | ** sweep a list until a live object (or end of list) | ||
781 | */ | ||
782 | static GCObject **sweeptolive (lua_State *L, GCObject **p) { | ||
783 | GCObject **old = p; | ||
784 | do { | ||
785 | p = sweeplist(L, p, 1, NULL); | ||
786 | } while (p == old); | ||
787 | return p; | ||
788 | } | ||
789 | |||
790 | /* }====================================================== */ | ||
791 | |||
792 | |||
793 | /* | ||
794 | ** {====================================================== | ||
795 | ** Finalization | ||
796 | ** ======================================================= | ||
797 | */ | ||
798 | |||
799 | /* | ||
800 | ** If possible, shrink string table. | ||
801 | */ | ||
802 | static void checkSizes (lua_State *L, global_State *g) { | ||
803 | if (!g->gcemergency) { | ||
804 | if (g->strt.nuse < g->strt.size / 4) { /* string table too big? */ | ||
805 | l_mem olddebt = g->GCdebt; | ||
806 | luaS_resize(L, g->strt.size / 2); | ||
807 | g->GCestimate += g->GCdebt - olddebt; /* correct estimate */ | ||
808 | } | ||
809 | } | ||
810 | } | ||
811 | |||
812 | |||
813 | /* | ||
814 | ** Get the next udata to be finalized from the 'tobefnz' list, and | ||
815 | ** link it back into the 'allgc' list. | ||
816 | */ | ||
817 | static GCObject *udata2finalize (global_State *g) { | ||
818 | GCObject *o = g->tobefnz; /* get first element */ | ||
819 | lua_assert(tofinalize(o)); | ||
820 | g->tobefnz = o->next; /* remove it from 'tobefnz' list */ | ||
821 | o->next = g->allgc; /* return it to 'allgc' list */ | ||
822 | g->allgc = o; | ||
823 | resetbit(o->marked, FINALIZEDBIT); /* object is "normal" again */ | ||
824 | if (issweepphase(g)) | ||
825 | makewhite(g, o); /* "sweep" object */ | ||
826 | return o; | ||
827 | } | ||
828 | |||
829 | |||
830 | static void dothecall (lua_State *L, void *ud) { | ||
831 | UNUSED(ud); | ||
832 | luaD_callnoyield(L, L->top - 2, 0); | ||
833 | } | ||
834 | |||
835 | |||
836 | static void GCTM (lua_State *L) { | ||
837 | global_State *g = G(L); | ||
838 | const TValue *tm; | ||
839 | TValue v; | ||
840 | lua_assert(!g->gcemergency); | ||
841 | setgcovalue(L, &v, udata2finalize(g)); | ||
842 | tm = luaT_gettmbyobj(L, &v, TM_GC); | ||
843 | if (!notm(tm)) { /* is there a finalizer? */ | ||
844 | int status; | ||
845 | lu_byte oldah = L->allowhook; | ||
846 | int running = g->gcrunning; | ||
847 | L->allowhook = 0; /* stop debug hooks during GC metamethod */ | ||
848 | g->gcrunning = 0; /* avoid GC steps */ | ||
849 | setobj2s(L, L->top++, tm); /* push finalizer... */ | ||
850 | setobj2s(L, L->top++, &v); /* ... and its argument */ | ||
851 | L->ci->callstatus |= CIST_FIN; /* will run a finalizer */ | ||
852 | status = luaD_pcall(L, dothecall, NULL, savestack(L, L->top - 2), 0); | ||
853 | L->ci->callstatus &= ~CIST_FIN; /* not running a finalizer anymore */ | ||
854 | L->allowhook = oldah; /* restore hooks */ | ||
855 | g->gcrunning = running; /* restore state */ | ||
856 | if (unlikely(status != LUA_OK)) { /* error while running __gc? */ | ||
857 | luaE_warnerror(L, "__gc metamethod"); | ||
858 | L->top--; /* pops error object */ | ||
859 | } | ||
860 | } | ||
861 | } | ||
862 | |||
863 | |||
864 | /* | ||
865 | ** Call a few finalizers | ||
866 | */ | ||
867 | static int runafewfinalizers (lua_State *L, int n) { | ||
868 | global_State *g = G(L); | ||
869 | int i; | ||
870 | for (i = 0; i < n && g->tobefnz; i++) | ||
871 | GCTM(L); /* call one finalizer */ | ||
872 | return i; | ||
873 | } | ||
874 | |||
875 | |||
876 | /* | ||
877 | ** call all pending finalizers | ||
878 | */ | ||
879 | static void callallpendingfinalizers (lua_State *L) { | ||
880 | global_State *g = G(L); | ||
881 | while (g->tobefnz) | ||
882 | GCTM(L); | ||
883 | } | ||
884 | |||
885 | |||
886 | /* | ||
887 | ** find last 'next' field in list 'p' list (to add elements in its end) | ||
888 | */ | ||
889 | static GCObject **findlast (GCObject **p) { | ||
890 | while (*p != NULL) | ||
891 | p = &(*p)->next; | ||
892 | return p; | ||
893 | } | ||
894 | |||
895 | |||
896 | /* | ||
897 | ** Move all unreachable objects (or 'all' objects) that need | ||
898 | ** finalization from list 'finobj' to list 'tobefnz' (to be finalized). | ||
899 | ** (Note that objects after 'finobjold' cannot be white, so they | ||
900 | ** don't need to be traversed. In incremental mode, 'finobjold' is NULL, | ||
901 | ** so the whole list is traversed.) | ||
902 | */ | ||
903 | static void separatetobefnz (global_State *g, int all) { | ||
904 | GCObject *curr; | ||
905 | GCObject **p = &g->finobj; | ||
906 | GCObject **lastnext = findlast(&g->tobefnz); | ||
907 | while ((curr = *p) != g->finobjold) { /* traverse all finalizable objects */ | ||
908 | lua_assert(tofinalize(curr)); | ||
909 | if (!(iswhite(curr) || all)) /* not being collected? */ | ||
910 | p = &curr->next; /* don't bother with it */ | ||
911 | else { | ||
912 | if (curr == g->finobjsur) /* removing 'finobjsur'? */ | ||
913 | g->finobjsur = curr->next; /* correct it */ | ||
914 | *p = curr->next; /* remove 'curr' from 'finobj' list */ | ||
915 | curr->next = *lastnext; /* link at the end of 'tobefnz' list */ | ||
916 | *lastnext = curr; | ||
917 | lastnext = &curr->next; | ||
918 | } | ||
919 | } | ||
920 | } | ||
921 | |||
922 | |||
923 | /* | ||
924 | ** if object 'o' has a finalizer, remove it from 'allgc' list (must | ||
925 | ** search the list to find it) and link it in 'finobj' list. | ||
926 | */ | ||
927 | void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) { | ||
928 | global_State *g = G(L); | ||
929 | if (tofinalize(o) || /* obj. is already marked... */ | ||
930 | gfasttm(g, mt, TM_GC) == NULL) /* or has no finalizer? */ | ||
931 | return; /* nothing to be done */ | ||
932 | else { /* move 'o' to 'finobj' list */ | ||
933 | GCObject **p; | ||
934 | if (issweepphase(g)) { | ||
935 | makewhite(g, o); /* "sweep" object 'o' */ | ||
936 | if (g->sweepgc == &o->next) /* should not remove 'sweepgc' object */ | ||
937 | g->sweepgc = sweeptolive(L, g->sweepgc); /* change 'sweepgc' */ | ||
938 | } | ||
939 | else { /* correct pointers into 'allgc' list, if needed */ | ||
940 | if (o == g->survival) | ||
941 | g->survival = o->next; | ||
942 | if (o == g->old) | ||
943 | g->old = o->next; | ||
944 | if (o == g->reallyold) | ||
945 | g->reallyold = o->next; | ||
946 | } | ||
947 | /* search for pointer pointing to 'o' */ | ||
948 | for (p = &g->allgc; *p != o; p = &(*p)->next) { /* empty */ } | ||
949 | *p = o->next; /* remove 'o' from 'allgc' list */ | ||
950 | o->next = g->finobj; /* link it in 'finobj' list */ | ||
951 | g->finobj = o; | ||
952 | l_setbit(o->marked, FINALIZEDBIT); /* mark it as such */ | ||
953 | } | ||
954 | } | ||
955 | |||
956 | /* }====================================================== */ | ||
957 | |||
958 | |||
959 | /* | ||
960 | ** {====================================================== | ||
961 | ** Generational Collector | ||
962 | ** ======================================================= | ||
963 | */ | ||
964 | |||
965 | static void setpause (global_State *g); | ||
966 | |||
967 | |||
968 | /* mask to erase all color bits, not changing gen-related stuff */ | ||
969 | #define maskgencolors (~(bitmask(BLACKBIT) | WHITEBITS)) | ||
970 | |||
971 | |||
972 | /* | ||
973 | ** Sweep a list of objects, deleting dead ones and turning | ||
974 | ** the non dead to old (without changing their colors). | ||
975 | */ | ||
976 | static void sweep2old (lua_State *L, GCObject **p) { | ||
977 | GCObject *curr; | ||
978 | while ((curr = *p) != NULL) { | ||
979 | if (iswhite(curr)) { /* is 'curr' dead? */ | ||
980 | lua_assert(isdead(G(L), curr)); | ||
981 | *p = curr->next; /* remove 'curr' from list */ | ||
982 | freeobj(L, curr); /* erase 'curr' */ | ||
983 | } | ||
984 | else { /* all surviving objects become old */ | ||
985 | setage(curr, G_OLD); | ||
986 | p = &curr->next; /* go to next element */ | ||
987 | } | ||
988 | } | ||
989 | } | ||
990 | |||
991 | |||
992 | /* | ||
993 | ** Sweep for generational mode. Delete dead objects. (Because the | ||
994 | ** collection is not incremental, there are no "new white" objects | ||
995 | ** during the sweep. So, any white object must be dead.) For | ||
996 | ** non-dead objects, advance their ages and clear the color of | ||
997 | ** new objects. (Old objects keep their colors.) | ||
998 | */ | ||
999 | static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p, | ||
1000 | GCObject *limit) { | ||
1001 | static const lu_byte nextage[] = { | ||
1002 | G_SURVIVAL, /* from G_NEW */ | ||
1003 | G_OLD1, /* from G_SURVIVAL */ | ||
1004 | G_OLD1, /* from G_OLD0 */ | ||
1005 | G_OLD, /* from G_OLD1 */ | ||
1006 | G_OLD, /* from G_OLD (do not change) */ | ||
1007 | G_TOUCHED1, /* from G_TOUCHED1 (do not change) */ | ||
1008 | G_TOUCHED2 /* from G_TOUCHED2 (do not change) */ | ||
1009 | }; | ||
1010 | int white = luaC_white(g); | ||
1011 | GCObject *curr; | ||
1012 | while ((curr = *p) != limit) { | ||
1013 | if (iswhite(curr)) { /* is 'curr' dead? */ | ||
1014 | lua_assert(!isold(curr) && isdead(g, curr)); | ||
1015 | *p = curr->next; /* remove 'curr' from list */ | ||
1016 | freeobj(L, curr); /* erase 'curr' */ | ||
1017 | } | ||
1018 | else { /* correct mark and age */ | ||
1019 | if (getage(curr) == G_NEW) | ||
1020 | curr->marked = cast_byte((curr->marked & maskgencolors) | white); | ||
1021 | setage(curr, nextage[getage(curr)]); | ||
1022 | p = &curr->next; /* go to next element */ | ||
1023 | } | ||
1024 | } | ||
1025 | return p; | ||
1026 | } | ||
1027 | |||
1028 | |||
1029 | /* | ||
1030 | ** Traverse a list making all its elements white and clearing their | ||
1031 | ** age. | ||
1032 | */ | ||
1033 | static void whitelist (global_State *g, GCObject *p) { | ||
1034 | int white = luaC_white(g); | ||
1035 | for (; p != NULL; p = p->next) | ||
1036 | p->marked = cast_byte((p->marked & maskcolors) | white); | ||
1037 | } | ||
1038 | |||
1039 | |||
1040 | /* | ||
1041 | ** Correct a list of gray objects. | ||
1042 | ** Because this correction is done after sweeping, young objects might | ||
1043 | ** be turned white and still be in the list. They are only removed. | ||
1044 | ** For tables and userdata, advance 'touched1' to 'touched2'; 'touched2' | ||
1045 | ** objects become regular old and are removed from the list. | ||
1046 | ** For threads, just remove white ones from the list. | ||
1047 | */ | ||
1048 | static GCObject **correctgraylist (GCObject **p) { | ||
1049 | GCObject *curr; | ||
1050 | while ((curr = *p) != NULL) { | ||
1051 | switch (curr->tt) { | ||
1052 | case LUA_VTABLE: case LUA_VUSERDATA: { | ||
1053 | GCObject **next = getgclist(curr); | ||
1054 | if (getage(curr) == G_TOUCHED1) { /* touched in this cycle? */ | ||
1055 | lua_assert(isgray(curr)); | ||
1056 | gray2black(curr); /* make it black, for next barrier */ | ||
1057 | changeage(curr, G_TOUCHED1, G_TOUCHED2); | ||
1058 | p = next; /* go to next element */ | ||
1059 | } | ||
1060 | else { /* not touched in this cycle */ | ||
1061 | if (!iswhite(curr)) { /* not white? */ | ||
1062 | lua_assert(isold(curr)); | ||
1063 | if (getage(curr) == G_TOUCHED2) /* advance from G_TOUCHED2... */ | ||
1064 | changeage(curr, G_TOUCHED2, G_OLD); /* ... to G_OLD */ | ||
1065 | gray2black(curr); /* make it black */ | ||
1066 | } | ||
1067 | /* else, object is white: just remove it from this list */ | ||
1068 | *p = *next; /* remove 'curr' from gray list */ | ||
1069 | } | ||
1070 | break; | ||
1071 | } | ||
1072 | case LUA_VTHREAD: { | ||
1073 | lua_State *th = gco2th(curr); | ||
1074 | lua_assert(!isblack(th)); | ||
1075 | if (iswhite(th)) /* new object? */ | ||
1076 | *p = th->gclist; /* remove from gray list */ | ||
1077 | else /* old threads remain gray */ | ||
1078 | p = &th->gclist; /* go to next element */ | ||
1079 | break; | ||
1080 | } | ||
1081 | default: lua_assert(0); /* nothing more could be gray here */ | ||
1082 | } | ||
1083 | } | ||
1084 | return p; | ||
1085 | } | ||
1086 | |||
1087 | |||
1088 | /* | ||
1089 | ** Correct all gray lists, coalescing them into 'grayagain'. | ||
1090 | */ | ||
1091 | static void correctgraylists (global_State *g) { | ||
1092 | GCObject **list = correctgraylist(&g->grayagain); | ||
1093 | *list = g->weak; g->weak = NULL; | ||
1094 | list = correctgraylist(list); | ||
1095 | *list = g->allweak; g->allweak = NULL; | ||
1096 | list = correctgraylist(list); | ||
1097 | *list = g->ephemeron; g->ephemeron = NULL; | ||
1098 | correctgraylist(list); | ||
1099 | } | ||
1100 | |||
1101 | |||
1102 | /* | ||
1103 | ** Mark 'OLD1' objects when starting a new young collection. | ||
1104 | ** Gray objects are already in some gray list, and so will be visited | ||
1105 | ** in the atomic step. | ||
1106 | */ | ||
1107 | static void markold (global_State *g, GCObject *from, GCObject *to) { | ||
1108 | GCObject *p; | ||
1109 | for (p = from; p != to; p = p->next) { | ||
1110 | if (getage(p) == G_OLD1) { | ||
1111 | lua_assert(!iswhite(p)); | ||
1112 | if (isblack(p)) { | ||
1113 | black2gray(p); /* should be '2white', but gray works too */ | ||
1114 | reallymarkobject(g, p); | ||
1115 | } | ||
1116 | } | ||
1117 | } | ||
1118 | } | ||
1119 | |||
1120 | |||
1121 | /* | ||
1122 | ** Finish a young-generation collection. | ||
1123 | */ | ||
1124 | static void finishgencycle (lua_State *L, global_State *g) { | ||
1125 | correctgraylists(g); | ||
1126 | checkSizes(L, g); | ||
1127 | g->gcstate = GCSpropagate; /* skip restart */ | ||
1128 | if (!g->gcemergency) | ||
1129 | callallpendingfinalizers(L); | ||
1130 | } | ||
1131 | |||
1132 | |||
1133 | /* | ||
1134 | ** Does a young collection. First, mark 'OLD1' objects. (Only survival | ||
1135 | ** and "recent old" lists can contain 'OLD1' objects. New lists cannot | ||
1136 | ** contain 'OLD1' objects, at most 'OLD0' objects that were already | ||
1137 | ** visited when marked old.) Then does the atomic step. Then, | ||
1138 | ** sweep all lists and advance pointers. Finally, finish the collection. | ||
1139 | */ | ||
1140 | static void youngcollection (lua_State *L, global_State *g) { | ||
1141 | GCObject **psurvival; /* to point to first non-dead survival object */ | ||
1142 | lua_assert(g->gcstate == GCSpropagate); | ||
1143 | markold(g, g->survival, g->reallyold); | ||
1144 | markold(g, g->finobj, g->finobjrold); | ||
1145 | atomic(L); | ||
1146 | |||
1147 | /* sweep nursery and get a pointer to its last live element */ | ||
1148 | psurvival = sweepgen(L, g, &g->allgc, g->survival); | ||
1149 | /* sweep 'survival' and 'old' */ | ||
1150 | sweepgen(L, g, psurvival, g->reallyold); | ||
1151 | g->reallyold = g->old; | ||
1152 | g->old = *psurvival; /* 'survival' survivals are old now */ | ||
1153 | g->survival = g->allgc; /* all news are survivals */ | ||
1154 | |||
1155 | /* repeat for 'finobj' lists */ | ||
1156 | psurvival = sweepgen(L, g, &g->finobj, g->finobjsur); | ||
1157 | /* sweep 'survival' and 'old' */ | ||
1158 | sweepgen(L, g, psurvival, g->finobjrold); | ||
1159 | g->finobjrold = g->finobjold; | ||
1160 | g->finobjold = *psurvival; /* 'survival' survivals are old now */ | ||
1161 | g->finobjsur = g->finobj; /* all news are survivals */ | ||
1162 | |||
1163 | sweepgen(L, g, &g->tobefnz, NULL); | ||
1164 | |||
1165 | finishgencycle(L, g); | ||
1166 | } | ||
1167 | |||
1168 | |||
1169 | static void atomic2gen (lua_State *L, global_State *g) { | ||
1170 | /* sweep all elements making them old */ | ||
1171 | sweep2old(L, &g->allgc); | ||
1172 | /* everything alive now is old */ | ||
1173 | g->reallyold = g->old = g->survival = g->allgc; | ||
1174 | |||
1175 | /* repeat for 'finobj' lists */ | ||
1176 | sweep2old(L, &g->finobj); | ||
1177 | g->finobjrold = g->finobjold = g->finobjsur = g->finobj; | ||
1178 | |||
1179 | sweep2old(L, &g->tobefnz); | ||
1180 | |||
1181 | g->gckind = KGC_GEN; | ||
1182 | g->lastatomic = 0; | ||
1183 | g->GCestimate = gettotalbytes(g); /* base for memory control */ | ||
1184 | finishgencycle(L, g); | ||
1185 | } | ||
1186 | |||
1187 | |||
1188 | /* | ||
1189 | ** Enter generational mode. Must go until the end of an atomic cycle | ||
1190 | ** to ensure that all threads and weak tables are in the gray lists. | ||
1191 | ** Then, turn all objects into old and finishes the collection. | ||
1192 | */ | ||
1193 | static lu_mem entergen (lua_State *L, global_State *g) { | ||
1194 | lu_mem numobjs; | ||
1195 | luaC_runtilstate(L, bitmask(GCSpause)); /* prepare to start a new cycle */ | ||
1196 | luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */ | ||
1197 | numobjs = atomic(L); /* propagates all and then do the atomic stuff */ | ||
1198 | atomic2gen(L, g); | ||
1199 | return numobjs; | ||
1200 | } | ||
1201 | |||
1202 | |||
1203 | /* | ||
1204 | ** Enter incremental mode. Turn all objects white, make all | ||
1205 | ** intermediate lists point to NULL (to avoid invalid pointers), | ||
1206 | ** and go to the pause state. | ||
1207 | */ | ||
1208 | static void enterinc (global_State *g) { | ||
1209 | whitelist(g, g->allgc); | ||
1210 | g->reallyold = g->old = g->survival = NULL; | ||
1211 | whitelist(g, g->finobj); | ||
1212 | whitelist(g, g->tobefnz); | ||
1213 | g->finobjrold = g->finobjold = g->finobjsur = NULL; | ||
1214 | g->gcstate = GCSpause; | ||
1215 | g->gckind = KGC_INC; | ||
1216 | g->lastatomic = 0; | ||
1217 | } | ||
1218 | |||
1219 | |||
1220 | /* | ||
1221 | ** Change collector mode to 'newmode'. | ||
1222 | */ | ||
1223 | void luaC_changemode (lua_State *L, int newmode) { | ||
1224 | global_State *g = G(L); | ||
1225 | if (newmode != g->gckind) { | ||
1226 | if (newmode == KGC_GEN) /* entering generational mode? */ | ||
1227 | entergen(L, g); | ||
1228 | else | ||
1229 | enterinc(g); /* entering incremental mode */ | ||
1230 | } | ||
1231 | g->lastatomic = 0; | ||
1232 | } | ||
1233 | |||
1234 | |||
1235 | /* | ||
1236 | ** Does a full collection in generational mode. | ||
1237 | */ | ||
1238 | static lu_mem fullgen (lua_State *L, global_State *g) { | ||
1239 | enterinc(g); | ||
1240 | return entergen(L, g); | ||
1241 | } | ||
1242 | |||
1243 | |||
1244 | /* | ||
1245 | ** Set debt for the next minor collection, which will happen when | ||
1246 | ** memory grows 'genminormul'%. | ||
1247 | */ | ||
1248 | static void setminordebt (global_State *g) { | ||
1249 | luaE_setdebt(g, -(cast(l_mem, (gettotalbytes(g) / 100)) * g->genminormul)); | ||
1250 | } | ||
1251 | |||
1252 | |||
1253 | /* | ||
1254 | ** Does a major collection after last collection was a "bad collection". | ||
1255 | ** | ||
1256 | ** When the program is building a big structure, it allocates lots of | ||
1257 | ** memory but generates very little garbage. In those scenarios, | ||
1258 | ** the generational mode just wastes time doing small collections, and | ||
1259 | ** major collections are frequently what we call a "bad collection", a | ||
1260 | ** collection that frees too few objects. To avoid the cost of switching | ||
1261 | ** between generational mode and the incremental mode needed for full | ||
1262 | ** (major) collections, the collector tries to stay in incremental mode | ||
1263 | ** after a bad collection, and to switch back to generational mode only | ||
1264 | ** after a "good" collection (one that traverses less than 9/8 objects | ||
1265 | ** of the previous one). | ||
1266 | ** The collector must choose whether to stay in incremental mode or to | ||
1267 | ** switch back to generational mode before sweeping. At this point, it | ||
1268 | ** does not know the real memory in use, so it cannot use memory to | ||
1269 | ** decide whether to return to generational mode. Instead, it uses the | ||
1270 | ** number of objects traversed (returned by 'atomic') as a proxy. The | ||
1271 | ** field 'g->lastatomic' keeps this count from the last collection. | ||
1272 | ** ('g->lastatomic != 0' also means that the last collection was bad.) | ||
1273 | */ | ||
1274 | static void stepgenfull (lua_State *L, global_State *g) { | ||
1275 | lu_mem newatomic; /* count of traversed objects */ | ||
1276 | lu_mem lastatomic = g->lastatomic; /* count from last collection */ | ||
1277 | if (g->gckind == KGC_GEN) /* still in generational mode? */ | ||
1278 | enterinc(g); /* enter incremental mode */ | ||
1279 | luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */ | ||
1280 | newatomic = atomic(L); /* mark everybody */ | ||
1281 | if (newatomic < lastatomic + (lastatomic >> 3)) { /* good collection? */ | ||
1282 | atomic2gen(L, g); /* return to generational mode */ | ||
1283 | setminordebt(g); | ||
1284 | } | ||
1285 | else { /* another bad collection; stay in incremental mode */ | ||
1286 | g->GCestimate = gettotalbytes(g); /* first estimate */; | ||
1287 | entersweep(L); | ||
1288 | luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */ | ||
1289 | setpause(g); | ||
1290 | g->lastatomic = newatomic; | ||
1291 | } | ||
1292 | } | ||
1293 | |||
1294 | |||
1295 | /* | ||
1296 | ** Does a generational "step". | ||
1297 | ** Usually, this means doing a minor collection and setting the debt to | ||
1298 | ** make another collection when memory grows 'genminormul'% larger. | ||
1299 | ** | ||
1300 | ** However, there are exceptions. If memory grows 'genmajormul'% | ||
1301 | ** larger than it was at the end of the last major collection (kept | ||
1302 | ** in 'g->GCestimate'), the function does a major collection. At the | ||
1303 | ** end, it checks whether the major collection was able to free a | ||
1304 | ** decent amount of memory (at least half the growth in memory since | ||
1305 | ** previous major collection). If so, the collector keeps its state, | ||
1306 | ** and the next collection will probably be minor again. Otherwise, | ||
1307 | ** we have what we call a "bad collection". In that case, set the field | ||
1308 | ** 'g->lastatomic' to signal that fact, so that the next collection will | ||
1309 | ** go to 'stepgenfull'. | ||
1310 | ** | ||
1311 | ** 'GCdebt <= 0' means an explicit call to GC step with "size" zero; | ||
1312 | ** in that case, do a minor collection. | ||
1313 | */ | ||
1314 | static void genstep (lua_State *L, global_State *g) { | ||
1315 | if (g->lastatomic != 0) /* last collection was a bad one? */ | ||
1316 | stepgenfull(L, g); /* do a full step */ | ||
1317 | else { | ||
1318 | lu_mem majorbase = g->GCestimate; /* memory after last major collection */ | ||
1319 | lu_mem majorinc = (majorbase / 100) * getgcparam(g->genmajormul); | ||
1320 | if (g->GCdebt > 0 && gettotalbytes(g) > majorbase + majorinc) { | ||
1321 | lu_mem numobjs = fullgen(L, g); /* do a major collection */ | ||
1322 | if (gettotalbytes(g) < majorbase + (majorinc / 2)) { | ||
1323 | /* collected at least half of memory growth since last major | ||
1324 | collection; keep doing minor collections */ | ||
1325 | setminordebt(g); | ||
1326 | } | ||
1327 | else { /* bad collection */ | ||
1328 | g->lastatomic = numobjs; /* signal that last collection was bad */ | ||
1329 | setpause(g); /* do a long wait for next (major) collection */ | ||
1330 | } | ||
1331 | } | ||
1332 | else { /* regular case; do a minor collection */ | ||
1333 | youngcollection(L, g); | ||
1334 | setminordebt(g); | ||
1335 | g->GCestimate = majorbase; /* preserve base value */ | ||
1336 | } | ||
1337 | } | ||
1338 | lua_assert(isdecGCmodegen(g)); | ||
1339 | } | ||
1340 | |||
1341 | /* }====================================================== */ | ||
1342 | |||
1343 | |||
1344 | /* | ||
1345 | ** {====================================================== | ||
1346 | ** GC control | ||
1347 | ** ======================================================= | ||
1348 | */ | ||
1349 | |||
1350 | |||
1351 | /* | ||
1352 | ** Set the "time" to wait before starting a new GC cycle; cycle will | ||
1353 | ** start when memory use hits the threshold of ('estimate' * pause / | ||
1354 | ** PAUSEADJ). (Division by 'estimate' should be OK: it cannot be zero, | ||
1355 | ** because Lua cannot even start with less than PAUSEADJ bytes). | ||
1356 | */ | ||
1357 | static void setpause (global_State *g) { | ||
1358 | l_mem threshold, debt; | ||
1359 | int pause = getgcparam(g->gcpause); | ||
1360 | l_mem estimate = g->GCestimate / PAUSEADJ; /* adjust 'estimate' */ | ||
1361 | lua_assert(estimate > 0); | ||
1362 | threshold = (pause < MAX_LMEM / estimate) /* overflow? */ | ||
1363 | ? estimate * pause /* no overflow */ | ||
1364 | : MAX_LMEM; /* overflow; truncate to maximum */ | ||
1365 | debt = gettotalbytes(g) - threshold; | ||
1366 | if (debt > 0) debt = 0; | ||
1367 | luaE_setdebt(g, debt); | ||
1368 | } | ||
1369 | |||
1370 | |||
1371 | /* | ||
1372 | ** Enter first sweep phase. | ||
1373 | ** The call to 'sweeptolive' makes the pointer point to an object | ||
1374 | ** inside the list (instead of to the header), so that the real sweep do | ||
1375 | ** not need to skip objects created between "now" and the start of the | ||
1376 | ** real sweep. | ||
1377 | */ | ||
1378 | static void entersweep (lua_State *L) { | ||
1379 | global_State *g = G(L); | ||
1380 | g->gcstate = GCSswpallgc; | ||
1381 | lua_assert(g->sweepgc == NULL); | ||
1382 | g->sweepgc = sweeptolive(L, &g->allgc); | ||
1383 | } | ||
1384 | |||
1385 | |||
1386 | /* | ||
1387 | ** Delete all objects in list 'p' until (but not including) object | ||
1388 | ** 'limit'. | ||
1389 | */ | ||
1390 | static void deletelist (lua_State *L, GCObject *p, GCObject *limit) { | ||
1391 | while (p != limit) { | ||
1392 | GCObject *next = p->next; | ||
1393 | freeobj(L, p); | ||
1394 | p = next; | ||
1395 | } | ||
1396 | } | ||
1397 | |||
1398 | |||
1399 | /* | ||
1400 | ** Call all finalizers of the objects in the given Lua state, and | ||
1401 | ** then free all objects, except for the main thread. | ||
1402 | */ | ||
1403 | void luaC_freeallobjects (lua_State *L) { | ||
1404 | global_State *g = G(L); | ||
1405 | luaC_changemode(L, KGC_INC); | ||
1406 | separatetobefnz(g, 1); /* separate all objects with finalizers */ | ||
1407 | lua_assert(g->finobj == NULL); | ||
1408 | callallpendingfinalizers(L); | ||
1409 | deletelist(L, g->allgc, obj2gco(g->mainthread)); | ||
1410 | deletelist(L, g->finobj, NULL); | ||
1411 | deletelist(L, g->fixedgc, NULL); /* collect fixed objects */ | ||
1412 | lua_assert(g->strt.nuse == 0); | ||
1413 | } | ||
1414 | |||
1415 | |||
1416 | static lu_mem atomic (lua_State *L) { | ||
1417 | global_State *g = G(L); | ||
1418 | lu_mem work = 0; | ||
1419 | GCObject *origweak, *origall; | ||
1420 | GCObject *grayagain = g->grayagain; /* save original list */ | ||
1421 | g->grayagain = NULL; | ||
1422 | lua_assert(g->ephemeron == NULL && g->weak == NULL); | ||
1423 | lua_assert(!iswhite(g->mainthread)); | ||
1424 | g->gcstate = GCSatomic; | ||
1425 | markobject(g, L); /* mark running thread */ | ||
1426 | /* registry and global metatables may be changed by API */ | ||
1427 | markvalue(g, &g->l_registry); | ||
1428 | markmt(g); /* mark global metatables */ | ||
1429 | work += propagateall(g); /* empties 'gray' list */ | ||
1430 | /* remark occasional upvalues of (maybe) dead threads */ | ||
1431 | work += remarkupvals(g); | ||
1432 | work += propagateall(g); /* propagate changes */ | ||
1433 | g->gray = grayagain; | ||
1434 | work += propagateall(g); /* traverse 'grayagain' list */ | ||
1435 | convergeephemerons(g); | ||
1436 | /* at this point, all strongly accessible objects are marked. */ | ||
1437 | /* Clear values from weak tables, before checking finalizers */ | ||
1438 | clearbyvalues(g, g->weak, NULL); | ||
1439 | clearbyvalues(g, g->allweak, NULL); | ||
1440 | origweak = g->weak; origall = g->allweak; | ||
1441 | separatetobefnz(g, 0); /* separate objects to be finalized */ | ||
1442 | work += markbeingfnz(g); /* mark objects that will be finalized */ | ||
1443 | work += propagateall(g); /* remark, to propagate 'resurrection' */ | ||
1444 | convergeephemerons(g); | ||
1445 | /* at this point, all resurrected objects are marked. */ | ||
1446 | /* remove dead objects from weak tables */ | ||
1447 | clearbykeys(g, g->ephemeron); /* clear keys from all ephemeron tables */ | ||
1448 | clearbykeys(g, g->allweak); /* clear keys from all 'allweak' tables */ | ||
1449 | /* clear values from resurrected weak tables */ | ||
1450 | clearbyvalues(g, g->weak, origweak); | ||
1451 | clearbyvalues(g, g->allweak, origall); | ||
1452 | luaS_clearcache(g); | ||
1453 | g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */ | ||
1454 | lua_assert(g->gray == NULL); | ||
1455 | return work; /* estimate of slots marked by 'atomic' */ | ||
1456 | } | ||
1457 | |||
1458 | |||
1459 | static int sweepstep (lua_State *L, global_State *g, | ||
1460 | int nextstate, GCObject **nextlist) { | ||
1461 | if (g->sweepgc) { | ||
1462 | l_mem olddebt = g->GCdebt; | ||
1463 | int count; | ||
1464 | g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX, &count); | ||
1465 | g->GCestimate += g->GCdebt - olddebt; /* update estimate */ | ||
1466 | return count; | ||
1467 | } | ||
1468 | else { /* enter next state */ | ||
1469 | g->gcstate = nextstate; | ||
1470 | g->sweepgc = nextlist; | ||
1471 | return 0; /* no work done */ | ||
1472 | } | ||
1473 | } | ||
1474 | |||
1475 | |||
1476 | static lu_mem singlestep (lua_State *L) { | ||
1477 | global_State *g = G(L); | ||
1478 | switch (g->gcstate) { | ||
1479 | case GCSpause: { | ||
1480 | restartcollection(g); | ||
1481 | g->gcstate = GCSpropagate; | ||
1482 | return 1; | ||
1483 | } | ||
1484 | case GCSpropagate: { | ||
1485 | if (g->gray == NULL) { /* no more gray objects? */ | ||
1486 | g->gcstate = GCSenteratomic; /* finish propagate phase */ | ||
1487 | return 0; | ||
1488 | } | ||
1489 | else | ||
1490 | return propagatemark(g); /* traverse one gray object */ | ||
1491 | } | ||
1492 | case GCSenteratomic: { | ||
1493 | lu_mem work = atomic(L); /* work is what was traversed by 'atomic' */ | ||
1494 | entersweep(L); | ||
1495 | g->GCestimate = gettotalbytes(g); /* first estimate */; | ||
1496 | return work; | ||
1497 | } | ||
1498 | case GCSswpallgc: { /* sweep "regular" objects */ | ||
1499 | return sweepstep(L, g, GCSswpfinobj, &g->finobj); | ||
1500 | } | ||
1501 | case GCSswpfinobj: { /* sweep objects with finalizers */ | ||
1502 | return sweepstep(L, g, GCSswptobefnz, &g->tobefnz); | ||
1503 | } | ||
1504 | case GCSswptobefnz: { /* sweep objects to be finalized */ | ||
1505 | return sweepstep(L, g, GCSswpend, NULL); | ||
1506 | } | ||
1507 | case GCSswpend: { /* finish sweeps */ | ||
1508 | checkSizes(L, g); | ||
1509 | g->gcstate = GCScallfin; | ||
1510 | return 0; | ||
1511 | } | ||
1512 | case GCScallfin: { /* call remaining finalizers */ | ||
1513 | if (g->tobefnz && !g->gcemergency) { | ||
1514 | int n = runafewfinalizers(L, GCFINMAX); | ||
1515 | return n * GCFINALIZECOST; | ||
1516 | } | ||
1517 | else { /* emergency mode or no more finalizers */ | ||
1518 | g->gcstate = GCSpause; /* finish collection */ | ||
1519 | return 0; | ||
1520 | } | ||
1521 | } | ||
1522 | default: lua_assert(0); return 0; | ||
1523 | } | ||
1524 | } | ||
1525 | |||
1526 | |||
1527 | /* | ||
1528 | ** advances the garbage collector until it reaches a state allowed | ||
1529 | ** by 'statemask' | ||
1530 | */ | ||
1531 | void luaC_runtilstate (lua_State *L, int statesmask) { | ||
1532 | global_State *g = G(L); | ||
1533 | while (!testbit(statesmask, g->gcstate)) | ||
1534 | singlestep(L); | ||
1535 | } | ||
1536 | |||
1537 | |||
1538 | /* | ||
1539 | ** Performs a basic incremental step. The debt and step size are | ||
1540 | ** converted from bytes to "units of work"; then the function loops | ||
1541 | ** running single steps until adding that many units of work or | ||
1542 | ** finishing a cycle (pause state). Finally, it sets the debt that | ||
1543 | ** controls when next step will be performed. | ||
1544 | */ | ||
1545 | static void incstep (lua_State *L, global_State *g) { | ||
1546 | int stepmul = (getgcparam(g->gcstepmul) | 1); /* avoid division by 0 */ | ||
1547 | l_mem debt = (g->GCdebt / WORK2MEM) * stepmul; | ||
1548 | l_mem stepsize = (g->gcstepsize <= log2maxs(l_mem)) | ||
1549 | ? ((cast(l_mem, 1) << g->gcstepsize) / WORK2MEM) * stepmul | ||
1550 | : MAX_LMEM; /* overflow; keep maximum value */ | ||
1551 | do { /* repeat until pause or enough "credit" (negative debt) */ | ||
1552 | lu_mem work = singlestep(L); /* perform one single step */ | ||
1553 | debt -= work; | ||
1554 | } while (debt > -stepsize && g->gcstate != GCSpause); | ||
1555 | if (g->gcstate == GCSpause) | ||
1556 | setpause(g); /* pause until next cycle */ | ||
1557 | else { | ||
1558 | debt = (debt / stepmul) * WORK2MEM; /* convert 'work units' to bytes */ | ||
1559 | luaE_setdebt(g, debt); | ||
1560 | } | ||
1561 | } | ||
1562 | |||
1563 | /* | ||
1564 | ** performs a basic GC step if collector is running | ||
1565 | */ | ||
1566 | void luaC_step (lua_State *L) { | ||
1567 | global_State *g = G(L); | ||
1568 | lua_assert(!g->gcemergency); | ||
1569 | if (g->gcrunning) { /* running? */ | ||
1570 | if(isdecGCmodegen(g)) | ||
1571 | genstep(L, g); | ||
1572 | else | ||
1573 | incstep(L, g); | ||
1574 | } | ||
1575 | } | ||
1576 | |||
1577 | |||
1578 | /* | ||
1579 | ** Perform a full collection in incremental mode. | ||
1580 | ** Before running the collection, check 'keepinvariant'; if it is true, | ||
1581 | ** there may be some objects marked as black, so the collector has | ||
1582 | ** to sweep all objects to turn them back to white (as white has not | ||
1583 | ** changed, nothing will be collected). | ||
1584 | */ | ||
1585 | static void fullinc (lua_State *L, global_State *g) { | ||
1586 | if (keepinvariant(g)) /* black objects? */ | ||
1587 | entersweep(L); /* sweep everything to turn them back to white */ | ||
1588 | /* finish any pending sweep phase to start a new cycle */ | ||
1589 | luaC_runtilstate(L, bitmask(GCSpause)); | ||
1590 | luaC_runtilstate(L, bitmask(GCScallfin)); /* run up to finalizers */ | ||
1591 | /* estimate must be correct after a full GC cycle */ | ||
1592 | lua_assert(g->GCestimate == gettotalbytes(g)); | ||
1593 | luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */ | ||
1594 | setpause(g); | ||
1595 | } | ||
1596 | |||
1597 | |||
1598 | /* | ||
1599 | ** Performs a full GC cycle; if 'isemergency', set a flag to avoid | ||
1600 | ** some operations which could change the interpreter state in some | ||
1601 | ** unexpected ways (running finalizers and shrinking some structures). | ||
1602 | */ | ||
1603 | void luaC_fullgc (lua_State *L, int isemergency) { | ||
1604 | global_State *g = G(L); | ||
1605 | lua_assert(!g->gcemergency); | ||
1606 | g->gcemergency = isemergency; /* set flag */ | ||
1607 | if (g->gckind == KGC_INC) | ||
1608 | fullinc(L, g); | ||
1609 | else | ||
1610 | fullgen(L, g); | ||
1611 | g->gcemergency = 0; | ||
1612 | } | ||
1613 | |||
1614 | /* }====================================================== */ | ||
1615 | |||
1616 | |||