diff options
author | Li Jin <dragon-fly@qq.com> | 2022-10-31 11:32:33 +0800 |
---|---|---|
committer | Li Jin <dragon-fly@qq.com> | 2022-11-09 11:29:32 +0800 |
commit | 417ec1a37922c6178900adfec70628cad46731ff (patch) | |
tree | a5a2d74927ad2c41b5a16264a78409e1c0334b72 /win-build/Lua53/lgc.c | |
parent | 3dd607c8887d2fe0186668aabca31bb84a41e2da (diff) | |
download | yuescript-417ec1a37922c6178900adfec70628cad46731ff.tar.gz yuescript-417ec1a37922c6178900adfec70628cad46731ff.tar.bz2 yuescript-417ec1a37922c6178900adfec70628cad46731ff.zip |
fix issue #112 and issue #113.
Diffstat (limited to 'win-build/Lua53/lgc.c')
-rw-r--r-- | win-build/Lua53/lgc.c | 1179 |
1 files changed, 1179 insertions, 0 deletions
diff --git a/win-build/Lua53/lgc.c b/win-build/Lua53/lgc.c new file mode 100644 index 0000000..db4df82 --- /dev/null +++ b/win-build/Lua53/lgc.c | |||
@@ -0,0 +1,1179 @@ | |||
1 | /* | ||
2 | ** $Id: lgc.c,v 2.215.1.2 2017/08/31 16:15:27 roberto Exp $ | ||
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 | |||
13 | #include <string.h> | ||
14 | |||
15 | #include "lua.h" | ||
16 | |||
17 | #include "ldebug.h" | ||
18 | #include "ldo.h" | ||
19 | #include "lfunc.h" | ||
20 | #include "lgc.h" | ||
21 | #include "lmem.h" | ||
22 | #include "lobject.h" | ||
23 | #include "lstate.h" | ||
24 | #include "lstring.h" | ||
25 | #include "ltable.h" | ||
26 | #include "ltm.h" | ||
27 | |||
28 | |||
29 | /* | ||
30 | ** internal state for collector while inside the atomic phase. The | ||
31 | ** collector should never be in this state while running regular code. | ||
32 | */ | ||
33 | #define GCSinsideatomic (GCSpause + 1) | ||
34 | |||
35 | /* | ||
36 | ** cost of sweeping one element (the size of a small object divided | ||
37 | ** by some adjust for the sweep speed) | ||
38 | */ | ||
39 | #define GCSWEEPCOST ((sizeof(TString) + 4) / 4) | ||
40 | |||
41 | /* maximum number of elements to sweep in each single step */ | ||
42 | #define GCSWEEPMAX (cast_int((GCSTEPSIZE / GCSWEEPCOST) / 4)) | ||
43 | |||
44 | /* cost of calling one finalizer */ | ||
45 | #define GCFINALIZECOST GCSWEEPCOST | ||
46 | |||
47 | |||
48 | /* | ||
49 | ** macro to adjust 'stepmul': 'stepmul' is actually used like | ||
50 | ** 'stepmul / STEPMULADJ' (value chosen by tests) | ||
51 | */ | ||
52 | #define STEPMULADJ 200 | ||
53 | |||
54 | |||
55 | /* | ||
56 | ** macro to adjust 'pause': 'pause' is actually used like | ||
57 | ** 'pause / PAUSEADJ' (value chosen by tests) | ||
58 | */ | ||
59 | #define PAUSEADJ 100 | ||
60 | |||
61 | |||
62 | /* | ||
63 | ** 'makewhite' erases all color bits then sets only the current white | ||
64 | ** bit | ||
65 | */ | ||
66 | #define maskcolors (~(bitmask(BLACKBIT) | WHITEBITS)) | ||
67 | #define makewhite(g,x) \ | ||
68 | (x->marked = cast_byte((x->marked & maskcolors) | luaC_white(g))) | ||
69 | |||
70 | #define white2gray(x) resetbits(x->marked, WHITEBITS) | ||
71 | #define black2gray(x) resetbit(x->marked, BLACKBIT) | ||
72 | |||
73 | |||
74 | #define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x))) | ||
75 | |||
76 | #define checkdeadkey(n) lua_assert(!ttisdeadkey(gkey(n)) || ttisnil(gval(n))) | ||
77 | |||
78 | |||
79 | #define checkconsistency(obj) \ | ||
80 | lua_longassert(!iscollectable(obj) || righttt(obj)) | ||
81 | |||
82 | |||
83 | #define markvalue(g,o) { checkconsistency(o); \ | ||
84 | if (valiswhite(o)) reallymarkobject(g,gcvalue(o)); } | ||
85 | |||
86 | #define markobject(g,t) { if (iswhite(t)) reallymarkobject(g, obj2gco(t)); } | ||
87 | |||
88 | /* | ||
89 | ** mark an object that can be NULL (either because it is really optional, | ||
90 | ** or it was stripped as debug info, or inside an uncompleted structure) | ||
91 | */ | ||
92 | #define markobjectN(g,t) { if (t) markobject(g,t); } | ||
93 | |||
94 | static void reallymarkobject (global_State *g, GCObject *o); | ||
95 | |||
96 | |||
97 | /* | ||
98 | ** {====================================================== | ||
99 | ** Generic functions | ||
100 | ** ======================================================= | ||
101 | */ | ||
102 | |||
103 | |||
104 | /* | ||
105 | ** one after last element in a hash array | ||
106 | */ | ||
107 | #define gnodelast(h) gnode(h, cast(size_t, sizenode(h))) | ||
108 | |||
109 | |||
110 | /* | ||
111 | ** link collectable object 'o' into list pointed by 'p' | ||
112 | */ | ||
113 | #define linkgclist(o,p) ((o)->gclist = (p), (p) = obj2gco(o)) | ||
114 | |||
115 | |||
116 | /* | ||
117 | ** If key is not marked, mark its entry as dead. This allows key to be | ||
118 | ** collected, but keeps its entry in the table. A dead node is needed | ||
119 | ** when Lua looks up for a key (it may be part of a chain) and when | ||
120 | ** traversing a weak table (key might be removed from the table during | ||
121 | ** traversal). Other places never manipulate dead keys, because its | ||
122 | ** associated nil value is enough to signal that the entry is logically | ||
123 | ** empty. | ||
124 | */ | ||
125 | static void removeentry (Node *n) { | ||
126 | lua_assert(ttisnil(gval(n))); | ||
127 | if (valiswhite(gkey(n))) | ||
128 | setdeadvalue(wgkey(n)); /* unused and unmarked key; remove it */ | ||
129 | } | ||
130 | |||
131 | |||
132 | /* | ||
133 | ** tells whether a key or value can be cleared from a weak | ||
134 | ** table. Non-collectable objects are never removed from weak | ||
135 | ** tables. Strings behave as 'values', so are never removed too. for | ||
136 | ** other objects: if really collected, cannot keep them; for objects | ||
137 | ** being finalized, keep them in keys, but not in values | ||
138 | */ | ||
139 | static int iscleared (global_State *g, const TValue *o) { | ||
140 | if (!iscollectable(o)) return 0; | ||
141 | else if (ttisstring(o)) { | ||
142 | markobject(g, tsvalue(o)); /* strings are 'values', so are never weak */ | ||
143 | return 0; | ||
144 | } | ||
145 | else return iswhite(gcvalue(o)); | ||
146 | } | ||
147 | |||
148 | |||
149 | /* | ||
150 | ** barrier that moves collector forward, that is, mark the white object | ||
151 | ** being pointed by a black object. (If in sweep phase, clear the black | ||
152 | ** object to white [sweep it] to avoid other barrier calls for this | ||
153 | ** same object.) | ||
154 | */ | ||
155 | void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) { | ||
156 | global_State *g = G(L); | ||
157 | lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o)); | ||
158 | if (keepinvariant(g)) /* must keep invariant? */ | ||
159 | reallymarkobject(g, v); /* restore invariant */ | ||
160 | else { /* sweep phase */ | ||
161 | lua_assert(issweepphase(g)); | ||
162 | makewhite(g, o); /* mark main obj. as white to avoid other barriers */ | ||
163 | } | ||
164 | } | ||
165 | |||
166 | |||
167 | /* | ||
168 | ** barrier that moves collector backward, that is, mark the black object | ||
169 | ** pointing to a white object as gray again. | ||
170 | */ | ||
171 | void luaC_barrierback_ (lua_State *L, Table *t) { | ||
172 | global_State *g = G(L); | ||
173 | lua_assert(isblack(t) && !isdead(g, t)); | ||
174 | black2gray(t); /* make table gray (again) */ | ||
175 | linkgclist(t, g->grayagain); | ||
176 | } | ||
177 | |||
178 | |||
179 | /* | ||
180 | ** barrier for assignments to closed upvalues. Because upvalues are | ||
181 | ** shared among closures, it is impossible to know the color of all | ||
182 | ** closures pointing to it. So, we assume that the object being assigned | ||
183 | ** must be marked. | ||
184 | */ | ||
185 | void luaC_upvalbarrier_ (lua_State *L, UpVal *uv) { | ||
186 | global_State *g = G(L); | ||
187 | GCObject *o = gcvalue(uv->v); | ||
188 | lua_assert(!upisopen(uv)); /* ensured by macro luaC_upvalbarrier */ | ||
189 | if (keepinvariant(g)) | ||
190 | markobject(g, o); | ||
191 | } | ||
192 | |||
193 | |||
194 | void luaC_fix (lua_State *L, GCObject *o) { | ||
195 | global_State *g = G(L); | ||
196 | lua_assert(g->allgc == o); /* object must be 1st in 'allgc' list! */ | ||
197 | white2gray(o); /* they will be gray forever */ | ||
198 | g->allgc = o->next; /* remove object from 'allgc' list */ | ||
199 | o->next = g->fixedgc; /* link it to 'fixedgc' list */ | ||
200 | g->fixedgc = o; | ||
201 | } | ||
202 | |||
203 | |||
204 | /* | ||
205 | ** create a new collectable object (with given type and size) and link | ||
206 | ** it to 'allgc' list. | ||
207 | */ | ||
208 | GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) { | ||
209 | global_State *g = G(L); | ||
210 | GCObject *o = cast(GCObject *, luaM_newobject(L, novariant(tt), sz)); | ||
211 | o->marked = luaC_white(g); | ||
212 | o->tt = tt; | ||
213 | o->next = g->allgc; | ||
214 | g->allgc = o; | ||
215 | return o; | ||
216 | } | ||
217 | |||
218 | /* }====================================================== */ | ||
219 | |||
220 | |||
221 | |||
222 | /* | ||
223 | ** {====================================================== | ||
224 | ** Mark functions | ||
225 | ** ======================================================= | ||
226 | */ | ||
227 | |||
228 | |||
229 | /* | ||
230 | ** mark an object. Userdata, strings, and closed upvalues are visited | ||
231 | ** and turned black here. Other objects are marked gray and added | ||
232 | ** to appropriate list to be visited (and turned black) later. (Open | ||
233 | ** upvalues are already linked in 'headuv' list.) | ||
234 | */ | ||
235 | static void reallymarkobject (global_State *g, GCObject *o) { | ||
236 | reentry: | ||
237 | white2gray(o); | ||
238 | switch (o->tt) { | ||
239 | case LUA_TSHRSTR: { | ||
240 | gray2black(o); | ||
241 | g->GCmemtrav += sizelstring(gco2ts(o)->shrlen); | ||
242 | break; | ||
243 | } | ||
244 | case LUA_TLNGSTR: { | ||
245 | gray2black(o); | ||
246 | g->GCmemtrav += sizelstring(gco2ts(o)->u.lnglen); | ||
247 | break; | ||
248 | } | ||
249 | case LUA_TUSERDATA: { | ||
250 | TValue uvalue; | ||
251 | markobjectN(g, gco2u(o)->metatable); /* mark its metatable */ | ||
252 | gray2black(o); | ||
253 | g->GCmemtrav += sizeudata(gco2u(o)); | ||
254 | getuservalue(g->mainthread, gco2u(o), &uvalue); | ||
255 | if (valiswhite(&uvalue)) { /* markvalue(g, &uvalue); */ | ||
256 | o = gcvalue(&uvalue); | ||
257 | goto reentry; | ||
258 | } | ||
259 | break; | ||
260 | } | ||
261 | case LUA_TLCL: { | ||
262 | linkgclist(gco2lcl(o), g->gray); | ||
263 | break; | ||
264 | } | ||
265 | case LUA_TCCL: { | ||
266 | linkgclist(gco2ccl(o), g->gray); | ||
267 | break; | ||
268 | } | ||
269 | case LUA_TTABLE: { | ||
270 | linkgclist(gco2t(o), g->gray); | ||
271 | break; | ||
272 | } | ||
273 | case LUA_TTHREAD: { | ||
274 | linkgclist(gco2th(o), g->gray); | ||
275 | break; | ||
276 | } | ||
277 | case LUA_TPROTO: { | ||
278 | linkgclist(gco2p(o), g->gray); | ||
279 | break; | ||
280 | } | ||
281 | default: lua_assert(0); break; | ||
282 | } | ||
283 | } | ||
284 | |||
285 | |||
286 | /* | ||
287 | ** mark metamethods for basic types | ||
288 | */ | ||
289 | static void markmt (global_State *g) { | ||
290 | int i; | ||
291 | for (i=0; i < LUA_NUMTAGS; i++) | ||
292 | markobjectN(g, g->mt[i]); | ||
293 | } | ||
294 | |||
295 | |||
296 | /* | ||
297 | ** mark all objects in list of being-finalized | ||
298 | */ | ||
299 | static void markbeingfnz (global_State *g) { | ||
300 | GCObject *o; | ||
301 | for (o = g->tobefnz; o != NULL; o = o->next) | ||
302 | markobject(g, o); | ||
303 | } | ||
304 | |||
305 | |||
306 | /* | ||
307 | ** Mark all values stored in marked open upvalues from non-marked threads. | ||
308 | ** (Values from marked threads were already marked when traversing the | ||
309 | ** thread.) Remove from the list threads that no longer have upvalues and | ||
310 | ** not-marked threads. | ||
311 | */ | ||
312 | static void remarkupvals (global_State *g) { | ||
313 | lua_State *thread; | ||
314 | lua_State **p = &g->twups; | ||
315 | while ((thread = *p) != NULL) { | ||
316 | lua_assert(!isblack(thread)); /* threads are never black */ | ||
317 | if (isgray(thread) && thread->openupval != NULL) | ||
318 | p = &thread->twups; /* keep marked thread with upvalues in the list */ | ||
319 | else { /* thread is not marked or without upvalues */ | ||
320 | UpVal *uv; | ||
321 | *p = thread->twups; /* remove thread from the list */ | ||
322 | thread->twups = thread; /* mark that it is out of list */ | ||
323 | for (uv = thread->openupval; uv != NULL; uv = uv->u.open.next) { | ||
324 | if (uv->u.open.touched) { | ||
325 | markvalue(g, uv->v); /* remark upvalue's value */ | ||
326 | uv->u.open.touched = 0; | ||
327 | } | ||
328 | } | ||
329 | } | ||
330 | } | ||
331 | } | ||
332 | |||
333 | |||
334 | /* | ||
335 | ** mark root set and reset all gray lists, to start a new collection | ||
336 | */ | ||
337 | static void restartcollection (global_State *g) { | ||
338 | g->gray = g->grayagain = NULL; | ||
339 | g->weak = g->allweak = g->ephemeron = NULL; | ||
340 | markobject(g, g->mainthread); | ||
341 | markvalue(g, &g->l_registry); | ||
342 | markmt(g); | ||
343 | markbeingfnz(g); /* mark any finalizing object left from previous cycle */ | ||
344 | } | ||
345 | |||
346 | /* }====================================================== */ | ||
347 | |||
348 | |||
349 | /* | ||
350 | ** {====================================================== | ||
351 | ** Traverse functions | ||
352 | ** ======================================================= | ||
353 | */ | ||
354 | |||
355 | /* | ||
356 | ** Traverse a table with weak values and link it to proper list. During | ||
357 | ** propagate phase, keep it in 'grayagain' list, to be revisited in the | ||
358 | ** atomic phase. In the atomic phase, if table has any white value, | ||
359 | ** put it in 'weak' list, to be cleared. | ||
360 | */ | ||
361 | static void traverseweakvalue (global_State *g, Table *h) { | ||
362 | Node *n, *limit = gnodelast(h); | ||
363 | /* if there is array part, assume it may have white values (it is not | ||
364 | worth traversing it now just to check) */ | ||
365 | int hasclears = (h->sizearray > 0); | ||
366 | for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */ | ||
367 | checkdeadkey(n); | ||
368 | if (ttisnil(gval(n))) /* entry is empty? */ | ||
369 | removeentry(n); /* remove it */ | ||
370 | else { | ||
371 | lua_assert(!ttisnil(gkey(n))); | ||
372 | markvalue(g, gkey(n)); /* mark key */ | ||
373 | if (!hasclears && iscleared(g, gval(n))) /* is there a white value? */ | ||
374 | hasclears = 1; /* table will have to be cleared */ | ||
375 | } | ||
376 | } | ||
377 | if (g->gcstate == GCSpropagate) | ||
378 | linkgclist(h, g->grayagain); /* must retraverse it in atomic phase */ | ||
379 | else if (hasclears) | ||
380 | linkgclist(h, g->weak); /* has to be cleared later */ | ||
381 | } | ||
382 | |||
383 | |||
384 | /* | ||
385 | ** Traverse an ephemeron table and link it to proper list. Returns true | ||
386 | ** iff any object was marked during this traversal (which implies that | ||
387 | ** convergence has to continue). During propagation phase, keep table | ||
388 | ** in 'grayagain' list, to be visited again in the atomic phase. In | ||
389 | ** the atomic phase, if table has any white->white entry, it has to | ||
390 | ** be revisited during ephemeron convergence (as that key may turn | ||
391 | ** black). Otherwise, if it has any white key, table has to be cleared | ||
392 | ** (in the atomic phase). | ||
393 | */ | ||
394 | static int traverseephemeron (global_State *g, Table *h) { | ||
395 | int marked = 0; /* true if an object is marked in this traversal */ | ||
396 | int hasclears = 0; /* true if table has white keys */ | ||
397 | int hasww = 0; /* true if table has entry "white-key -> white-value" */ | ||
398 | Node *n, *limit = gnodelast(h); | ||
399 | unsigned int i; | ||
400 | /* traverse array part */ | ||
401 | for (i = 0; i < h->sizearray; i++) { | ||
402 | if (valiswhite(&h->array[i])) { | ||
403 | marked = 1; | ||
404 | reallymarkobject(g, gcvalue(&h->array[i])); | ||
405 | } | ||
406 | } | ||
407 | /* traverse hash part */ | ||
408 | for (n = gnode(h, 0); n < limit; n++) { | ||
409 | checkdeadkey(n); | ||
410 | if (ttisnil(gval(n))) /* entry is empty? */ | ||
411 | removeentry(n); /* remove it */ | ||
412 | else if (iscleared(g, gkey(n))) { /* key is not marked (yet)? */ | ||
413 | hasclears = 1; /* table must be cleared */ | ||
414 | if (valiswhite(gval(n))) /* value not marked yet? */ | ||
415 | hasww = 1; /* white-white entry */ | ||
416 | } | ||
417 | else if (valiswhite(gval(n))) { /* value not marked yet? */ | ||
418 | marked = 1; | ||
419 | reallymarkobject(g, gcvalue(gval(n))); /* mark it now */ | ||
420 | } | ||
421 | } | ||
422 | /* link table into proper list */ | ||
423 | if (g->gcstate == GCSpropagate) | ||
424 | linkgclist(h, g->grayagain); /* must retraverse it in atomic phase */ | ||
425 | else if (hasww) /* table has white->white entries? */ | ||
426 | linkgclist(h, g->ephemeron); /* have to propagate again */ | ||
427 | else if (hasclears) /* table has white keys? */ | ||
428 | linkgclist(h, g->allweak); /* may have to clean white keys */ | ||
429 | return marked; | ||
430 | } | ||
431 | |||
432 | |||
433 | static void traversestrongtable (global_State *g, Table *h) { | ||
434 | Node *n, *limit = gnodelast(h); | ||
435 | unsigned int i; | ||
436 | for (i = 0; i < h->sizearray; i++) /* traverse array part */ | ||
437 | markvalue(g, &h->array[i]); | ||
438 | for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */ | ||
439 | checkdeadkey(n); | ||
440 | if (ttisnil(gval(n))) /* entry is empty? */ | ||
441 | removeentry(n); /* remove it */ | ||
442 | else { | ||
443 | lua_assert(!ttisnil(gkey(n))); | ||
444 | markvalue(g, gkey(n)); /* mark key */ | ||
445 | markvalue(g, gval(n)); /* mark value */ | ||
446 | } | ||
447 | } | ||
448 | } | ||
449 | |||
450 | |||
451 | static lu_mem traversetable (global_State *g, Table *h) { | ||
452 | const char *weakkey, *weakvalue; | ||
453 | const TValue *mode = gfasttm(g, h->metatable, TM_MODE); | ||
454 | markobjectN(g, h->metatable); | ||
455 | if (mode && ttisstring(mode) && /* is there a weak mode? */ | ||
456 | ((weakkey = strchr(svalue(mode), 'k')), | ||
457 | (weakvalue = strchr(svalue(mode), 'v')), | ||
458 | (weakkey || weakvalue))) { /* is really weak? */ | ||
459 | black2gray(h); /* keep table gray */ | ||
460 | if (!weakkey) /* strong keys? */ | ||
461 | traverseweakvalue(g, h); | ||
462 | else if (!weakvalue) /* strong values? */ | ||
463 | traverseephemeron(g, h); | ||
464 | else /* all weak */ | ||
465 | linkgclist(h, g->allweak); /* nothing to traverse now */ | ||
466 | } | ||
467 | else /* not weak */ | ||
468 | traversestrongtable(g, h); | ||
469 | return sizeof(Table) + sizeof(TValue) * h->sizearray + | ||
470 | sizeof(Node) * cast(size_t, allocsizenode(h)); | ||
471 | } | ||
472 | |||
473 | |||
474 | /* | ||
475 | ** Traverse a prototype. (While a prototype is being build, its | ||
476 | ** arrays can be larger than needed; the extra slots are filled with | ||
477 | ** NULL, so the use of 'markobjectN') | ||
478 | */ | ||
479 | static int traverseproto (global_State *g, Proto *f) { | ||
480 | int i; | ||
481 | if (f->cache && iswhite(f->cache)) | ||
482 | f->cache = NULL; /* allow cache to be collected */ | ||
483 | markobjectN(g, f->source); | ||
484 | for (i = 0; i < f->sizek; i++) /* mark literals */ | ||
485 | markvalue(g, &f->k[i]); | ||
486 | for (i = 0; i < f->sizeupvalues; i++) /* mark upvalue names */ | ||
487 | markobjectN(g, f->upvalues[i].name); | ||
488 | for (i = 0; i < f->sizep; i++) /* mark nested protos */ | ||
489 | markobjectN(g, f->p[i]); | ||
490 | for (i = 0; i < f->sizelocvars; i++) /* mark local-variable names */ | ||
491 | markobjectN(g, f->locvars[i].varname); | ||
492 | return sizeof(Proto) + sizeof(Instruction) * f->sizecode + | ||
493 | sizeof(Proto *) * f->sizep + | ||
494 | sizeof(TValue) * f->sizek + | ||
495 | sizeof(int) * f->sizelineinfo + | ||
496 | sizeof(LocVar) * f->sizelocvars + | ||
497 | sizeof(Upvaldesc) * f->sizeupvalues; | ||
498 | } | ||
499 | |||
500 | |||
501 | static lu_mem traverseCclosure (global_State *g, CClosure *cl) { | ||
502 | int i; | ||
503 | for (i = 0; i < cl->nupvalues; i++) /* mark its upvalues */ | ||
504 | markvalue(g, &cl->upvalue[i]); | ||
505 | return sizeCclosure(cl->nupvalues); | ||
506 | } | ||
507 | |||
508 | /* | ||
509 | ** open upvalues point to values in a thread, so those values should | ||
510 | ** be marked when the thread is traversed except in the atomic phase | ||
511 | ** (because then the value cannot be changed by the thread and the | ||
512 | ** thread may not be traversed again) | ||
513 | */ | ||
514 | static lu_mem traverseLclosure (global_State *g, LClosure *cl) { | ||
515 | int i; | ||
516 | markobjectN(g, cl->p); /* mark its prototype */ | ||
517 | for (i = 0; i < cl->nupvalues; i++) { /* mark its upvalues */ | ||
518 | UpVal *uv = cl->upvals[i]; | ||
519 | if (uv != NULL) { | ||
520 | if (upisopen(uv) && g->gcstate != GCSinsideatomic) | ||
521 | uv->u.open.touched = 1; /* can be marked in 'remarkupvals' */ | ||
522 | else | ||
523 | markvalue(g, uv->v); | ||
524 | } | ||
525 | } | ||
526 | return sizeLclosure(cl->nupvalues); | ||
527 | } | ||
528 | |||
529 | |||
530 | static lu_mem traversethread (global_State *g, lua_State *th) { | ||
531 | StkId o = th->stack; | ||
532 | if (o == NULL) | ||
533 | return 1; /* stack not completely built yet */ | ||
534 | lua_assert(g->gcstate == GCSinsideatomic || | ||
535 | th->openupval == NULL || isintwups(th)); | ||
536 | for (; o < th->top; o++) /* mark live elements in the stack */ | ||
537 | markvalue(g, o); | ||
538 | if (g->gcstate == GCSinsideatomic) { /* final traversal? */ | ||
539 | StkId lim = th->stack + th->stacksize; /* real end of stack */ | ||
540 | for (; o < lim; o++) /* clear not-marked stack slice */ | ||
541 | setnilvalue(o); | ||
542 | /* 'remarkupvals' may have removed thread from 'twups' list */ | ||
543 | if (!isintwups(th) && th->openupval != NULL) { | ||
544 | th->twups = g->twups; /* link it back to the list */ | ||
545 | g->twups = th; | ||
546 | } | ||
547 | } | ||
548 | else if (g->gckind != KGC_EMERGENCY) | ||
549 | luaD_shrinkstack(th); /* do not change stack in emergency cycle */ | ||
550 | return (sizeof(lua_State) + sizeof(TValue) * th->stacksize + | ||
551 | sizeof(CallInfo) * th->nci); | ||
552 | } | ||
553 | |||
554 | |||
555 | /* | ||
556 | ** traverse one gray object, turning it to black (except for threads, | ||
557 | ** which are always gray). | ||
558 | */ | ||
559 | static void propagatemark (global_State *g) { | ||
560 | lu_mem size; | ||
561 | GCObject *o = g->gray; | ||
562 | lua_assert(isgray(o)); | ||
563 | gray2black(o); | ||
564 | switch (o->tt) { | ||
565 | case LUA_TTABLE: { | ||
566 | Table *h = gco2t(o); | ||
567 | g->gray = h->gclist; /* remove from 'gray' list */ | ||
568 | size = traversetable(g, h); | ||
569 | break; | ||
570 | } | ||
571 | case LUA_TLCL: { | ||
572 | LClosure *cl = gco2lcl(o); | ||
573 | g->gray = cl->gclist; /* remove from 'gray' list */ | ||
574 | size = traverseLclosure(g, cl); | ||
575 | break; | ||
576 | } | ||
577 | case LUA_TCCL: { | ||
578 | CClosure *cl = gco2ccl(o); | ||
579 | g->gray = cl->gclist; /* remove from 'gray' list */ | ||
580 | size = traverseCclosure(g, cl); | ||
581 | break; | ||
582 | } | ||
583 | case LUA_TTHREAD: { | ||
584 | lua_State *th = gco2th(o); | ||
585 | g->gray = th->gclist; /* remove from 'gray' list */ | ||
586 | linkgclist(th, g->grayagain); /* insert into 'grayagain' list */ | ||
587 | black2gray(o); | ||
588 | size = traversethread(g, th); | ||
589 | break; | ||
590 | } | ||
591 | case LUA_TPROTO: { | ||
592 | Proto *p = gco2p(o); | ||
593 | g->gray = p->gclist; /* remove from 'gray' list */ | ||
594 | size = traverseproto(g, p); | ||
595 | break; | ||
596 | } | ||
597 | default: lua_assert(0); return; | ||
598 | } | ||
599 | g->GCmemtrav += size; | ||
600 | } | ||
601 | |||
602 | |||
603 | static void propagateall (global_State *g) { | ||
604 | while (g->gray) propagatemark(g); | ||
605 | } | ||
606 | |||
607 | |||
608 | static void convergeephemerons (global_State *g) { | ||
609 | int changed; | ||
610 | do { | ||
611 | GCObject *w; | ||
612 | GCObject *next = g->ephemeron; /* get ephemeron list */ | ||
613 | g->ephemeron = NULL; /* tables may return to this list when traversed */ | ||
614 | changed = 0; | ||
615 | while ((w = next) != NULL) { | ||
616 | next = gco2t(w)->gclist; | ||
617 | if (traverseephemeron(g, gco2t(w))) { /* traverse marked some value? */ | ||
618 | propagateall(g); /* propagate changes */ | ||
619 | changed = 1; /* will have to revisit all ephemeron tables */ | ||
620 | } | ||
621 | } | ||
622 | } while (changed); | ||
623 | } | ||
624 | |||
625 | /* }====================================================== */ | ||
626 | |||
627 | |||
628 | /* | ||
629 | ** {====================================================== | ||
630 | ** Sweep Functions | ||
631 | ** ======================================================= | ||
632 | */ | ||
633 | |||
634 | |||
635 | /* | ||
636 | ** clear entries with unmarked keys from all weaktables in list 'l' up | ||
637 | ** to element 'f' | ||
638 | */ | ||
639 | static void clearkeys (global_State *g, GCObject *l, GCObject *f) { | ||
640 | for (; l != f; l = gco2t(l)->gclist) { | ||
641 | Table *h = gco2t(l); | ||
642 | Node *n, *limit = gnodelast(h); | ||
643 | for (n = gnode(h, 0); n < limit; n++) { | ||
644 | if (!ttisnil(gval(n)) && (iscleared(g, gkey(n)))) { | ||
645 | setnilvalue(gval(n)); /* remove value ... */ | ||
646 | } | ||
647 | if (ttisnil(gval(n))) /* is entry empty? */ | ||
648 | removeentry(n); /* remove entry from table */ | ||
649 | } | ||
650 | } | ||
651 | } | ||
652 | |||
653 | |||
654 | /* | ||
655 | ** clear entries with unmarked values from all weaktables in list 'l' up | ||
656 | ** to element 'f' | ||
657 | */ | ||
658 | static void clearvalues (global_State *g, GCObject *l, GCObject *f) { | ||
659 | for (; l != f; l = gco2t(l)->gclist) { | ||
660 | Table *h = gco2t(l); | ||
661 | Node *n, *limit = gnodelast(h); | ||
662 | unsigned int i; | ||
663 | for (i = 0; i < h->sizearray; i++) { | ||
664 | TValue *o = &h->array[i]; | ||
665 | if (iscleared(g, o)) /* value was collected? */ | ||
666 | setnilvalue(o); /* remove value */ | ||
667 | } | ||
668 | for (n = gnode(h, 0); n < limit; n++) { | ||
669 | if (!ttisnil(gval(n)) && iscleared(g, gval(n))) { | ||
670 | setnilvalue(gval(n)); /* remove value ... */ | ||
671 | removeentry(n); /* and remove entry from table */ | ||
672 | } | ||
673 | } | ||
674 | } | ||
675 | } | ||
676 | |||
677 | |||
678 | void luaC_upvdeccount (lua_State *L, UpVal *uv) { | ||
679 | lua_assert(uv->refcount > 0); | ||
680 | uv->refcount--; | ||
681 | if (uv->refcount == 0 && !upisopen(uv)) | ||
682 | luaM_free(L, uv); | ||
683 | } | ||
684 | |||
685 | |||
686 | static void freeLclosure (lua_State *L, LClosure *cl) { | ||
687 | int i; | ||
688 | for (i = 0; i < cl->nupvalues; i++) { | ||
689 | UpVal *uv = cl->upvals[i]; | ||
690 | if (uv) | ||
691 | luaC_upvdeccount(L, uv); | ||
692 | } | ||
693 | luaM_freemem(L, cl, sizeLclosure(cl->nupvalues)); | ||
694 | } | ||
695 | |||
696 | |||
697 | static void freeobj (lua_State *L, GCObject *o) { | ||
698 | switch (o->tt) { | ||
699 | case LUA_TPROTO: luaF_freeproto(L, gco2p(o)); break; | ||
700 | case LUA_TLCL: { | ||
701 | freeLclosure(L, gco2lcl(o)); | ||
702 | break; | ||
703 | } | ||
704 | case LUA_TCCL: { | ||
705 | luaM_freemem(L, o, sizeCclosure(gco2ccl(o)->nupvalues)); | ||
706 | break; | ||
707 | } | ||
708 | case LUA_TTABLE: luaH_free(L, gco2t(o)); break; | ||
709 | case LUA_TTHREAD: luaE_freethread(L, gco2th(o)); break; | ||
710 | case LUA_TUSERDATA: luaM_freemem(L, o, sizeudata(gco2u(o))); break; | ||
711 | case LUA_TSHRSTR: | ||
712 | luaS_remove(L, gco2ts(o)); /* remove it from hash table */ | ||
713 | luaM_freemem(L, o, sizelstring(gco2ts(o)->shrlen)); | ||
714 | break; | ||
715 | case LUA_TLNGSTR: { | ||
716 | luaM_freemem(L, o, sizelstring(gco2ts(o)->u.lnglen)); | ||
717 | break; | ||
718 | } | ||
719 | default: lua_assert(0); | ||
720 | } | ||
721 | } | ||
722 | |||
723 | |||
724 | #define sweepwholelist(L,p) sweeplist(L,p,MAX_LUMEM) | ||
725 | static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count); | ||
726 | |||
727 | |||
728 | /* | ||
729 | ** sweep at most 'count' elements from a list of GCObjects erasing dead | ||
730 | ** objects, where a dead object is one marked with the old (non current) | ||
731 | ** white; change all non-dead objects back to white, preparing for next | ||
732 | ** collection cycle. Return where to continue the traversal or NULL if | ||
733 | ** list is finished. | ||
734 | */ | ||
735 | static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count) { | ||
736 | global_State *g = G(L); | ||
737 | int ow = otherwhite(g); | ||
738 | int white = luaC_white(g); /* current white */ | ||
739 | while (*p != NULL && count-- > 0) { | ||
740 | GCObject *curr = *p; | ||
741 | int marked = curr->marked; | ||
742 | if (isdeadm(ow, marked)) { /* is 'curr' dead? */ | ||
743 | *p = curr->next; /* remove 'curr' from list */ | ||
744 | freeobj(L, curr); /* erase 'curr' */ | ||
745 | } | ||
746 | else { /* change mark to 'white' */ | ||
747 | curr->marked = cast_byte((marked & maskcolors) | white); | ||
748 | p = &curr->next; /* go to next element */ | ||
749 | } | ||
750 | } | ||
751 | return (*p == NULL) ? NULL : p; | ||
752 | } | ||
753 | |||
754 | |||
755 | /* | ||
756 | ** sweep a list until a live object (or end of list) | ||
757 | */ | ||
758 | static GCObject **sweeptolive (lua_State *L, GCObject **p) { | ||
759 | GCObject **old = p; | ||
760 | do { | ||
761 | p = sweeplist(L, p, 1); | ||
762 | } while (p == old); | ||
763 | return p; | ||
764 | } | ||
765 | |||
766 | /* }====================================================== */ | ||
767 | |||
768 | |||
769 | /* | ||
770 | ** {====================================================== | ||
771 | ** Finalization | ||
772 | ** ======================================================= | ||
773 | */ | ||
774 | |||
775 | /* | ||
776 | ** If possible, shrink string table | ||
777 | */ | ||
778 | static void checkSizes (lua_State *L, global_State *g) { | ||
779 | if (g->gckind != KGC_EMERGENCY) { | ||
780 | l_mem olddebt = g->GCdebt; | ||
781 | if (g->strt.nuse < g->strt.size / 4) /* string table too big? */ | ||
782 | luaS_resize(L, g->strt.size / 2); /* shrink it a little */ | ||
783 | g->GCestimate += g->GCdebt - olddebt; /* update estimate */ | ||
784 | } | ||
785 | } | ||
786 | |||
787 | |||
788 | static GCObject *udata2finalize (global_State *g) { | ||
789 | GCObject *o = g->tobefnz; /* get first element */ | ||
790 | lua_assert(tofinalize(o)); | ||
791 | g->tobefnz = o->next; /* remove it from 'tobefnz' list */ | ||
792 | o->next = g->allgc; /* return it to 'allgc' list */ | ||
793 | g->allgc = o; | ||
794 | resetbit(o->marked, FINALIZEDBIT); /* object is "normal" again */ | ||
795 | if (issweepphase(g)) | ||
796 | makewhite(g, o); /* "sweep" object */ | ||
797 | return o; | ||
798 | } | ||
799 | |||
800 | |||
801 | static void dothecall (lua_State *L, void *ud) { | ||
802 | UNUSED(ud); | ||
803 | luaD_callnoyield(L, L->top - 2, 0); | ||
804 | } | ||
805 | |||
806 | |||
807 | static void GCTM (lua_State *L, int propagateerrors) { | ||
808 | global_State *g = G(L); | ||
809 | const TValue *tm; | ||
810 | TValue v; | ||
811 | setgcovalue(L, &v, udata2finalize(g)); | ||
812 | tm = luaT_gettmbyobj(L, &v, TM_GC); | ||
813 | if (tm != NULL && ttisfunction(tm)) { /* is there a finalizer? */ | ||
814 | int status; | ||
815 | lu_byte oldah = L->allowhook; | ||
816 | int running = g->gcrunning; | ||
817 | L->allowhook = 0; /* stop debug hooks during GC metamethod */ | ||
818 | g->gcrunning = 0; /* avoid GC steps */ | ||
819 | setobj2s(L, L->top, tm); /* push finalizer... */ | ||
820 | setobj2s(L, L->top + 1, &v); /* ... and its argument */ | ||
821 | L->top += 2; /* and (next line) call the finalizer */ | ||
822 | L->ci->callstatus |= CIST_FIN; /* will run a finalizer */ | ||
823 | status = luaD_pcall(L, dothecall, NULL, savestack(L, L->top - 2), 0); | ||
824 | L->ci->callstatus &= ~CIST_FIN; /* not running a finalizer anymore */ | ||
825 | L->allowhook = oldah; /* restore hooks */ | ||
826 | g->gcrunning = running; /* restore state */ | ||
827 | if (status != LUA_OK && propagateerrors) { /* error while running __gc? */ | ||
828 | if (status == LUA_ERRRUN) { /* is there an error object? */ | ||
829 | const char *msg = (ttisstring(L->top - 1)) | ||
830 | ? svalue(L->top - 1) | ||
831 | : "no message"; | ||
832 | luaO_pushfstring(L, "error in __gc metamethod (%s)", msg); | ||
833 | status = LUA_ERRGCMM; /* error in __gc metamethod */ | ||
834 | } | ||
835 | luaD_throw(L, status); /* re-throw error */ | ||
836 | } | ||
837 | } | ||
838 | } | ||
839 | |||
840 | |||
841 | /* | ||
842 | ** call a few (up to 'g->gcfinnum') finalizers | ||
843 | */ | ||
844 | static int runafewfinalizers (lua_State *L) { | ||
845 | global_State *g = G(L); | ||
846 | unsigned int i; | ||
847 | lua_assert(!g->tobefnz || g->gcfinnum > 0); | ||
848 | for (i = 0; g->tobefnz && i < g->gcfinnum; i++) | ||
849 | GCTM(L, 1); /* call one finalizer */ | ||
850 | g->gcfinnum = (!g->tobefnz) ? 0 /* nothing more to finalize? */ | ||
851 | : g->gcfinnum * 2; /* else call a few more next time */ | ||
852 | return i; | ||
853 | } | ||
854 | |||
855 | |||
856 | /* | ||
857 | ** call all pending finalizers | ||
858 | */ | ||
859 | static void callallpendingfinalizers (lua_State *L) { | ||
860 | global_State *g = G(L); | ||
861 | while (g->tobefnz) | ||
862 | GCTM(L, 0); | ||
863 | } | ||
864 | |||
865 | |||
866 | /* | ||
867 | ** find last 'next' field in list 'p' list (to add elements in its end) | ||
868 | */ | ||
869 | static GCObject **findlast (GCObject **p) { | ||
870 | while (*p != NULL) | ||
871 | p = &(*p)->next; | ||
872 | return p; | ||
873 | } | ||
874 | |||
875 | |||
876 | /* | ||
877 | ** move all unreachable objects (or 'all' objects) that need | ||
878 | ** finalization from list 'finobj' to list 'tobefnz' (to be finalized) | ||
879 | */ | ||
880 | static void separatetobefnz (global_State *g, int all) { | ||
881 | GCObject *curr; | ||
882 | GCObject **p = &g->finobj; | ||
883 | GCObject **lastnext = findlast(&g->tobefnz); | ||
884 | while ((curr = *p) != NULL) { /* traverse all finalizable objects */ | ||
885 | lua_assert(tofinalize(curr)); | ||
886 | if (!(iswhite(curr) || all)) /* not being collected? */ | ||
887 | p = &curr->next; /* don't bother with it */ | ||
888 | else { | ||
889 | *p = curr->next; /* remove 'curr' from 'finobj' list */ | ||
890 | curr->next = *lastnext; /* link at the end of 'tobefnz' list */ | ||
891 | *lastnext = curr; | ||
892 | lastnext = &curr->next; | ||
893 | } | ||
894 | } | ||
895 | } | ||
896 | |||
897 | |||
898 | /* | ||
899 | ** if object 'o' has a finalizer, remove it from 'allgc' list (must | ||
900 | ** search the list to find it) and link it in 'finobj' list. | ||
901 | */ | ||
902 | void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) { | ||
903 | global_State *g = G(L); | ||
904 | if (tofinalize(o) || /* obj. is already marked... */ | ||
905 | gfasttm(g, mt, TM_GC) == NULL) /* or has no finalizer? */ | ||
906 | return; /* nothing to be done */ | ||
907 | else { /* move 'o' to 'finobj' list */ | ||
908 | GCObject **p; | ||
909 | if (issweepphase(g)) { | ||
910 | makewhite(g, o); /* "sweep" object 'o' */ | ||
911 | if (g->sweepgc == &o->next) /* should not remove 'sweepgc' object */ | ||
912 | g->sweepgc = sweeptolive(L, g->sweepgc); /* change 'sweepgc' */ | ||
913 | } | ||
914 | /* search for pointer pointing to 'o' */ | ||
915 | for (p = &g->allgc; *p != o; p = &(*p)->next) { /* empty */ } | ||
916 | *p = o->next; /* remove 'o' from 'allgc' list */ | ||
917 | o->next = g->finobj; /* link it in 'finobj' list */ | ||
918 | g->finobj = o; | ||
919 | l_setbit(o->marked, FINALIZEDBIT); /* mark it as such */ | ||
920 | } | ||
921 | } | ||
922 | |||
923 | /* }====================================================== */ | ||
924 | |||
925 | |||
926 | |||
927 | /* | ||
928 | ** {====================================================== | ||
929 | ** GC control | ||
930 | ** ======================================================= | ||
931 | */ | ||
932 | |||
933 | |||
934 | /* | ||
935 | ** Set a reasonable "time" to wait before starting a new GC cycle; cycle | ||
936 | ** will start when memory use hits threshold. (Division by 'estimate' | ||
937 | ** should be OK: it cannot be zero (because Lua cannot even start with | ||
938 | ** less than PAUSEADJ bytes). | ||
939 | */ | ||
940 | static void setpause (global_State *g) { | ||
941 | l_mem threshold, debt; | ||
942 | l_mem estimate = g->GCestimate / PAUSEADJ; /* adjust 'estimate' */ | ||
943 | lua_assert(estimate > 0); | ||
944 | threshold = (g->gcpause < MAX_LMEM / estimate) /* overflow? */ | ||
945 | ? estimate * g->gcpause /* no overflow */ | ||
946 | : MAX_LMEM; /* overflow; truncate to maximum */ | ||
947 | debt = gettotalbytes(g) - threshold; | ||
948 | luaE_setdebt(g, debt); | ||
949 | } | ||
950 | |||
951 | |||
952 | /* | ||
953 | ** Enter first sweep phase. | ||
954 | ** The call to 'sweeplist' tries to make pointer point to an object | ||
955 | ** inside the list (instead of to the header), so that the real sweep do | ||
956 | ** not need to skip objects created between "now" and the start of the | ||
957 | ** real sweep. | ||
958 | */ | ||
959 | static void entersweep (lua_State *L) { | ||
960 | global_State *g = G(L); | ||
961 | g->gcstate = GCSswpallgc; | ||
962 | lua_assert(g->sweepgc == NULL); | ||
963 | g->sweepgc = sweeplist(L, &g->allgc, 1); | ||
964 | } | ||
965 | |||
966 | |||
967 | void luaC_freeallobjects (lua_State *L) { | ||
968 | global_State *g = G(L); | ||
969 | separatetobefnz(g, 1); /* separate all objects with finalizers */ | ||
970 | lua_assert(g->finobj == NULL); | ||
971 | callallpendingfinalizers(L); | ||
972 | lua_assert(g->tobefnz == NULL); | ||
973 | g->currentwhite = WHITEBITS; /* this "white" makes all objects look dead */ | ||
974 | g->gckind = KGC_NORMAL; | ||
975 | sweepwholelist(L, &g->finobj); | ||
976 | sweepwholelist(L, &g->allgc); | ||
977 | sweepwholelist(L, &g->fixedgc); /* collect fixed objects */ | ||
978 | lua_assert(g->strt.nuse == 0); | ||
979 | } | ||
980 | |||
981 | |||
982 | static l_mem atomic (lua_State *L) { | ||
983 | global_State *g = G(L); | ||
984 | l_mem work; | ||
985 | GCObject *origweak, *origall; | ||
986 | GCObject *grayagain = g->grayagain; /* save original list */ | ||
987 | lua_assert(g->ephemeron == NULL && g->weak == NULL); | ||
988 | lua_assert(!iswhite(g->mainthread)); | ||
989 | g->gcstate = GCSinsideatomic; | ||
990 | g->GCmemtrav = 0; /* start counting work */ | ||
991 | markobject(g, L); /* mark running thread */ | ||
992 | /* registry and global metatables may be changed by API */ | ||
993 | markvalue(g, &g->l_registry); | ||
994 | markmt(g); /* mark global metatables */ | ||
995 | /* remark occasional upvalues of (maybe) dead threads */ | ||
996 | remarkupvals(g); | ||
997 | propagateall(g); /* propagate changes */ | ||
998 | work = g->GCmemtrav; /* stop counting (do not recount 'grayagain') */ | ||
999 | g->gray = grayagain; | ||
1000 | propagateall(g); /* traverse 'grayagain' list */ | ||
1001 | g->GCmemtrav = 0; /* restart counting */ | ||
1002 | convergeephemerons(g); | ||
1003 | /* at this point, all strongly accessible objects are marked. */ | ||
1004 | /* Clear values from weak tables, before checking finalizers */ | ||
1005 | clearvalues(g, g->weak, NULL); | ||
1006 | clearvalues(g, g->allweak, NULL); | ||
1007 | origweak = g->weak; origall = g->allweak; | ||
1008 | work += g->GCmemtrav; /* stop counting (objects being finalized) */ | ||
1009 | separatetobefnz(g, 0); /* separate objects to be finalized */ | ||
1010 | g->gcfinnum = 1; /* there may be objects to be finalized */ | ||
1011 | markbeingfnz(g); /* mark objects that will be finalized */ | ||
1012 | propagateall(g); /* remark, to propagate 'resurrection' */ | ||
1013 | g->GCmemtrav = 0; /* restart counting */ | ||
1014 | convergeephemerons(g); | ||
1015 | /* at this point, all resurrected objects are marked. */ | ||
1016 | /* remove dead objects from weak tables */ | ||
1017 | clearkeys(g, g->ephemeron, NULL); /* clear keys from all ephemeron tables */ | ||
1018 | clearkeys(g, g->allweak, NULL); /* clear keys from all 'allweak' tables */ | ||
1019 | /* clear values from resurrected weak tables */ | ||
1020 | clearvalues(g, g->weak, origweak); | ||
1021 | clearvalues(g, g->allweak, origall); | ||
1022 | luaS_clearcache(g); | ||
1023 | g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */ | ||
1024 | work += g->GCmemtrav; /* complete counting */ | ||
1025 | return work; /* estimate of memory marked by 'atomic' */ | ||
1026 | } | ||
1027 | |||
1028 | |||
1029 | static lu_mem sweepstep (lua_State *L, global_State *g, | ||
1030 | int nextstate, GCObject **nextlist) { | ||
1031 | if (g->sweepgc) { | ||
1032 | l_mem olddebt = g->GCdebt; | ||
1033 | g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX); | ||
1034 | g->GCestimate += g->GCdebt - olddebt; /* update estimate */ | ||
1035 | if (g->sweepgc) /* is there still something to sweep? */ | ||
1036 | return (GCSWEEPMAX * GCSWEEPCOST); | ||
1037 | } | ||
1038 | /* else enter next state */ | ||
1039 | g->gcstate = nextstate; | ||
1040 | g->sweepgc = nextlist; | ||
1041 | return 0; | ||
1042 | } | ||
1043 | |||
1044 | |||
1045 | static lu_mem singlestep (lua_State *L) { | ||
1046 | global_State *g = G(L); | ||
1047 | switch (g->gcstate) { | ||
1048 | case GCSpause: { | ||
1049 | g->GCmemtrav = g->strt.size * sizeof(GCObject*); | ||
1050 | restartcollection(g); | ||
1051 | g->gcstate = GCSpropagate; | ||
1052 | return g->GCmemtrav; | ||
1053 | } | ||
1054 | case GCSpropagate: { | ||
1055 | g->GCmemtrav = 0; | ||
1056 | lua_assert(g->gray); | ||
1057 | propagatemark(g); | ||
1058 | if (g->gray == NULL) /* no more gray objects? */ | ||
1059 | g->gcstate = GCSatomic; /* finish propagate phase */ | ||
1060 | return g->GCmemtrav; /* memory traversed in this step */ | ||
1061 | } | ||
1062 | case GCSatomic: { | ||
1063 | lu_mem work; | ||
1064 | propagateall(g); /* make sure gray list is empty */ | ||
1065 | work = atomic(L); /* work is what was traversed by 'atomic' */ | ||
1066 | entersweep(L); | ||
1067 | g->GCestimate = gettotalbytes(g); /* first estimate */; | ||
1068 | return work; | ||
1069 | } | ||
1070 | case GCSswpallgc: { /* sweep "regular" objects */ | ||
1071 | return sweepstep(L, g, GCSswpfinobj, &g->finobj); | ||
1072 | } | ||
1073 | case GCSswpfinobj: { /* sweep objects with finalizers */ | ||
1074 | return sweepstep(L, g, GCSswptobefnz, &g->tobefnz); | ||
1075 | } | ||
1076 | case GCSswptobefnz: { /* sweep objects to be finalized */ | ||
1077 | return sweepstep(L, g, GCSswpend, NULL); | ||
1078 | } | ||
1079 | case GCSswpend: { /* finish sweeps */ | ||
1080 | makewhite(g, g->mainthread); /* sweep main thread */ | ||
1081 | checkSizes(L, g); | ||
1082 | g->gcstate = GCScallfin; | ||
1083 | return 0; | ||
1084 | } | ||
1085 | case GCScallfin: { /* call remaining finalizers */ | ||
1086 | if (g->tobefnz && g->gckind != KGC_EMERGENCY) { | ||
1087 | int n = runafewfinalizers(L); | ||
1088 | return (n * GCFINALIZECOST); | ||
1089 | } | ||
1090 | else { /* emergency mode or no more finalizers */ | ||
1091 | g->gcstate = GCSpause; /* finish collection */ | ||
1092 | return 0; | ||
1093 | } | ||
1094 | } | ||
1095 | default: lua_assert(0); return 0; | ||
1096 | } | ||
1097 | } | ||
1098 | |||
1099 | |||
1100 | /* | ||
1101 | ** advances the garbage collector until it reaches a state allowed | ||
1102 | ** by 'statemask' | ||
1103 | */ | ||
1104 | void luaC_runtilstate (lua_State *L, int statesmask) { | ||
1105 | global_State *g = G(L); | ||
1106 | while (!testbit(statesmask, g->gcstate)) | ||
1107 | singlestep(L); | ||
1108 | } | ||
1109 | |||
1110 | |||
1111 | /* | ||
1112 | ** get GC debt and convert it from Kb to 'work units' (avoid zero debt | ||
1113 | ** and overflows) | ||
1114 | */ | ||
1115 | static l_mem getdebt (global_State *g) { | ||
1116 | l_mem debt = g->GCdebt; | ||
1117 | int stepmul = g->gcstepmul; | ||
1118 | if (debt <= 0) return 0; /* minimal debt */ | ||
1119 | else { | ||
1120 | debt = (debt / STEPMULADJ) + 1; | ||
1121 | debt = (debt < MAX_LMEM / stepmul) ? debt * stepmul : MAX_LMEM; | ||
1122 | return debt; | ||
1123 | } | ||
1124 | } | ||
1125 | |||
1126 | /* | ||
1127 | ** performs a basic GC step when collector is running | ||
1128 | */ | ||
1129 | void luaC_step (lua_State *L) { | ||
1130 | global_State *g = G(L); | ||
1131 | l_mem debt = getdebt(g); /* GC deficit (be paid now) */ | ||
1132 | if (!g->gcrunning) { /* not running? */ | ||
1133 | luaE_setdebt(g, -GCSTEPSIZE * 10); /* avoid being called too often */ | ||
1134 | return; | ||
1135 | } | ||
1136 | do { /* repeat until pause or enough "credit" (negative debt) */ | ||
1137 | lu_mem work = singlestep(L); /* perform one single step */ | ||
1138 | debt -= work; | ||
1139 | } while (debt > -GCSTEPSIZE && g->gcstate != GCSpause); | ||
1140 | if (g->gcstate == GCSpause) | ||
1141 | setpause(g); /* pause until next cycle */ | ||
1142 | else { | ||
1143 | debt = (debt / g->gcstepmul) * STEPMULADJ; /* convert 'work units' to Kb */ | ||
1144 | luaE_setdebt(g, debt); | ||
1145 | runafewfinalizers(L); | ||
1146 | } | ||
1147 | } | ||
1148 | |||
1149 | |||
1150 | /* | ||
1151 | ** Performs a full GC cycle; if 'isemergency', set a flag to avoid | ||
1152 | ** some operations which could change the interpreter state in some | ||
1153 | ** unexpected ways (running finalizers and shrinking some structures). | ||
1154 | ** Before running the collection, check 'keepinvariant'; if it is true, | ||
1155 | ** there may be some objects marked as black, so the collector has | ||
1156 | ** to sweep all objects to turn them back to white (as white has not | ||
1157 | ** changed, nothing will be collected). | ||
1158 | */ | ||
1159 | void luaC_fullgc (lua_State *L, int isemergency) { | ||
1160 | global_State *g = G(L); | ||
1161 | lua_assert(g->gckind == KGC_NORMAL); | ||
1162 | if (isemergency) g->gckind = KGC_EMERGENCY; /* set flag */ | ||
1163 | if (keepinvariant(g)) { /* black objects? */ | ||
1164 | entersweep(L); /* sweep everything to turn them back to white */ | ||
1165 | } | ||
1166 | /* finish any pending sweep phase to start a new cycle */ | ||
1167 | luaC_runtilstate(L, bitmask(GCSpause)); | ||
1168 | luaC_runtilstate(L, ~bitmask(GCSpause)); /* start new collection */ | ||
1169 | luaC_runtilstate(L, bitmask(GCScallfin)); /* run up to finalizers */ | ||
1170 | /* estimate must be correct after a full GC cycle */ | ||
1171 | lua_assert(g->GCestimate == gettotalbytes(g)); | ||
1172 | luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */ | ||
1173 | g->gckind = KGC_NORMAL; | ||
1174 | setpause(g); | ||
1175 | } | ||
1176 | |||
1177 | /* }====================================================== */ | ||
1178 | |||
1179 | |||