/src/testdir/build/lua-master/source/lgc.c
Line | Count | Source (jump to first uncovered line) |
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 <string.h> |
13 | | |
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 | | ** Maximum number of elements to sweep in each single step. |
31 | | ** (Large enough to dissipate fixed overheads but small enough |
32 | | ** to allow small steps for the collector.) |
33 | | */ |
34 | 13.4k | #define GCSWEEPMAX 20 |
35 | | |
36 | | |
37 | | /* |
38 | | ** Cost (in work units) of running one finalizer. |
39 | | */ |
40 | 0 | #define CWUFIN 10 |
41 | | |
42 | | |
43 | | /* mask with all color bits */ |
44 | 0 | #define maskcolors (bitmask(BLACKBIT) | WHITEBITS) |
45 | | |
46 | | /* mask with all GC bits */ |
47 | 0 | #define maskgcbits (maskcolors | AGEBITS) |
48 | | |
49 | | |
50 | | /* macro to erase all color bits then set only the current white bit */ |
51 | | #define makewhite(g,x) \ |
52 | 1.26k | (x->marked = cast_byte((x->marked & ~maskcolors) | luaC_white(g))) |
53 | | |
54 | | /* make an object gray (neither white nor black) */ |
55 | 34.5k | #define set2gray(x) resetbits(x->marked, maskcolors) |
56 | | |
57 | | |
58 | | /* make an object black (coming from any color) */ |
59 | | #define set2black(x) \ |
60 | 42.2k | (x->marked = cast_byte((x->marked & ~WHITEBITS) | bitmask(BLACKBIT))) |
61 | | |
62 | | |
63 | 6.73M | #define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x))) |
64 | | |
65 | 95.6k | #define keyiswhite(n) (keyiscollectable(n) && iswhite(gckey(n))) |
66 | | |
67 | | |
68 | | /* |
69 | | ** Protected access to objects in values |
70 | | */ |
71 | 0 | #define gcvalueN(o) (iscollectable(o) ? gcvalue(o) : NULL) |
72 | | |
73 | | |
74 | | /* |
75 | | ** Access to collectable objects in array part of tables |
76 | | */ |
77 | | #define gcvalarr(t,i) \ |
78 | 686k | ((*getArrTag(t,i) & BIT_ISCOLLECTABLE) ? getArrVal(t,i)->gc : NULL) |
79 | | |
80 | | |
81 | 6.73M | #define markvalue(g,o) { checkliveness(mainthread(g),o); \ |
82 | 6.73M | if (valiswhite(o)) reallymarkobject(g,gcvalue(o)); } |
83 | | |
84 | 95.6k | #define markkey(g, n) { if keyiswhite(n) reallymarkobject(g,gckey(n)); } |
85 | | |
86 | 20.3k | #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 | 57.9k | #define markobjectN(g,t) { if (t) markobject(g,t); } |
93 | | |
94 | | |
95 | | static void reallymarkobject (global_State *g, GCObject *o); |
96 | | static void atomic (lua_State *L); |
97 | | static void entersweep (lua_State *L); |
98 | | |
99 | | |
100 | | /* |
101 | | ** {====================================================== |
102 | | ** Generic functions |
103 | | ** ======================================================= |
104 | | */ |
105 | | |
106 | | |
107 | | /* |
108 | | ** one after last element in a hash array |
109 | | */ |
110 | 5.04k | #define gnodelast(h) gnode(h, cast_sizet(sizenode(h))) |
111 | | |
112 | | |
113 | 99.6k | static l_mem objsize (GCObject *o) { |
114 | 99.6k | lu_mem res; |
115 | 99.6k | switch (o->tt) { |
116 | 7.43k | case LUA_VTABLE: { |
117 | 7.43k | res = luaH_size(gco2t(o)); |
118 | 0 | break; |
119 | 7.43k | } |
120 | 1.65k | case LUA_VLCL: { |
121 | 1.65k | LClosure *cl = gco2lcl(o); |
122 | 1.65k | res = sizeLclosure(cl->nupvalues); |
123 | 1.65k | break; |
124 | 1.65k | } |
125 | 0 | case LUA_VCCL: { |
126 | 0 | CClosure *cl = gco2ccl(o); |
127 | 0 | res = sizeCclosure(cl->nupvalues); |
128 | 0 | break; |
129 | 0 | } |
130 | 103 | case LUA_VUSERDATA: { |
131 | 103 | Udata *u = gco2u(o); |
132 | 103 | res = sizeudata(u->nuvalue, u->len); |
133 | 103 | break; |
134 | 103 | } |
135 | 3.45k | case LUA_VPROTO: { |
136 | 3.45k | res = luaF_protosize(gco2p(o)); |
137 | 0 | break; |
138 | 3.45k | } |
139 | 1.23k | case LUA_VTHREAD: { |
140 | 1.23k | res = luaE_threadsize(gco2th(o)); |
141 | 0 | break; |
142 | 1.23k | } |
143 | 77.7k | case LUA_VSHRSTR: { |
144 | 77.7k | TString *ts = gco2ts(o); |
145 | 77.7k | res = sizestrshr(cast_uint(ts->shrlen)); |
146 | 77.7k | break; |
147 | 77.7k | } |
148 | 7.89k | case LUA_VLNGSTR: { |
149 | 7.89k | TString *ts = gco2ts(o); |
150 | 0 | res = luaS_sizelngstr(ts->u.lnglen, ts->shrlen); |
151 | 7.89k | break; |
152 | 7.89k | } |
153 | 159 | case LUA_VUPVAL: { |
154 | 159 | res = sizeof(UpVal); |
155 | 159 | break; |
156 | 7.89k | } |
157 | 0 | default: res = 0; lua_assert(0); |
158 | 99.6k | } |
159 | 99.6k | return cast(l_mem, res); |
160 | 99.6k | } |
161 | | |
162 | | |
163 | 23.3k | static GCObject **getgclist (GCObject *o) { |
164 | 23.3k | switch (o->tt) { |
165 | 12.3k | case LUA_VTABLE: return &gco2t(o)->gclist; |
166 | 2.46k | case LUA_VLCL: return &gco2lcl(o)->gclist; |
167 | 0 | case LUA_VCCL: return &gco2ccl(o)->gclist; |
168 | 3.68k | case LUA_VTHREAD: return &gco2th(o)->gclist; |
169 | 4.87k | case LUA_VPROTO: return &gco2p(o)->gclist; |
170 | 0 | case LUA_VUSERDATA: { |
171 | 0 | Udata *u = gco2u(o); |
172 | 0 | lua_assert(u->nuvalue > 0); |
173 | 0 | return &u->gclist; |
174 | 0 | } |
175 | 0 | default: lua_assert(0); return 0; |
176 | 23.3k | } |
177 | 23.3k | } |
178 | | |
179 | | |
180 | | /* |
181 | | ** Link a collectable object 'o' with a known type into the list 'p'. |
182 | | ** (Must be a macro to access the 'gclist' field in different types.) |
183 | | */ |
184 | 1.23k | #define linkgclist(o,p) linkgclist_(obj2gco(o), &(o)->gclist, &(p)) |
185 | | |
186 | 13.4k | static void linkgclist_ (GCObject *o, GCObject **pnext, GCObject **list) { |
187 | 13.4k | lua_assert(!isgray(o)); /* cannot be in a gray list */ |
188 | 13.4k | *pnext = *list; |
189 | 13.4k | *list = o; |
190 | 13.4k | set2gray(o); /* now it is */ |
191 | 13.4k | } |
192 | | |
193 | | |
194 | | /* |
195 | | ** Link a generic collectable object 'o' into the list 'p'. |
196 | | */ |
197 | 12.2k | #define linkobjgclist(o,p) linkgclist_(obj2gco(o), getgclist(o), &(p)) |
198 | | |
199 | | |
200 | | |
201 | | /* |
202 | | ** Clear keys for empty entries in tables. If entry is empty, mark its |
203 | | ** entry as dead. This allows the collection of the key, but keeps its |
204 | | ** entry in the table: its removal could break a chain and could break |
205 | | ** a table traversal. Other places never manipulate dead keys, because |
206 | | ** its associated empty value is enough to signal that the entry is |
207 | | ** logically empty. |
208 | | */ |
209 | 49.6k | static void clearkey (Node *n) { |
210 | 49.6k | lua_assert(isempty(gval(n))); |
211 | 49.6k | if (keyiscollectable(n)) |
212 | 0 | setdeadkey(n); /* unused key; remove it */ |
213 | 49.6k | } |
214 | | |
215 | | |
216 | | /* |
217 | | ** tells whether a key or value can be cleared from a weak |
218 | | ** table. Non-collectable objects are never removed from weak |
219 | | ** tables. Strings behave as 'values', so are never removed too. for |
220 | | ** other objects: if really collected, cannot keep them; for objects |
221 | | ** being finalized, keep them in keys, but not in values |
222 | | */ |
223 | 0 | static int iscleared (global_State *g, const GCObject *o) { |
224 | 0 | if (o == NULL) return 0; /* non-collectable value */ |
225 | 0 | else if (novariant(o->tt) == LUA_TSTRING) { |
226 | 0 | markobject(g, o); /* strings are 'values', so are never weak */ |
227 | 0 | return 0; |
228 | 0 | } |
229 | 0 | else return iswhite(o); |
230 | 0 | } |
231 | | |
232 | | |
233 | | /* |
234 | | ** Barrier that moves collector forward, that is, marks the white object |
235 | | ** 'v' being pointed by the black object 'o'. In the generational |
236 | | ** mode, 'v' must also become old, if 'o' is old; however, it cannot |
237 | | ** be changed directly to OLD, because it may still point to non-old |
238 | | ** objects. So, it is marked as OLD0. In the next cycle it will become |
239 | | ** OLD1, and in the next it will finally become OLD (regular old). By |
240 | | ** then, any object it points to will also be old. If called in the |
241 | | ** incremental sweep phase, it clears the black object to white (sweep |
242 | | ** it) to avoid other barrier calls for this same object. (That cannot |
243 | | ** be done is generational mode, as its sweep does not distinguish |
244 | | ** white from dead.) |
245 | | */ |
246 | 1.45k | void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) { |
247 | 1.45k | global_State *g = G(L); |
248 | 1.45k | lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o)); |
249 | 1.45k | if (keepinvariant(g)) { /* must keep invariant? */ |
250 | 247 | reallymarkobject(g, v); /* restore invariant */ |
251 | 247 | if (isold(o)) { |
252 | 0 | lua_assert(!isold(v)); /* white object could not be old */ |
253 | 0 | setage(v, G_OLD0); /* restore generational invariant */ |
254 | 0 | } |
255 | 247 | } |
256 | 1.20k | else { /* sweep phase */ |
257 | 1.20k | lua_assert(issweepphase(g)); |
258 | 1.20k | if (g->gckind != KGC_GENMINOR) /* incremental mode? */ |
259 | 1.20k | makewhite(g, o); /* mark 'o' as white to avoid other barriers */ |
260 | 1.20k | } |
261 | 1.45k | } |
262 | | |
263 | | |
264 | | /* |
265 | | ** barrier that moves collector backward, that is, mark the black object |
266 | | ** pointing to a white object as gray again. |
267 | | */ |
268 | 2.29k | void luaC_barrierback_ (lua_State *L, GCObject *o) { |
269 | 2.29k | global_State *g = G(L); |
270 | 2.29k | lua_assert(isblack(o) && !isdead(g, o)); |
271 | 2.29k | lua_assert((g->gckind != KGC_GENMINOR) |
272 | 2.29k | || (isold(o) && getage(o) != G_TOUCHED1)); |
273 | 2.29k | if (getage(o) == G_TOUCHED2) /* already in gray list? */ |
274 | 2.29k | set2gray(o); /* make it gray to become touched1 */ |
275 | 2.29k | else /* link it in 'grayagain' and paint it gray */ |
276 | 2.29k | linkobjgclist(o, g->grayagain); |
277 | 2.29k | if (isold(o)) /* generational mode? */ |
278 | 0 | setage(o, G_TOUCHED1); /* touched in current cycle */ |
279 | 2.29k | } |
280 | | |
281 | | |
282 | 21.1k | void luaC_fix (lua_State *L, GCObject *o) { |
283 | 21.1k | global_State *g = G(L); |
284 | 21.1k | lua_assert(g->allgc == o); /* object must be 1st in 'allgc' list! */ |
285 | 21.1k | set2gray(o); /* they will be gray forever */ |
286 | 21.1k | setage(o, G_OLD); /* and old forever */ |
287 | 21.1k | g->allgc = o->next; /* remove object from 'allgc' list */ |
288 | 21.1k | o->next = g->fixedgc; /* link it to 'fixedgc' list */ |
289 | 21.1k | g->fixedgc = o; |
290 | 21.1k | } |
291 | | |
292 | | |
293 | | /* |
294 | | ** create a new collectable object (with given type, size, and offset) |
295 | | ** and link it to 'allgc' list. |
296 | | */ |
297 | 47.5k | GCObject *luaC_newobjdt (lua_State *L, lu_byte tt, size_t sz, size_t offset) { |
298 | 47.5k | global_State *g = G(L); |
299 | 47.5k | char *p = cast_charp(luaM_newobject(L, novariant(tt), sz)); |
300 | 47.5k | GCObject *o = cast(GCObject *, p + offset); |
301 | 47.5k | o->marked = luaC_white(g); |
302 | 47.5k | o->tt = tt; |
303 | 47.5k | o->next = g->allgc; |
304 | 47.5k | g->allgc = o; |
305 | 47.5k | return o; |
306 | 47.5k | } |
307 | | |
308 | | |
309 | | /* |
310 | | ** create a new collectable object with no offset. |
311 | | */ |
312 | 47.5k | GCObject *luaC_newobj (lua_State *L, lu_byte tt, size_t sz) { |
313 | 47.5k | return luaC_newobjdt(L, tt, sz, 0); |
314 | 47.5k | } |
315 | | |
316 | | /* }====================================================== */ |
317 | | |
318 | | |
319 | | |
320 | | /* |
321 | | ** {====================================================== |
322 | | ** Mark functions |
323 | | ** ======================================================= |
324 | | */ |
325 | | |
326 | | |
327 | | /* |
328 | | ** Mark an object. Userdata with no user values, strings, and closed |
329 | | ** upvalues are visited and turned black here. Open upvalues are |
330 | | ** already indirectly linked through their respective threads in the |
331 | | ** 'twups' list, so they don't go to the gray list; nevertheless, they |
332 | | ** are kept gray to avoid barriers, as their values will be revisited |
333 | | ** by the thread or by 'remarkupvals'. Other objects are added to the |
334 | | ** gray list to be visited (and turned black) later. Both userdata and |
335 | | ** upvalues can call this function recursively, but this recursion goes |
336 | | ** for at most two levels: An upvalue cannot refer to another upvalue |
337 | | ** (only closures can), and a userdata's metatable must be a table. |
338 | | */ |
339 | 52.1k | static void reallymarkobject (global_State *g, GCObject *o) { |
340 | 52.1k | g->GCmarked += objsize(o); |
341 | 52.1k | switch (o->tt) { |
342 | 39.7k | case LUA_VSHRSTR: |
343 | 42.1k | case LUA_VLNGSTR: { |
344 | 42.1k | set2black(o); /* nothing to visit */ |
345 | 42.1k | break; |
346 | 39.7k | } |
347 | 51 | case LUA_VUPVAL: { |
348 | 51 | UpVal *uv = gco2upv(o); |
349 | 51 | if (upisopen(uv)) |
350 | 51 | set2gray(uv); /* open upvalues are kept gray */ |
351 | 51 | else |
352 | 51 | set2black(uv); /* closed upvalues are visited here */ |
353 | 51 | markvalue(g, uv->v.p); /* mark its content */ |
354 | 51 | break; |
355 | 51 | } |
356 | 50 | case LUA_VUSERDATA: { |
357 | 50 | Udata *u = gco2u(o); |
358 | 50 | if (u->nuvalue == 0) { /* no user values? */ |
359 | 50 | markobjectN(g, u->metatable); /* mark its metatable */ |
360 | 50 | set2black(u); /* nothing else to mark */ |
361 | 50 | break; |
362 | 50 | } |
363 | | /* else... */ |
364 | 50 | } /* FALLTHROUGH */ |
365 | 6.26k | case LUA_VLCL: case LUA_VCCL: case LUA_VTABLE: |
366 | 9.94k | case LUA_VTHREAD: case LUA_VPROTO: { |
367 | 9.94k | linkobjgclist(o, g->gray); /* to be visited later */ |
368 | 0 | break; |
369 | 9.94k | } |
370 | 0 | default: lua_assert(0); break; |
371 | 52.1k | } |
372 | 52.1k | } |
373 | | |
374 | | |
375 | | /* |
376 | | ** mark metamethods for basic types |
377 | | */ |
378 | 2.44k | static void markmt (global_State *g) { |
379 | 2.44k | int i; |
380 | 24.4k | for (i=0; i < LUA_NUMTYPES; i++) |
381 | 22.0k | markobjectN(g, g->mt[i]); |
382 | 2.44k | } |
383 | | |
384 | | |
385 | | /* |
386 | | ** mark all objects in list of being-finalized |
387 | | */ |
388 | 2.44k | static void markbeingfnz (global_State *g) { |
389 | 2.44k | GCObject *o; |
390 | 2.45k | for (o = g->tobefnz; o != NULL; o = o->next) |
391 | 2.44k | markobject(g, o); |
392 | 2.44k | } |
393 | | |
394 | | |
395 | | /* |
396 | | ** For each non-marked thread, simulates a barrier between each open |
397 | | ** upvalue and its value. (If the thread is collected, the value will be |
398 | | ** assigned to the upvalue, but then it can be too late for the barrier |
399 | | ** to act. The "barrier" does not need to check colors: A non-marked |
400 | | ** thread must be young; upvalues cannot be older than their threads; so |
401 | | ** any visited upvalue must be young too.) Also removes the thread from |
402 | | ** the list, as it was already visited. Removes also threads with no |
403 | | ** upvalues, as they have nothing to be checked. (If the thread gets an |
404 | | ** upvalue later, it will be linked in the list again.) |
405 | | */ |
406 | 1.21k | static void remarkupvals (global_State *g) { |
407 | 1.21k | lua_State *thread; |
408 | 1.21k | lua_State **p = &g->twups; |
409 | 1.21k | while ((thread = *p) != NULL) { |
410 | 0 | if (!iswhite(thread) && thread->openupval != NULL) |
411 | 0 | p = &thread->twups; /* keep marked thread with upvalues in the list */ |
412 | 0 | else { /* thread is not marked or without upvalues */ |
413 | 0 | UpVal *uv; |
414 | 0 | lua_assert(!isold(thread) || thread->openupval == NULL); |
415 | 0 | *p = thread->twups; /* remove thread from the list */ |
416 | 0 | thread->twups = thread; /* mark that it is out of list */ |
417 | 0 | for (uv = thread->openupval; uv != NULL; uv = uv->u.open.next) { |
418 | 0 | lua_assert(getage(uv) <= getage(thread)); |
419 | 0 | if (!iswhite(uv)) { /* upvalue already visited? */ |
420 | 0 | lua_assert(upisopen(uv) && isgray(uv)); |
421 | 0 | markvalue(g, uv->v.p); /* mark its value */ |
422 | 0 | } |
423 | 0 | } |
424 | 0 | } |
425 | 0 | } |
426 | 1.21k | } |
427 | | |
428 | | |
429 | 1.23k | static void cleargraylists (global_State *g) { |
430 | 1.23k | g->gray = g->grayagain = NULL; |
431 | 1.23k | g->weak = g->allweak = g->ephemeron = NULL; |
432 | 1.23k | } |
433 | | |
434 | | |
435 | | /* |
436 | | ** mark root set and reset all gray lists, to start a new collection. |
437 | | ** 'GCmarked' is initialized to count the total number of live bytes |
438 | | ** during a cycle. |
439 | | */ |
440 | 1.23k | static void restartcollection (global_State *g) { |
441 | 1.23k | cleargraylists(g); |
442 | 1.23k | g->GCmarked = 0; |
443 | 1.23k | markobject(g, mainthread(g)); |
444 | 1.23k | markvalue(g, &g->l_registry); |
445 | 1.23k | markmt(g); |
446 | 1.23k | markbeingfnz(g); /* mark any finalizing object left from previous cycle */ |
447 | 1.23k | } |
448 | | |
449 | | /* }====================================================== */ |
450 | | |
451 | | |
452 | | /* |
453 | | ** {====================================================== |
454 | | ** Traverse functions |
455 | | ** ======================================================= |
456 | | */ |
457 | | |
458 | | |
459 | | /* |
460 | | ** Check whether object 'o' should be kept in the 'grayagain' list for |
461 | | ** post-processing by 'correctgraylist'. (It could put all old objects |
462 | | ** in the list and leave all the work to 'correctgraylist', but it is |
463 | | ** more efficient to avoid adding elements that will be removed.) Only |
464 | | ** TOUCHED1 objects need to be in the list. TOUCHED2 doesn't need to go |
465 | | ** back to a gray list, but then it must become OLD. (That is what |
466 | | ** 'correctgraylist' does when it finds a TOUCHED2 object.) |
467 | | ** This function is a no-op in incremental mode, as objects cannot be |
468 | | ** marked as touched in that mode. |
469 | | */ |
470 | 5.04k | static void genlink (global_State *g, GCObject *o) { |
471 | 5.04k | lua_assert(isblack(o)); |
472 | 5.04k | if (getage(o) == G_TOUCHED1) { /* touched in this cycle? */ |
473 | 0 | linkobjgclist(o, g->grayagain); /* link it back in 'grayagain' */ |
474 | 0 | } /* everything else do not need to be linked back */ |
475 | 5.04k | else if (getage(o) == G_TOUCHED2) |
476 | 0 | setage(o, G_OLD); /* advance age */ |
477 | 5.04k | } |
478 | | |
479 | | |
480 | | /* |
481 | | ** Traverse a table with weak values and link it to proper list. During |
482 | | ** propagate phase, keep it in 'grayagain' list, to be revisited in the |
483 | | ** atomic phase. In the atomic phase, if table has any white value, |
484 | | ** put it in 'weak' list, to be cleared; otherwise, call 'genlink' |
485 | | ** to check table age in generational mode. |
486 | | */ |
487 | 0 | static void traverseweakvalue (global_State *g, Table *h) { |
488 | 0 | Node *n, *limit = gnodelast(h); |
489 | | /* if there is array part, assume it may have white values (it is not |
490 | | worth traversing it now just to check) */ |
491 | 0 | int hasclears = (h->asize > 0); |
492 | 0 | for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */ |
493 | 0 | if (isempty(gval(n))) /* entry is empty? */ |
494 | 0 | clearkey(n); /* clear its key */ |
495 | 0 | else { |
496 | 0 | lua_assert(!keyisnil(n)); |
497 | 0 | markkey(g, n); |
498 | 0 | if (!hasclears && iscleared(g, gcvalueN(gval(n)))) /* a white value? */ |
499 | 0 | hasclears = 1; /* table will have to be cleared */ |
500 | 0 | } |
501 | 0 | } |
502 | 0 | if (g->gcstate == GCSpropagate) |
503 | 0 | linkgclist(h, g->grayagain); /* must retraverse it in atomic phase */ |
504 | 0 | else if (hasclears) |
505 | 0 | linkgclist(h, g->weak); /* has to be cleared later */ |
506 | 0 | else |
507 | 0 | genlink(g, obj2gco(h)); |
508 | 0 | } |
509 | | |
510 | | |
511 | | /* |
512 | | ** Traverse the array part of a table. |
513 | | */ |
514 | 5.04k | static int traversearray (global_State *g, Table *h) { |
515 | 5.04k | unsigned asize = h->asize; |
516 | 5.04k | int marked = 0; /* true if some object is marked in this traversal */ |
517 | 5.04k | unsigned i; |
518 | 691k | for (i = 0; i < asize; i++) { |
519 | 686k | GCObject *o = gcvalarr(h, i); |
520 | 686k | if (o != NULL && iswhite(o)) { |
521 | 1.23k | marked = 1; |
522 | 1.23k | reallymarkobject(g, o); |
523 | 1.23k | } |
524 | 686k | } |
525 | 5.04k | return marked; |
526 | 5.04k | } |
527 | | |
528 | | |
529 | | /* |
530 | | ** Traverse an ephemeron table and link it to proper list. Returns true |
531 | | ** iff any object was marked during this traversal (which implies that |
532 | | ** convergence has to continue). During propagation phase, keep table |
533 | | ** in 'grayagain' list, to be visited again in the atomic phase. In |
534 | | ** the atomic phase, if table has any white->white entry, it has to |
535 | | ** be revisited during ephemeron convergence (as that key may turn |
536 | | ** black). Otherwise, if it has any white key, table has to be cleared |
537 | | ** (in the atomic phase). In generational mode, some tables |
538 | | ** must be kept in some gray list for post-processing; this is done |
539 | | ** by 'genlink'. |
540 | | */ |
541 | 0 | static int traverseephemeron (global_State *g, Table *h, int inv) { |
542 | 0 | int hasclears = 0; /* true if table has white keys */ |
543 | 0 | int hasww = 0; /* true if table has entry "white-key -> white-value" */ |
544 | 0 | unsigned int i; |
545 | 0 | unsigned int nsize = sizenode(h); |
546 | 0 | int marked = traversearray(g, h); /* traverse array part */ |
547 | | /* traverse hash part; if 'inv', traverse descending |
548 | | (see 'convergeephemerons') */ |
549 | 0 | for (i = 0; i < nsize; i++) { |
550 | 0 | Node *n = inv ? gnode(h, nsize - 1 - i) : gnode(h, i); |
551 | 0 | if (isempty(gval(n))) /* entry is empty? */ |
552 | 0 | clearkey(n); /* clear its key */ |
553 | 0 | else if (iscleared(g, gckeyN(n))) { /* key is not marked (yet)? */ |
554 | 0 | hasclears = 1; /* table must be cleared */ |
555 | 0 | if (valiswhite(gval(n))) /* value not marked yet? */ |
556 | 0 | hasww = 1; /* white-white entry */ |
557 | 0 | } |
558 | 0 | else if (valiswhite(gval(n))) { /* value not marked yet? */ |
559 | 0 | marked = 1; |
560 | 0 | reallymarkobject(g, gcvalue(gval(n))); /* mark it now */ |
561 | 0 | } |
562 | 0 | } |
563 | | /* link table into proper list */ |
564 | 0 | if (g->gcstate == GCSpropagate) |
565 | 0 | linkgclist(h, g->grayagain); /* must retraverse it in atomic phase */ |
566 | 0 | else if (hasww) /* table has white->white entries? */ |
567 | 0 | linkgclist(h, g->ephemeron); /* have to propagate again */ |
568 | 0 | else if (hasclears) /* table has white keys? */ |
569 | 0 | linkgclist(h, g->allweak); /* may have to clean white keys */ |
570 | 0 | else |
571 | 0 | genlink(g, obj2gco(h)); /* check whether collector still needs to see it */ |
572 | 0 | return marked; |
573 | 0 | } |
574 | | |
575 | | |
576 | 5.04k | static void traversestrongtable (global_State *g, Table *h) { |
577 | 5.04k | Node *n, *limit = gnodelast(h); |
578 | 5.04k | traversearray(g, h); |
579 | 150k | for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */ |
580 | 145k | if (isempty(gval(n))) /* entry is empty? */ |
581 | 49.6k | clearkey(n); /* clear its key */ |
582 | 95.6k | else { |
583 | 95.6k | lua_assert(!keyisnil(n)); |
584 | 95.6k | markkey(g, n); |
585 | 95.6k | markvalue(g, gval(n)); |
586 | 95.6k | } |
587 | 145k | } |
588 | 5.04k | genlink(g, obj2gco(h)); |
589 | 5.04k | } |
590 | | |
591 | | |
592 | | /* |
593 | | ** (result & 1) iff weak values; (result & 2) iff weak keys. |
594 | | */ |
595 | 5.04k | static int getmode (global_State *g, Table *h) { |
596 | 5.04k | const TValue *mode = gfasttm(g, h->metatable, TM_MODE); |
597 | 5.04k | if (mode == NULL || !ttisshrstring(mode)) |
598 | 5.04k | return 0; /* ignore non-(short)string modes */ |
599 | 0 | else { |
600 | 0 | const char *smode = getshrstr(tsvalue(mode)); |
601 | 0 | const char *weakkey = strchr(smode, 'k'); |
602 | 0 | const char *weakvalue = strchr(smode, 'v'); |
603 | 0 | return ((weakkey != NULL) << 1) | (weakvalue != NULL); |
604 | 0 | } |
605 | 5.04k | } |
606 | | |
607 | | |
608 | 5.04k | static l_mem traversetable (global_State *g, Table *h) { |
609 | 5.04k | markobjectN(g, h->metatable); |
610 | 5.04k | switch (getmode(g, h)) { |
611 | 5.04k | case 0: /* not weak */ |
612 | 5.04k | traversestrongtable(g, h); |
613 | 5.04k | break; |
614 | 0 | case 1: /* weak values */ |
615 | 0 | traverseweakvalue(g, h); |
616 | 0 | break; |
617 | 0 | case 2: /* weak keys */ |
618 | 0 | traverseephemeron(g, h, 0); |
619 | 0 | break; |
620 | 0 | case 3: /* all weak; nothing to traverse */ |
621 | 0 | if (g->gcstate == GCSpropagate) |
622 | 0 | linkgclist(h, g->grayagain); /* must visit again its metatable */ |
623 | 0 | else |
624 | 0 | linkgclist(h, g->allweak); /* must clear collected entries */ |
625 | 0 | break; |
626 | 5.04k | } |
627 | 5.04k | return 1 + 2*sizenode(h) + h->asize; |
628 | 5.04k | } |
629 | | |
630 | | |
631 | 0 | static l_mem traverseudata (global_State *g, Udata *u) { |
632 | 0 | int i; |
633 | 0 | markobjectN(g, u->metatable); /* mark its metatable */ |
634 | 0 | for (i = 0; i < u->nuvalue; i++) |
635 | 0 | markvalue(g, &u->uv[i].uv); |
636 | 0 | genlink(g, obj2gco(u)); |
637 | 0 | return 1 + u->nuvalue; |
638 | 0 | } |
639 | | |
640 | | |
641 | | /* |
642 | | ** Traverse a prototype. (While a prototype is being build, its |
643 | | ** arrays can be larger than needed; the extra slots are filled with |
644 | | ** NULL, so the use of 'markobjectN') |
645 | | */ |
646 | 2.42k | static l_mem traverseproto (global_State *g, Proto *f) { |
647 | 2.42k | int i; |
648 | 2.42k | markobjectN(g, f->source); |
649 | 6.62M | for (i = 0; i < f->sizek; i++) /* mark literals */ |
650 | 6.62M | markvalue(g, &f->k[i]); |
651 | 7.93k | for (i = 0; i < f->sizeupvalues; i++) /* mark upvalue names */ |
652 | 5.50k | markobjectN(g, f->upvalues[i].name); |
653 | 4.66k | for (i = 0; i < f->sizep; i++) /* mark nested protos */ |
654 | 2.42k | markobjectN(g, f->p[i]); |
655 | 13.3k | for (i = 0; i < f->sizelocvars; i++) /* mark local-variable names */ |
656 | 10.9k | markobjectN(g, f->locvars[i].varname); |
657 | 2.42k | return 1 + f->sizek + f->sizeupvalues + f->sizep + f->sizelocvars; |
658 | 2.42k | } |
659 | | |
660 | | |
661 | 0 | static l_mem traverseCclosure (global_State *g, CClosure *cl) { |
662 | 0 | int i; |
663 | 0 | for (i = 0; i < cl->nupvalues; i++) /* mark its upvalues */ |
664 | 0 | markvalue(g, &cl->upvalue[i]); |
665 | 0 | return 1 + cl->nupvalues; |
666 | 0 | } |
667 | | |
668 | | /* |
669 | | ** Traverse a Lua closure, marking its prototype and its upvalues. |
670 | | ** (Both can be NULL while closure is being created.) |
671 | | */ |
672 | 1.22k | static l_mem traverseLclosure (global_State *g, LClosure *cl) { |
673 | 1.22k | int i; |
674 | 1.22k | markobjectN(g, cl->p); /* mark its prototype */ |
675 | 2.45k | for (i = 0; i < cl->nupvalues; i++) { /* visit its upvalues */ |
676 | 1.22k | UpVal *uv = cl->upvals[i]; |
677 | 1.22k | markobjectN(g, uv); /* mark upvalue */ |
678 | 1.22k | } |
679 | 1.22k | return 1 + cl->nupvalues; |
680 | 1.22k | } |
681 | | |
682 | | |
683 | | /* |
684 | | ** Traverse a thread, marking the elements in the stack up to its top |
685 | | ** and cleaning the rest of the stack in the final traversal. That |
686 | | ** ensures that the entire stack have valid (non-dead) objects. |
687 | | ** Threads have no barriers. In gen. mode, old threads must be visited |
688 | | ** at every cycle, because they might point to young objects. In inc. |
689 | | ** mode, the thread can still be modified before the end of the cycle, |
690 | | ** and therefore it must be visited again in the atomic phase. To ensure |
691 | | ** these visits, threads must return to a gray list if they are not new |
692 | | ** (which can only happen in generational mode) or if the traverse is in |
693 | | ** the propagate phase (which can only happen in incremental mode). |
694 | | */ |
695 | 2.44k | static l_mem traversethread (global_State *g, lua_State *th) { |
696 | 2.44k | UpVal *uv; |
697 | 2.44k | StkId o = th->stack.p; |
698 | 2.44k | if (isold(th) || g->gcstate == GCSpropagate) |
699 | 1.23k | linkgclist(th, g->grayagain); /* insert into 'grayagain' list */ |
700 | 2.44k | if (o == NULL) |
701 | 0 | return 0; /* stack not completely built yet */ |
702 | 2.44k | lua_assert(g->gcstate == GCSatomic || |
703 | 2.44k | th->openupval == NULL || isintwups(th)); |
704 | 15.6k | for (; o < th->top.p; o++) /* mark live elements in the stack */ |
705 | 13.1k | markvalue(g, s2v(o)); |
706 | 2.44k | for (uv = th->openupval; uv != NULL; uv = uv->u.open.next) |
707 | 2.44k | markobject(g, uv); /* open upvalues cannot be collected */ |
708 | 2.44k | if (g->gcstate == GCSatomic) { /* final traversal? */ |
709 | 1.21k | if (!g->gcemergency) |
710 | 1.21k | luaD_shrinkstack(th); /* do not change stack in emergency cycle */ |
711 | 49.4k | for (o = th->top.p; o < th->stack_last.p + EXTRA_STACK; o++) |
712 | 48.2k | setnilvalue(s2v(o)); /* clear dead stack slice */ |
713 | | /* 'remarkupvals' may have removed thread from 'twups' list */ |
714 | 1.21k | if (!isintwups(th) && th->openupval != NULL) { |
715 | 0 | th->twups = g->twups; /* link it back to the list */ |
716 | 0 | g->twups = th; |
717 | 0 | } |
718 | 1.21k | } |
719 | 2.44k | return 1 + (th->top.p - th->stack.p); |
720 | 2.44k | } |
721 | | |
722 | | |
723 | | /* |
724 | | ** traverse one gray object, turning it to black. Return an estimate |
725 | | ** of the number of slots traversed. |
726 | | */ |
727 | 11.1k | static l_mem propagatemark (global_State *g) { |
728 | 11.1k | GCObject *o = g->gray; |
729 | 11.1k | nw2black(o); |
730 | 0 | g->gray = *getgclist(o); /* remove from 'gray' list */ |
731 | 11.1k | switch (o->tt) { |
732 | 5.04k | case LUA_VTABLE: return traversetable(g, gco2t(o)); |
733 | 0 | case LUA_VUSERDATA: return traverseudata(g, gco2u(o)); |
734 | 1.22k | case LUA_VLCL: return traverseLclosure(g, gco2lcl(o)); |
735 | 0 | case LUA_VCCL: return traverseCclosure(g, gco2ccl(o)); |
736 | 2.42k | case LUA_VPROTO: return traverseproto(g, gco2p(o)); |
737 | 2.44k | case LUA_VTHREAD: return traversethread(g, gco2th(o)); |
738 | 0 | default: lua_assert(0); return 0; |
739 | 11.1k | } |
740 | 11.1k | } |
741 | | |
742 | | |
743 | 4.86k | static void propagateall (global_State *g) { |
744 | 6.14k | while (g->gray) |
745 | 1.28k | propagatemark(g); |
746 | 4.86k | } |
747 | | |
748 | | |
749 | | /* |
750 | | ** Traverse all ephemeron tables propagating marks from keys to values. |
751 | | ** Repeat until it converges, that is, nothing new is marked. 'dir' |
752 | | ** inverts the direction of the traversals, trying to speed up |
753 | | ** convergence on chains in the same table. |
754 | | */ |
755 | 2.43k | static void convergeephemerons (global_State *g) { |
756 | 2.43k | int changed; |
757 | 2.43k | int dir = 0; |
758 | 2.43k | do { |
759 | 2.43k | GCObject *w; |
760 | 2.43k | GCObject *next = g->ephemeron; /* get ephemeron list */ |
761 | 2.43k | g->ephemeron = NULL; /* tables may return to this list when traversed */ |
762 | 2.43k | changed = 0; |
763 | 2.43k | while ((w = next) != NULL) { /* for each ephemeron table */ |
764 | 0 | Table *h = gco2t(w); |
765 | 0 | next = h->gclist; /* list is rebuilt during loop */ |
766 | 0 | nw2black(h); /* out of the list (for now) */ |
767 | 0 | if (traverseephemeron(g, h, dir)) { /* marked some value? */ |
768 | 0 | propagateall(g); /* propagate changes */ |
769 | 0 | changed = 1; /* will have to revisit all ephemeron tables */ |
770 | 0 | } |
771 | 0 | } |
772 | 2.43k | dir = !dir; /* invert direction next time */ |
773 | 2.43k | } while (changed); /* repeat until no more changes */ |
774 | 2.43k | } |
775 | | |
776 | | /* }====================================================== */ |
777 | | |
778 | | |
779 | | /* |
780 | | ** {====================================================== |
781 | | ** Sweep Functions |
782 | | ** ======================================================= |
783 | | */ |
784 | | |
785 | | |
786 | | /* |
787 | | ** clear entries with unmarked keys from all weaktables in list 'l' |
788 | | */ |
789 | 2.43k | static void clearbykeys (global_State *g, GCObject *l) { |
790 | 2.43k | for (; l; l = gco2t(l)->gclist) { |
791 | 0 | Table *h = gco2t(l); |
792 | 0 | Node *limit = gnodelast(h); |
793 | 0 | Node *n; |
794 | 0 | for (n = gnode(h, 0); n < limit; n++) { |
795 | 0 | if (iscleared(g, gckeyN(n))) /* unmarked key? */ |
796 | 0 | setempty(gval(n)); /* remove entry */ |
797 | 0 | if (isempty(gval(n))) /* is entry empty? */ |
798 | 0 | clearkey(n); /* clear its key */ |
799 | 0 | } |
800 | 0 | } |
801 | 2.43k | } |
802 | | |
803 | | |
804 | | /* |
805 | | ** clear entries with unmarked values from all weaktables in list 'l' up |
806 | | ** to element 'f' |
807 | | */ |
808 | 4.86k | static void clearbyvalues (global_State *g, GCObject *l, GCObject *f) { |
809 | 4.86k | for (; l != f; l = gco2t(l)->gclist) { |
810 | 0 | Table *h = gco2t(l); |
811 | 0 | Node *n, *limit = gnodelast(h); |
812 | 0 | unsigned int i; |
813 | 0 | unsigned int asize = h->asize; |
814 | 0 | for (i = 0; i < asize; i++) { |
815 | 0 | GCObject *o = gcvalarr(h, i); |
816 | 0 | if (iscleared(g, o)) /* value was collected? */ |
817 | 0 | *getArrTag(h, i) = LUA_VEMPTY; /* remove entry */ |
818 | 0 | } |
819 | 0 | for (n = gnode(h, 0); n < limit; n++) { |
820 | 0 | if (iscleared(g, gcvalueN(gval(n)))) /* unmarked value? */ |
821 | 0 | setempty(gval(n)); /* remove entry */ |
822 | 0 | if (isempty(gval(n))) /* is entry empty? */ |
823 | 0 | clearkey(n); /* clear its key */ |
824 | 0 | } |
825 | 0 | } |
826 | 4.86k | } |
827 | | |
828 | | |
829 | 108 | static void freeupval (lua_State *L, UpVal *uv) { |
830 | 108 | if (upisopen(uv)) |
831 | 0 | luaF_unlinkupval(uv); |
832 | 108 | luaM_free(L, uv); |
833 | 108 | } |
834 | | |
835 | | |
836 | 47.5k | static void freeobj (lua_State *L, GCObject *o) { |
837 | 47.5k | assert_code(l_mem newmem = gettotalbytes(G(L)) - objsize(o)); |
838 | 47.5k | switch (o->tt) { |
839 | 1.01k | case LUA_VPROTO: |
840 | 1.01k | luaF_freeproto(L, gco2p(o)); |
841 | 0 | break; |
842 | 108 | case LUA_VUPVAL: |
843 | 108 | freeupval(L, gco2upv(o)); |
844 | 0 | break; |
845 | 422 | case LUA_VLCL: { |
846 | 422 | LClosure *cl = gco2lcl(o); |
847 | 422 | luaM_freemem(L, cl, sizeLclosure(cl->nupvalues)); |
848 | 422 | break; |
849 | 422 | } |
850 | 0 | case LUA_VCCL: { |
851 | 0 | CClosure *cl = gco2ccl(o); |
852 | 0 | luaM_freemem(L, cl, sizeCclosure(cl->nupvalues)); |
853 | 0 | break; |
854 | 0 | } |
855 | 2.40k | case LUA_VTABLE: |
856 | 2.40k | luaH_free(L, gco2t(o)); |
857 | 0 | break; |
858 | 0 | case LUA_VTHREAD: |
859 | 0 | luaE_freethread(L, gco2th(o)); |
860 | 0 | break; |
861 | 53 | case LUA_VUSERDATA: { |
862 | 53 | Udata *u = gco2u(o); |
863 | 53 | luaM_freemem(L, o, sizeudata(u->nuvalue, u->len)); |
864 | 53 | break; |
865 | 53 | } |
866 | 37.9k | case LUA_VSHRSTR: { |
867 | 37.9k | TString *ts = gco2ts(o); |
868 | 0 | luaS_remove(L, ts); /* remove it from hash table */ |
869 | 37.9k | luaM_freemem(L, ts, sizestrshr(cast_uint(ts->shrlen))); |
870 | 37.9k | break; |
871 | 37.9k | } |
872 | 5.58k | case LUA_VLNGSTR: { |
873 | 5.58k | TString *ts = gco2ts(o); |
874 | 5.58k | if (ts->shrlen == LSTRMEM) /* must free external string? */ |
875 | 53 | (*ts->falloc)(ts->ud, ts->contents, ts->u.lnglen + 1, 0); |
876 | 5.58k | luaM_freemem(L, ts, luaS_sizelngstr(ts->u.lnglen, ts->shrlen)); |
877 | 5.58k | break; |
878 | 5.58k | } |
879 | 0 | default: lua_assert(0); |
880 | 47.5k | } |
881 | 47.5k | lua_assert(gettotalbytes(G(L)) == newmem); |
882 | 47.5k | } |
883 | | |
884 | | |
885 | | /* |
886 | | ** sweep at most 'countin' elements from a list of GCObjects erasing dead |
887 | | ** objects, where a dead object is one marked with the old (non current) |
888 | | ** white; change all non-dead objects back to white (and new), preparing |
889 | | ** for next collection cycle. Return where to continue the traversal or |
890 | | ** NULL if list is finished. |
891 | | */ |
892 | 5.97k | static GCObject **sweeplist (lua_State *L, GCObject **p, l_mem countin) { |
893 | 5.97k | global_State *g = G(L); |
894 | 5.97k | int ow = otherwhite(g); |
895 | 5.97k | int white = luaC_white(g); /* current white */ |
896 | 50.9k | while (*p != NULL && countin-- > 0) { |
897 | 44.9k | GCObject *curr = *p; |
898 | 44.9k | int marked = curr->marked; |
899 | 44.9k | if (isdeadm(ow, marked)) { /* is 'curr' dead? */ |
900 | 2.33k | *p = curr->next; /* remove 'curr' from list */ |
901 | 2.33k | freeobj(L, curr); /* erase 'curr' */ |
902 | 2.33k | } |
903 | 42.5k | else { /* change mark to 'white' and age to 'new' */ |
904 | 42.5k | curr->marked = cast_byte((marked & ~maskgcbits) | white | G_NEW); |
905 | 42.5k | p = &curr->next; /* go to next element */ |
906 | 42.5k | } |
907 | 44.9k | } |
908 | 5.97k | return (*p == NULL) ? NULL : p; |
909 | 5.97k | } |
910 | | |
911 | | |
912 | | /* |
913 | | ** sweep a list until a live object (or end of list) |
914 | | */ |
915 | 1.24k | static GCObject **sweeptolive (lua_State *L, GCObject **p) { |
916 | 1.24k | GCObject **old = p; |
917 | 1.25k | do { |
918 | 1.25k | p = sweeplist(L, p, 1); |
919 | 1.25k | } while (p == old); |
920 | 1.24k | return p; |
921 | 1.24k | } |
922 | | |
923 | | /* }====================================================== */ |
924 | | |
925 | | |
926 | | /* |
927 | | ** {====================================================== |
928 | | ** Finalization |
929 | | ** ======================================================= |
930 | | */ |
931 | | |
932 | | /* |
933 | | ** If possible, shrink string table. |
934 | | */ |
935 | 1.01k | static void checkSizes (lua_State *L, global_State *g) { |
936 | 1.01k | if (!g->gcemergency) { |
937 | 1.01k | if (g->strt.nuse < g->strt.size / 4) /* string table too big? */ |
938 | 0 | luaS_resize(L, g->strt.size / 2); |
939 | 1.01k | } |
940 | 1.01k | } |
941 | | |
942 | | |
943 | | /* |
944 | | ** Get the next udata to be finalized from the 'tobefnz' list, and |
945 | | ** link it back into the 'allgc' list. |
946 | | */ |
947 | 53 | static GCObject *udata2finalize (global_State *g) { |
948 | 53 | GCObject *o = g->tobefnz; /* get first element */ |
949 | 53 | lua_assert(tofinalize(o)); |
950 | 53 | g->tobefnz = o->next; /* remove it from 'tobefnz' list */ |
951 | 53 | o->next = g->allgc; /* return it to 'allgc' list */ |
952 | 53 | g->allgc = o; |
953 | 53 | resetbit(o->marked, FINALIZEDBIT); /* object is "normal" again */ |
954 | 53 | if (issweepphase(g)) |
955 | 4 | makewhite(g, o); /* "sweep" object */ |
956 | 49 | else if (getage(o) == G_OLD1) |
957 | 0 | g->firstold1 = o; /* it is the first OLD1 object in the list */ |
958 | 53 | return o; |
959 | 53 | } |
960 | | |
961 | | |
962 | 53 | static void dothecall (lua_State *L, void *ud) { |
963 | 53 | UNUSED(ud); |
964 | 53 | luaD_callnoyield(L, L->top.p - 2, 0); |
965 | 53 | } |
966 | | |
967 | | |
968 | 53 | static void GCTM (lua_State *L) { |
969 | 53 | global_State *g = G(L); |
970 | 53 | const TValue *tm; |
971 | 53 | TValue v; |
972 | 53 | lua_assert(!g->gcemergency); |
973 | 53 | setgcovalue(L, &v, udata2finalize(g)); |
974 | 53 | tm = luaT_gettmbyobj(L, &v, TM_GC); |
975 | 53 | if (!notm(tm)) { /* is there a finalizer? */ |
976 | 53 | TStatus status; |
977 | 53 | lu_byte oldah = L->allowhook; |
978 | 53 | lu_byte oldgcstp = g->gcstp; |
979 | 53 | g->gcstp |= GCSTPGC; /* avoid GC steps */ |
980 | 53 | L->allowhook = 0; /* stop debug hooks during GC metamethod */ |
981 | 53 | setobj2s(L, L->top.p++, tm); /* push finalizer... */ |
982 | 53 | setobj2s(L, L->top.p++, &v); /* ... and its argument */ |
983 | 53 | L->ci->callstatus |= CIST_FIN; /* will run a finalizer */ |
984 | 53 | status = luaD_pcall(L, dothecall, NULL, savestack(L, L->top.p - 2), 0); |
985 | 53 | L->ci->callstatus &= ~CIST_FIN; /* not running a finalizer anymore */ |
986 | 53 | L->allowhook = oldah; /* restore hooks */ |
987 | 53 | g->gcstp = oldgcstp; /* restore state */ |
988 | 53 | if (l_unlikely(status != LUA_OK)) { /* error while running __gc? */ |
989 | 0 | luaE_warnerror(L, "__gc"); |
990 | 0 | L->top.p--; /* pops error object */ |
991 | 0 | } |
992 | 53 | } |
993 | 53 | } |
994 | | |
995 | | |
996 | | /* |
997 | | ** call all pending finalizers |
998 | | */ |
999 | 422 | static void callallpendingfinalizers (lua_State *L) { |
1000 | 422 | global_State *g = G(L); |
1001 | 475 | while (g->tobefnz) |
1002 | 53 | GCTM(L); |
1003 | 422 | } |
1004 | | |
1005 | | |
1006 | | /* |
1007 | | ** find last 'next' field in list 'p' list (to add elements in its end) |
1008 | | */ |
1009 | 1.63k | static GCObject **findlast (GCObject **p) { |
1010 | 1.63k | while (*p != NULL) |
1011 | 1 | p = &(*p)->next; |
1012 | 1.63k | return p; |
1013 | 1.63k | } |
1014 | | |
1015 | | |
1016 | | /* |
1017 | | ** Move all unreachable objects (or 'all' objects) that need |
1018 | | ** finalization from list 'finobj' to list 'tobefnz' (to be finalized). |
1019 | | ** (Note that objects after 'finobjold1' cannot be white, so they |
1020 | | ** don't need to be traversed. In incremental mode, 'finobjold1' is NULL, |
1021 | | ** so the whole list is traversed.) |
1022 | | */ |
1023 | 1.63k | static void separatetobefnz (global_State *g, int all) { |
1024 | 1.63k | GCObject *curr; |
1025 | 1.63k | GCObject **p = &g->finobj; |
1026 | 1.63k | GCObject **lastnext = findlast(&g->tobefnz); |
1027 | 1.69k | while ((curr = *p) != g->finobjold1) { /* traverse all finalizable objects */ |
1028 | 53 | lua_assert(tofinalize(curr)); |
1029 | 53 | if (!(iswhite(curr) || all)) /* not being collected? */ |
1030 | 0 | p = &curr->next; /* don't bother with it */ |
1031 | 53 | else { |
1032 | 53 | if (curr == g->finobjsur) /* removing 'finobjsur'? */ |
1033 | 0 | g->finobjsur = curr->next; /* correct it */ |
1034 | 53 | *p = curr->next; /* remove 'curr' from 'finobj' list */ |
1035 | 53 | curr->next = *lastnext; /* link at the end of 'tobefnz' list */ |
1036 | 53 | *lastnext = curr; |
1037 | 53 | lastnext = &curr->next; |
1038 | 53 | } |
1039 | 53 | } |
1040 | 1.63k | } |
1041 | | |
1042 | | |
1043 | | /* |
1044 | | ** If pointer 'p' points to 'o', move it to the next element. |
1045 | | */ |
1046 | 4 | static void checkpointer (GCObject **p, GCObject *o) { |
1047 | 4 | if (o == *p) |
1048 | 0 | *p = o->next; |
1049 | 4 | } |
1050 | | |
1051 | | |
1052 | | /* |
1053 | | ** Correct pointers to objects inside 'allgc' list when |
1054 | | ** object 'o' is being removed from the list. |
1055 | | */ |
1056 | 1 | static void correctpointers (global_State *g, GCObject *o) { |
1057 | 1 | checkpointer(&g->survival, o); |
1058 | 1 | checkpointer(&g->old1, o); |
1059 | 1 | checkpointer(&g->reallyold, o); |
1060 | 1 | checkpointer(&g->firstold1, o); |
1061 | 1 | } |
1062 | | |
1063 | | |
1064 | | /* |
1065 | | ** if object 'o' has a finalizer, remove it from 'allgc' list (must |
1066 | | ** search the list to find it) and link it in 'finobj' list. |
1067 | | */ |
1068 | 53 | void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) { |
1069 | 53 | global_State *g = G(L); |
1070 | 53 | if (tofinalize(o) || /* obj. is already marked... */ |
1071 | 53 | gfasttm(g, mt, TM_GC) == NULL || /* or has no finalizer... */ |
1072 | 53 | (g->gcstp & GCSTPCLS)) /* or closing state? */ |
1073 | 0 | return; /* nothing to be done */ |
1074 | 53 | else { /* move 'o' to 'finobj' list */ |
1075 | 53 | GCObject **p; |
1076 | 53 | if (issweepphase(g)) { |
1077 | 52 | makewhite(g, o); /* "sweep" object 'o' */ |
1078 | 52 | if (g->sweepgc == &o->next) /* should not remove 'sweepgc' object */ |
1079 | 28 | g->sweepgc = sweeptolive(L, g->sweepgc); /* change 'sweepgc' */ |
1080 | 52 | } |
1081 | 1 | else |
1082 | 1 | correctpointers(g, o); |
1083 | | /* search for pointer pointing to 'o' */ |
1084 | 212 | for (p = &g->allgc; *p != o; p = &(*p)->next) { /* empty */ } |
1085 | 53 | *p = o->next; /* remove 'o' from 'allgc' list */ |
1086 | 53 | o->next = g->finobj; /* link it in 'finobj' list */ |
1087 | 53 | g->finobj = o; |
1088 | 53 | l_setbit(o->marked, FINALIZEDBIT); /* mark it as such */ |
1089 | 53 | } |
1090 | 53 | } |
1091 | | |
1092 | | /* }====================================================== */ |
1093 | | |
1094 | | |
1095 | | /* |
1096 | | ** {====================================================== |
1097 | | ** Generational Collector |
1098 | | ** ======================================================= |
1099 | | */ |
1100 | | |
1101 | | /* |
1102 | | ** Fields 'GCmarked' and 'GCmajorminor' are used to control the pace and |
1103 | | ** the mode of the collector. They play several roles, depending on the |
1104 | | ** mode of the collector: |
1105 | | ** * KGC_INC: |
1106 | | ** GCmarked: number of marked bytes during a cycle. |
1107 | | ** GCmajorminor: not used. |
1108 | | ** * KGC_GENMINOR |
1109 | | ** GCmarked: number of bytes that became old since last major collection. |
1110 | | ** GCmajorminor: number of bytes marked in last major collection. |
1111 | | ** * KGC_GENMAJOR |
1112 | | ** GCmarked: number of bytes that became old since last major collection. |
1113 | | ** GCmajorminor: number of bytes marked in last major collection. |
1114 | | */ |
1115 | | |
1116 | | |
1117 | | /* |
1118 | | ** Set the "time" to wait before starting a new incremental cycle; |
1119 | | ** cycle will start when number of bytes in use hits the threshold of |
1120 | | ** approximately (marked * pause / 100). |
1121 | | */ |
1122 | 1.01k | static void setpause (global_State *g) { |
1123 | 1.01k | l_mem threshold = applygcparam(g, PAUSE, g->GCmarked); |
1124 | 1.01k | l_mem debt = threshold - gettotalbytes(g); |
1125 | 1.01k | if (debt < 0) debt = 0; |
1126 | 1.01k | luaE_setdebt(g, debt); |
1127 | 1.01k | } |
1128 | | |
1129 | | |
1130 | | /* |
1131 | | ** Sweep a list of objects to enter generational mode. Deletes dead |
1132 | | ** objects and turns the non dead to old. All non-dead threads---which |
1133 | | ** are now old---must be in a gray list. Everything else is not in a |
1134 | | ** gray list. Open upvalues are also kept gray. |
1135 | | */ |
1136 | 0 | static void sweep2old (lua_State *L, GCObject **p) { |
1137 | 0 | GCObject *curr; |
1138 | 0 | global_State *g = G(L); |
1139 | 0 | while ((curr = *p) != NULL) { |
1140 | 0 | if (iswhite(curr)) { /* is 'curr' dead? */ |
1141 | 0 | lua_assert(isdead(g, curr)); |
1142 | 0 | *p = curr->next; /* remove 'curr' from list */ |
1143 | 0 | freeobj(L, curr); /* erase 'curr' */ |
1144 | 0 | } |
1145 | 0 | else { /* all surviving objects become old */ |
1146 | 0 | setage(curr, G_OLD); |
1147 | 0 | if (curr->tt == LUA_VTHREAD) { /* threads must be watched */ |
1148 | 0 | lua_State *th = gco2th(curr); |
1149 | 0 | linkgclist(th, g->grayagain); /* insert into 'grayagain' list */ |
1150 | 0 | } |
1151 | 0 | else if (curr->tt == LUA_VUPVAL && upisopen(gco2upv(curr))) |
1152 | 0 | set2gray(curr); /* open upvalues are always gray */ |
1153 | 0 | else /* everything else is black */ |
1154 | 0 | nw2black(curr); |
1155 | 0 | p = &curr->next; /* go to next element */ |
1156 | 0 | } |
1157 | 0 | } |
1158 | 0 | } |
1159 | | |
1160 | | |
1161 | | /* |
1162 | | ** Sweep for generational mode. Delete dead objects. (Because the |
1163 | | ** collection is not incremental, there are no "new white" objects |
1164 | | ** during the sweep. So, any white object must be dead.) For |
1165 | | ** non-dead objects, advance their ages and clear the color of |
1166 | | ** new objects. (Old objects keep their colors.) |
1167 | | ** The ages of G_TOUCHED1 and G_TOUCHED2 objects cannot be advanced |
1168 | | ** here, because these old-generation objects are usually not swept |
1169 | | ** here. They will all be advanced in 'correctgraylist'. That function |
1170 | | ** will also remove objects turned white here from any gray list. |
1171 | | */ |
1172 | | static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p, |
1173 | | GCObject *limit, GCObject **pfirstold1, |
1174 | 0 | l_mem *paddedold) { |
1175 | 0 | static const lu_byte nextage[] = { |
1176 | 0 | G_SURVIVAL, /* from G_NEW */ |
1177 | 0 | G_OLD1, /* from G_SURVIVAL */ |
1178 | 0 | G_OLD1, /* from G_OLD0 */ |
1179 | 0 | G_OLD, /* from G_OLD1 */ |
1180 | 0 | G_OLD, /* from G_OLD (do not change) */ |
1181 | 0 | G_TOUCHED1, /* from G_TOUCHED1 (do not change) */ |
1182 | 0 | G_TOUCHED2 /* from G_TOUCHED2 (do not change) */ |
1183 | 0 | }; |
1184 | 0 | l_mem addedold = 0; |
1185 | 0 | int white = luaC_white(g); |
1186 | 0 | GCObject *curr; |
1187 | 0 | while ((curr = *p) != limit) { |
1188 | 0 | if (iswhite(curr)) { /* is 'curr' dead? */ |
1189 | 0 | lua_assert(!isold(curr) && isdead(g, curr)); |
1190 | 0 | *p = curr->next; /* remove 'curr' from list */ |
1191 | 0 | freeobj(L, curr); /* erase 'curr' */ |
1192 | 0 | } |
1193 | 0 | else { /* correct mark and age */ |
1194 | 0 | int age = getage(curr); |
1195 | 0 | if (age == G_NEW) { /* new objects go back to white */ |
1196 | 0 | int marked = curr->marked & ~maskgcbits; /* erase GC bits */ |
1197 | 0 | curr->marked = cast_byte(marked | G_SURVIVAL | white); |
1198 | 0 | } |
1199 | 0 | else { /* all other objects will be old, and so keep their color */ |
1200 | 0 | lua_assert(age != G_OLD1); /* advanced in 'markold' */ |
1201 | 0 | setage(curr, nextage[age]); |
1202 | 0 | if (getage(curr) == G_OLD1) { |
1203 | 0 | addedold += objsize(curr); /* bytes becoming old */ |
1204 | 0 | if (*pfirstold1 == NULL) |
1205 | 0 | *pfirstold1 = curr; /* first OLD1 object in the list */ |
1206 | 0 | } |
1207 | 0 | } |
1208 | 0 | p = &curr->next; /* go to next element */ |
1209 | 0 | } |
1210 | 0 | } |
1211 | 0 | *paddedold += addedold; |
1212 | 0 | return p; |
1213 | 0 | } |
1214 | | |
1215 | | |
1216 | | /* |
1217 | | ** Correct a list of gray objects. Return a pointer to the last element |
1218 | | ** left on the list, so that we can link another list to the end of |
1219 | | ** this one. |
1220 | | ** Because this correction is done after sweeping, young objects might |
1221 | | ** be turned white and still be in the list. They are only removed. |
1222 | | ** 'TOUCHED1' objects are advanced to 'TOUCHED2' and remain on the list; |
1223 | | ** Non-white threads also remain on the list. 'TOUCHED2' objects and |
1224 | | ** anything else become regular old, are marked black, and are removed |
1225 | | ** from the list. |
1226 | | */ |
1227 | 0 | static GCObject **correctgraylist (GCObject **p) { |
1228 | 0 | GCObject *curr; |
1229 | 0 | while ((curr = *p) != NULL) { |
1230 | 0 | GCObject **next = getgclist(curr); |
1231 | 0 | if (iswhite(curr)) |
1232 | 0 | goto remove; /* remove all white objects */ |
1233 | 0 | else if (getage(curr) == G_TOUCHED1) { /* touched in this cycle? */ |
1234 | 0 | lua_assert(isgray(curr)); |
1235 | 0 | nw2black(curr); /* make it black, for next barrier */ |
1236 | 0 | setage(curr, G_TOUCHED2); |
1237 | 0 | goto remain; /* keep it in the list and go to next element */ |
1238 | 0 | } |
1239 | 0 | else if (curr->tt == LUA_VTHREAD) { |
1240 | 0 | lua_assert(isgray(curr)); |
1241 | 0 | goto remain; /* keep non-white threads on the list */ |
1242 | 0 | } |
1243 | 0 | else { /* everything else is removed */ |
1244 | 0 | lua_assert(isold(curr)); /* young objects should be white here */ |
1245 | 0 | if (getage(curr) == G_TOUCHED2) /* advance from TOUCHED2... */ |
1246 | 0 | setage(curr, G_OLD); /* ... to OLD */ |
1247 | 0 | nw2black(curr); /* make object black (to be removed) */ |
1248 | 0 | goto remove; |
1249 | 0 | } |
1250 | 0 | remove: *p = *next; continue; |
1251 | 0 | remain: p = next; continue; |
1252 | 0 | } |
1253 | 0 | return p; |
1254 | 0 | } |
1255 | | |
1256 | | |
1257 | | /* |
1258 | | ** Correct all gray lists, coalescing them into 'grayagain'. |
1259 | | */ |
1260 | 0 | static void correctgraylists (global_State *g) { |
1261 | 0 | GCObject **list = correctgraylist(&g->grayagain); |
1262 | 0 | *list = g->weak; g->weak = NULL; |
1263 | 0 | list = correctgraylist(list); |
1264 | 0 | *list = g->allweak; g->allweak = NULL; |
1265 | 0 | list = correctgraylist(list); |
1266 | 0 | *list = g->ephemeron; g->ephemeron = NULL; |
1267 | 0 | correctgraylist(list); |
1268 | 0 | } |
1269 | | |
1270 | | |
1271 | | /* |
1272 | | ** Mark black 'OLD1' objects when starting a new young collection. |
1273 | | ** Gray objects are already in some gray list, and so will be visited in |
1274 | | ** the atomic step. |
1275 | | */ |
1276 | 0 | static void markold (global_State *g, GCObject *from, GCObject *to) { |
1277 | 0 | GCObject *p; |
1278 | 0 | for (p = from; p != to; p = p->next) { |
1279 | 0 | if (getage(p) == G_OLD1) { |
1280 | 0 | lua_assert(!iswhite(p)); |
1281 | 0 | setage(p, G_OLD); /* now they are old */ |
1282 | 0 | if (isblack(p)) |
1283 | 0 | reallymarkobject(g, p); |
1284 | 0 | } |
1285 | 0 | } |
1286 | 0 | } |
1287 | | |
1288 | | |
1289 | | /* |
1290 | | ** Finish a young-generation collection. |
1291 | | */ |
1292 | 0 | static void finishgencycle (lua_State *L, global_State *g) { |
1293 | 0 | correctgraylists(g); |
1294 | 0 | checkSizes(L, g); |
1295 | 0 | g->gcstate = GCSpropagate; /* skip restart */ |
1296 | 0 | if (!g->gcemergency) |
1297 | 0 | callallpendingfinalizers(L); |
1298 | 0 | } |
1299 | | |
1300 | | |
1301 | | /* |
1302 | | ** Shifts from a minor collection to major collections. It starts in |
1303 | | ** the "sweep all" state to clear all objects, which are mostly black |
1304 | | ** in generational mode. |
1305 | | */ |
1306 | 0 | static void minor2inc (lua_State *L, global_State *g, lu_byte kind) { |
1307 | 0 | g->GCmajorminor = g->GCmarked; /* number of live bytes */ |
1308 | 0 | g->gckind = kind; |
1309 | 0 | g->reallyold = g->old1 = g->survival = NULL; |
1310 | 0 | g->finobjrold = g->finobjold1 = g->finobjsur = NULL; |
1311 | 0 | entersweep(L); /* continue as an incremental cycle */ |
1312 | | /* set a debt equal to the step size */ |
1313 | 0 | luaE_setdebt(g, applygcparam(g, STEPSIZE, 100)); |
1314 | 0 | } |
1315 | | |
1316 | | |
1317 | | /* |
1318 | | ** Decide whether to shift to major mode. It shifts if the accumulated |
1319 | | ** number of added old bytes (counted in 'GCmarked') is larger than |
1320 | | ** 'minormajor'% of the number of lived bytes after the last major |
1321 | | ** collection. (This number is kept in 'GCmajorminor'.) |
1322 | | */ |
1323 | 0 | static int checkminormajor (global_State *g) { |
1324 | 0 | l_mem limit = applygcparam(g, MINORMAJOR, g->GCmajorminor); |
1325 | 0 | if (limit == 0) |
1326 | 0 | return 0; /* special case: 'minormajor' 0 stops major collections */ |
1327 | 0 | return (g->GCmarked >= limit); |
1328 | 0 | } |
1329 | | |
1330 | | /* |
1331 | | ** Does a young collection. First, mark 'OLD1' objects. Then does the |
1332 | | ** atomic step. Then, check whether to continue in minor mode. If so, |
1333 | | ** sweep all lists and advance pointers. Finally, finish the collection. |
1334 | | */ |
1335 | 0 | static void youngcollection (lua_State *L, global_State *g) { |
1336 | 0 | l_mem addedold1 = 0; |
1337 | 0 | l_mem marked = g->GCmarked; /* preserve 'g->GCmarked' */ |
1338 | 0 | GCObject **psurvival; /* to point to first non-dead survival object */ |
1339 | 0 | GCObject *dummy; /* dummy out parameter to 'sweepgen' */ |
1340 | 0 | lua_assert(g->gcstate == GCSpropagate); |
1341 | 0 | if (g->firstold1) { /* are there regular OLD1 objects? */ |
1342 | 0 | markold(g, g->firstold1, g->reallyold); /* mark them */ |
1343 | 0 | g->firstold1 = NULL; /* no more OLD1 objects (for now) */ |
1344 | 0 | } |
1345 | 0 | markold(g, g->finobj, g->finobjrold); |
1346 | 0 | markold(g, g->tobefnz, NULL); |
1347 | |
|
1348 | 0 | atomic(L); /* will lose 'g->marked' */ |
1349 | | |
1350 | | /* sweep nursery and get a pointer to its last live element */ |
1351 | 0 | g->gcstate = GCSswpallgc; |
1352 | 0 | psurvival = sweepgen(L, g, &g->allgc, g->survival, &g->firstold1, &addedold1); |
1353 | | /* sweep 'survival' */ |
1354 | 0 | sweepgen(L, g, psurvival, g->old1, &g->firstold1, &addedold1); |
1355 | 0 | g->reallyold = g->old1; |
1356 | 0 | g->old1 = *psurvival; /* 'survival' survivals are old now */ |
1357 | 0 | g->survival = g->allgc; /* all news are survivals */ |
1358 | | |
1359 | | /* repeat for 'finobj' lists */ |
1360 | 0 | dummy = NULL; /* no 'firstold1' optimization for 'finobj' lists */ |
1361 | 0 | psurvival = sweepgen(L, g, &g->finobj, g->finobjsur, &dummy, &addedold1); |
1362 | | /* sweep 'survival' */ |
1363 | 0 | sweepgen(L, g, psurvival, g->finobjold1, &dummy, &addedold1); |
1364 | 0 | g->finobjrold = g->finobjold1; |
1365 | 0 | g->finobjold1 = *psurvival; /* 'survival' survivals are old now */ |
1366 | 0 | g->finobjsur = g->finobj; /* all news are survivals */ |
1367 | |
|
1368 | 0 | sweepgen(L, g, &g->tobefnz, NULL, &dummy, &addedold1); |
1369 | | |
1370 | | /* keep total number of added old1 bytes */ |
1371 | 0 | g->GCmarked = marked + addedold1; |
1372 | | |
1373 | | /* decide whether to shift to major mode */ |
1374 | 0 | if (checkminormajor(g)) { |
1375 | 0 | minor2inc(L, g, KGC_GENMAJOR); /* go to major mode */ |
1376 | 0 | g->GCmarked = 0; /* avoid pause in first major cycle (see 'setpause') */ |
1377 | 0 | } |
1378 | 0 | else |
1379 | 0 | finishgencycle(L, g); /* still in minor mode; finish it */ |
1380 | 0 | } |
1381 | | |
1382 | | |
1383 | | /* |
1384 | | ** Clears all gray lists, sweeps objects, and prepare sublists to enter |
1385 | | ** generational mode. The sweeps remove dead objects and turn all |
1386 | | ** surviving objects to old. Threads go back to 'grayagain'; everything |
1387 | | ** else is turned black (not in any gray list). |
1388 | | */ |
1389 | 0 | static void atomic2gen (lua_State *L, global_State *g) { |
1390 | 0 | cleargraylists(g); |
1391 | | /* sweep all elements making them old */ |
1392 | 0 | g->gcstate = GCSswpallgc; |
1393 | 0 | sweep2old(L, &g->allgc); |
1394 | | /* everything alive now is old */ |
1395 | 0 | g->reallyold = g->old1 = g->survival = g->allgc; |
1396 | 0 | g->firstold1 = NULL; /* there are no OLD1 objects anywhere */ |
1397 | | |
1398 | | /* repeat for 'finobj' lists */ |
1399 | 0 | sweep2old(L, &g->finobj); |
1400 | 0 | g->finobjrold = g->finobjold1 = g->finobjsur = g->finobj; |
1401 | |
|
1402 | 0 | sweep2old(L, &g->tobefnz); |
1403 | |
|
1404 | 0 | g->gckind = KGC_GENMINOR; |
1405 | 0 | g->GCmajorminor = g->GCmarked; /* "base" for number of bytes */ |
1406 | 0 | g->GCmarked = 0; /* to count the number of added old1 bytes */ |
1407 | 0 | finishgencycle(L, g); |
1408 | 0 | } |
1409 | | |
1410 | | |
1411 | | /* |
1412 | | ** Set debt for the next minor collection, which will happen when |
1413 | | ** total number of bytes grows 'genminormul'% in relation to |
1414 | | ** the base, GCmajorminor, which is the number of bytes being used |
1415 | | ** after the last major collection. |
1416 | | */ |
1417 | 0 | static void setminordebt (global_State *g) { |
1418 | 0 | luaE_setdebt(g, applygcparam(g, MINORMUL, g->GCmajorminor)); |
1419 | 0 | } |
1420 | | |
1421 | | |
1422 | | /* |
1423 | | ** Enter generational mode. Must go until the end of an atomic cycle |
1424 | | ** to ensure that all objects are correctly marked and weak tables |
1425 | | ** are cleared. Then, turn all objects into old and finishes the |
1426 | | ** collection. |
1427 | | */ |
1428 | 0 | static void entergen (lua_State *L, global_State *g) { |
1429 | 0 | luaC_runtilstate(L, GCSpause, 1); /* prepare to start a new cycle */ |
1430 | 0 | luaC_runtilstate(L, GCSpropagate, 1); /* start new cycle */ |
1431 | 0 | atomic(L); /* propagates all and then do the atomic stuff */ |
1432 | 0 | atomic2gen(L, g); |
1433 | 0 | setminordebt(g); /* set debt assuming next cycle will be minor */ |
1434 | 0 | } |
1435 | | |
1436 | | |
1437 | | /* |
1438 | | ** Change collector mode to 'newmode'. |
1439 | | */ |
1440 | 422 | void luaC_changemode (lua_State *L, int newmode) { |
1441 | 422 | global_State *g = G(L); |
1442 | 422 | if (g->gckind == KGC_GENMAJOR) /* doing major collections? */ |
1443 | 0 | g->gckind = KGC_INC; /* already incremental but in name */ |
1444 | 422 | if (newmode != g->gckind) { /* does it need to change? */ |
1445 | 0 | if (newmode == KGC_INC) /* entering incremental mode? */ |
1446 | 0 | minor2inc(L, g, KGC_INC); /* entering incremental mode */ |
1447 | 0 | else { |
1448 | 0 | lua_assert(newmode == KGC_GENMINOR); |
1449 | 0 | entergen(L, g); |
1450 | 0 | } |
1451 | 0 | } |
1452 | 422 | } |
1453 | | |
1454 | | |
1455 | | /* |
1456 | | ** Does a full collection in generational mode. |
1457 | | */ |
1458 | 0 | static void fullgen (lua_State *L, global_State *g) { |
1459 | 0 | minor2inc(L, g, KGC_INC); |
1460 | 0 | entergen(L, g); |
1461 | 0 | } |
1462 | | |
1463 | | |
1464 | | /* |
1465 | | ** After an atomic incremental step from a major collection, |
1466 | | ** check whether collector could return to minor collections. |
1467 | | ** It checks whether the number of bytes 'tobecollected' |
1468 | | ** is greater than 'majorminor'% of the number of bytes added |
1469 | | ** since the last collection ('addedbytes'). |
1470 | | */ |
1471 | 1.21k | static int checkmajorminor (lua_State *L, global_State *g) { |
1472 | 1.21k | if (g->gckind == KGC_GENMAJOR) { /* generational mode? */ |
1473 | 0 | l_mem numbytes = gettotalbytes(g); |
1474 | 0 | l_mem addedbytes = numbytes - g->GCmajorminor; |
1475 | 0 | l_mem limit = applygcparam(g, MAJORMINOR, addedbytes); |
1476 | 0 | l_mem tobecollected = numbytes - g->GCmarked; |
1477 | 0 | if (tobecollected > limit) { |
1478 | 0 | atomic2gen(L, g); /* return to generational mode */ |
1479 | 0 | setminordebt(g); |
1480 | 0 | return 1; /* exit incremental collection */ |
1481 | 0 | } |
1482 | 0 | } |
1483 | 1.21k | g->GCmajorminor = g->GCmarked; /* prepare for next collection */ |
1484 | 1.21k | return 0; /* stay doing incremental collections */ |
1485 | 1.21k | } |
1486 | | |
1487 | | /* }====================================================== */ |
1488 | | |
1489 | | |
1490 | | /* |
1491 | | ** {====================================================== |
1492 | | ** GC control |
1493 | | ** ======================================================= |
1494 | | */ |
1495 | | |
1496 | | |
1497 | | /* |
1498 | | ** Enter first sweep phase. |
1499 | | ** The call to 'sweeptolive' makes the pointer point to an object |
1500 | | ** inside the list (instead of to the header), so that the real sweep do |
1501 | | ** not need to skip objects created between "now" and the start of the |
1502 | | ** real sweep. |
1503 | | */ |
1504 | 1.21k | static void entersweep (lua_State *L) { |
1505 | 1.21k | global_State *g = G(L); |
1506 | 1.21k | g->gcstate = GCSswpallgc; |
1507 | 1.21k | lua_assert(g->sweepgc == NULL); |
1508 | 1.21k | g->sweepgc = sweeptolive(L, &g->allgc); |
1509 | 1.21k | } |
1510 | | |
1511 | | |
1512 | | /* |
1513 | | ** Delete all objects in list 'p' until (but not including) object |
1514 | | ** 'limit'. |
1515 | | */ |
1516 | 844 | static void deletelist (lua_State *L, GCObject *p, GCObject *limit) { |
1517 | 46.0k | while (p != limit) { |
1518 | 45.1k | GCObject *next = p->next; |
1519 | 45.1k | freeobj(L, p); |
1520 | 45.1k | p = next; |
1521 | 45.1k | } |
1522 | 844 | } |
1523 | | |
1524 | | |
1525 | | /* |
1526 | | ** Call all finalizers of the objects in the given Lua state, and |
1527 | | ** then free all objects, except for the main thread. |
1528 | | */ |
1529 | 422 | void luaC_freeallobjects (lua_State *L) { |
1530 | 422 | global_State *g = G(L); |
1531 | 422 | g->gcstp = GCSTPCLS; /* no extra finalizers after here */ |
1532 | 422 | luaC_changemode(L, KGC_INC); |
1533 | 422 | separatetobefnz(g, 1); /* separate all objects with finalizers */ |
1534 | 422 | lua_assert(g->finobj == NULL); |
1535 | 422 | callallpendingfinalizers(L); |
1536 | 422 | deletelist(L, g->allgc, obj2gco(mainthread(g))); |
1537 | 422 | lua_assert(g->finobj == NULL); /* no new finalizers */ |
1538 | 422 | deletelist(L, g->fixedgc, NULL); /* collect fixed objects */ |
1539 | 422 | lua_assert(g->strt.nuse == 0); |
1540 | 422 | } |
1541 | | |
1542 | | |
1543 | 1.21k | static void atomic (lua_State *L) { |
1544 | 1.21k | global_State *g = G(L); |
1545 | 1.21k | GCObject *origweak, *origall; |
1546 | 1.21k | GCObject *grayagain = g->grayagain; /* save original list */ |
1547 | 1.21k | g->grayagain = NULL; |
1548 | 1.21k | lua_assert(g->ephemeron == NULL && g->weak == NULL); |
1549 | 1.21k | lua_assert(!iswhite(mainthread(g))); |
1550 | 1.21k | g->gcstate = GCSatomic; |
1551 | 1.21k | markobject(g, L); /* mark running thread */ |
1552 | | /* registry and global metatables may be changed by API */ |
1553 | 1.21k | markvalue(g, &g->l_registry); |
1554 | 1.21k | markmt(g); /* mark global metatables */ |
1555 | 1.21k | propagateall(g); /* empties 'gray' list */ |
1556 | | /* remark occasional upvalues of (maybe) dead threads */ |
1557 | 1.21k | remarkupvals(g); |
1558 | 1.21k | propagateall(g); /* propagate changes */ |
1559 | 1.21k | g->gray = grayagain; |
1560 | 1.21k | propagateall(g); /* traverse 'grayagain' list */ |
1561 | 1.21k | convergeephemerons(g); |
1562 | | /* at this point, all strongly accessible objects are marked. */ |
1563 | | /* Clear values from weak tables, before checking finalizers */ |
1564 | 1.21k | clearbyvalues(g, g->weak, NULL); |
1565 | 1.21k | clearbyvalues(g, g->allweak, NULL); |
1566 | 1.21k | origweak = g->weak; origall = g->allweak; |
1567 | 1.21k | separatetobefnz(g, 0); /* separate objects to be finalized */ |
1568 | 1.21k | markbeingfnz(g); /* mark objects that will be finalized */ |
1569 | 1.21k | propagateall(g); /* remark, to propagate 'resurrection' */ |
1570 | 1.21k | convergeephemerons(g); |
1571 | | /* at this point, all resurrected objects are marked. */ |
1572 | | /* remove dead objects from weak tables */ |
1573 | 1.21k | clearbykeys(g, g->ephemeron); /* clear keys from all ephemeron */ |
1574 | 1.21k | clearbykeys(g, g->allweak); /* clear keys from all 'allweak' */ |
1575 | | /* clear values from resurrected weak tables */ |
1576 | 1.21k | clearbyvalues(g, g->weak, origweak); |
1577 | 1.21k | clearbyvalues(g, g->allweak, origall); |
1578 | 1.21k | luaS_clearcache(g); |
1579 | 1.21k | g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */ |
1580 | 1.21k | lua_assert(g->gray == NULL); |
1581 | 1.21k | } |
1582 | | |
1583 | | |
1584 | | /* |
1585 | | ** Do a sweep step. The normal case (not fast) sweeps at most GCSWEEPMAX |
1586 | | ** elements. The fast case sweeps the whole list. |
1587 | | */ |
1588 | | static void sweepstep (lua_State *L, global_State *g, |
1589 | 7.75k | lu_byte nextstate, GCObject **nextlist, int fast) { |
1590 | 7.75k | if (g->sweepgc) |
1591 | 4.72k | g->sweepgc = sweeplist(L, g->sweepgc, fast ? MAX_LMEM : GCSWEEPMAX); |
1592 | 3.03k | else { /* enter next state */ |
1593 | 3.03k | g->gcstate = nextstate; |
1594 | 3.03k | g->sweepgc = nextlist; |
1595 | 3.03k | } |
1596 | 7.75k | } |
1597 | | |
1598 | | |
1599 | | /* |
1600 | | ** Performs one incremental "step" in an incremental garbage collection. |
1601 | | ** For indivisible work, a step goes to the next state. When marking |
1602 | | ** (propagating), a step traverses one object. When sweeping, a step |
1603 | | ** sweeps GCSWEEPMAX objects, to avoid a big overhead for sweeping |
1604 | | ** objects one by one. (Sweeping is inexpensive, no matter the |
1605 | | ** object.) When 'fast' is true, 'singlestep' tries to finish a state |
1606 | | ** "as fast as possible". In particular, it skips the propagation |
1607 | | ** phase and leaves all objects to be traversed by the atomic phase: |
1608 | | ** That avoids traversing twice some objects, such as threads and |
1609 | | ** weak tables. |
1610 | | */ |
1611 | | |
1612 | 47.6k | #define step2pause -3 /* finished collection; entered pause state */ |
1613 | 45.7k | #define atomicstep -2 /* atomic step */ |
1614 | 23.2k | #define step2minor -1 /* moved to minor collections */ |
1615 | | |
1616 | | |
1617 | 23.2k | static l_mem singlestep (lua_State *L, int fast) { |
1618 | 23.2k | global_State *g = G(L); |
1619 | 23.2k | l_mem stepresult; |
1620 | 23.2k | lua_assert(!g->gcstopem); /* collector is not reentrant */ |
1621 | 23.2k | g->gcstopem = 1; /* no emergency collections while collecting */ |
1622 | 23.2k | switch (g->gcstate) { |
1623 | 1.23k | case GCSpause: { |
1624 | 1.23k | restartcollection(g); |
1625 | 1.23k | g->gcstate = GCSpropagate; |
1626 | 1.23k | stepresult = 1; |
1627 | 1.23k | break; |
1628 | 0 | } |
1629 | 11.0k | case GCSpropagate: { |
1630 | 11.0k | if (fast || g->gray == NULL) { |
1631 | 1.21k | g->gcstate = GCSenteratomic; /* finish propagate phase */ |
1632 | 1.21k | stepresult = 1; |
1633 | 1.21k | } |
1634 | 9.86k | else |
1635 | 9.86k | stepresult = propagatemark(g); /* traverse one gray object */ |
1636 | 11.0k | break; |
1637 | 0 | } |
1638 | 1.21k | case GCSenteratomic: { |
1639 | 1.21k | atomic(L); |
1640 | 1.21k | if (checkmajorminor(L, g)) |
1641 | 0 | stepresult = step2minor; |
1642 | 1.21k | else { |
1643 | 1.21k | entersweep(L); |
1644 | 1.21k | stepresult = atomicstep; |
1645 | 1.21k | } |
1646 | 1.21k | break; |
1647 | 0 | } |
1648 | 3.71k | case GCSswpallgc: { /* sweep "regular" objects */ |
1649 | 3.71k | sweepstep(L, g, GCSswpfinobj, &g->finobj, fast); |
1650 | 3.71k | stepresult = GCSWEEPMAX; |
1651 | 3.71k | break; |
1652 | 0 | } |
1653 | 2.02k | case GCSswpfinobj: { /* sweep objects with finalizers */ |
1654 | 2.02k | sweepstep(L, g, GCSswptobefnz, &g->tobefnz, fast); |
1655 | 2.02k | stepresult = GCSWEEPMAX; |
1656 | 2.02k | break; |
1657 | 0 | } |
1658 | 2.02k | case GCSswptobefnz: { /* sweep objects to be finalized */ |
1659 | 2.02k | sweepstep(L, g, GCSswpend, NULL, fast); |
1660 | 2.02k | stepresult = GCSWEEPMAX; |
1661 | 2.02k | break; |
1662 | 0 | } |
1663 | 1.01k | case GCSswpend: { /* finish sweeps */ |
1664 | 1.01k | checkSizes(L, g); |
1665 | 1.01k | g->gcstate = GCScallfin; |
1666 | 1.01k | stepresult = GCSWEEPMAX; |
1667 | 1.01k | break; |
1668 | 0 | } |
1669 | 1.01k | case GCScallfin: { /* call finalizers */ |
1670 | 1.01k | if (g->tobefnz && !g->gcemergency) { |
1671 | 0 | g->gcstopem = 0; /* ok collections during finalizers */ |
1672 | 0 | GCTM(L); /* call one finalizer */ |
1673 | 0 | stepresult = CWUFIN; |
1674 | 0 | } |
1675 | 1.01k | else { /* emergency mode or no more finalizers */ |
1676 | 1.01k | g->gcstate = GCSpause; /* finish collection */ |
1677 | 1.01k | stepresult = step2pause; |
1678 | 1.01k | } |
1679 | 1.01k | break; |
1680 | 0 | } |
1681 | 0 | default: lua_assert(0); return 0; |
1682 | 23.2k | } |
1683 | 23.2k | g->gcstopem = 0; |
1684 | 23.2k | return stepresult; |
1685 | 23.2k | } |
1686 | | |
1687 | | |
1688 | | /* |
1689 | | ** Advances the garbage collector until it reaches the given state. |
1690 | | ** (The option 'fast' is only for testing; in normal code, 'fast' |
1691 | | ** here is always true.) |
1692 | | */ |
1693 | 0 | void luaC_runtilstate (lua_State *L, int state, int fast) { |
1694 | 0 | global_State *g = G(L); |
1695 | 0 | lua_assert(g->gckind == KGC_INC); |
1696 | 0 | while (state != g->gcstate) |
1697 | 0 | singlestep(L, fast); |
1698 | 0 | } |
1699 | | |
1700 | | |
1701 | | |
1702 | | /* |
1703 | | ** Performs a basic incremental step. The step size is |
1704 | | ** converted from bytes to "units of work"; then the function loops |
1705 | | ** running single steps until adding that many units of work or |
1706 | | ** finishing a cycle (pause state). Finally, it sets the debt that |
1707 | | ** controls when next step will be performed. |
1708 | | */ |
1709 | 2.31k | static void incstep (lua_State *L, global_State *g) { |
1710 | 2.31k | l_mem stepsize = applygcparam(g, STEPSIZE, 100); |
1711 | 2.31k | l_mem work2do = applygcparam(g, STEPMUL, stepsize / cast_int(sizeof(void*))); |
1712 | 2.31k | l_mem stres; |
1713 | 2.31k | int fast = (work2do == 0); /* special case: do a full collection */ |
1714 | 23.2k | do { /* repeat until enough work */ |
1715 | 23.2k | stres = singlestep(L, fast); /* perform one single step */ |
1716 | 23.2k | if (stres == step2minor) /* returned to minor collections? */ |
1717 | 0 | return; /* nothing else to be done here */ |
1718 | 23.2k | else if (stres == step2pause || (stres == atomicstep && !fast)) |
1719 | 2.22k | break; /* end of cycle or atomic */ |
1720 | 21.0k | else |
1721 | 21.0k | work2do -= stres; |
1722 | 23.2k | } while (fast || work2do > 0); |
1723 | 2.31k | if (g->gcstate == GCSpause) |
1724 | 1.01k | setpause(g); /* pause until next cycle */ |
1725 | 1.30k | else |
1726 | 1.30k | luaE_setdebt(g, stepsize); |
1727 | 2.31k | } |
1728 | | |
1729 | | |
1730 | | #if !defined(luai_tracegc) |
1731 | 4.62k | #define luai_tracegc(L,f) ((void)0) |
1732 | | #endif |
1733 | | |
1734 | | /* |
1735 | | ** Performs a basic GC step if collector is running. (If collector was |
1736 | | ** stopped by the user, set a reasonable debt to avoid it being called |
1737 | | ** at every single check.) |
1738 | | */ |
1739 | 2.31k | void luaC_step (lua_State *L) { |
1740 | 2.31k | global_State *g = G(L); |
1741 | 2.31k | lua_assert(!g->gcemergency); |
1742 | 2.31k | if (!gcrunning(g)) { /* not running? */ |
1743 | 0 | if (g->gcstp & GCSTPUSR) /* stopped by the user? */ |
1744 | 0 | luaE_setdebt(g, 20000); |
1745 | 0 | } |
1746 | 2.31k | else { |
1747 | 2.31k | luai_tracegc(L, 1); /* for internal debugging */ |
1748 | 2.31k | switch (g->gckind) { |
1749 | 2.31k | case KGC_INC: case KGC_GENMAJOR: |
1750 | 2.31k | incstep(L, g); |
1751 | 2.31k | break; |
1752 | 0 | case KGC_GENMINOR: |
1753 | 0 | youngcollection(L, g); |
1754 | 0 | setminordebt(g); |
1755 | 0 | break; |
1756 | 2.31k | } |
1757 | 2.31k | luai_tracegc(L, 0); /* for internal debugging */ |
1758 | 2.31k | } |
1759 | 2.31k | } |
1760 | | |
1761 | | |
1762 | | /* |
1763 | | ** Perform a full collection in incremental mode. |
1764 | | ** Before running the collection, check 'keepinvariant'; if it is true, |
1765 | | ** there may be some objects marked as black, so the collector has |
1766 | | ** to sweep all objects to turn them back to white (as white has not |
1767 | | ** changed, nothing will be collected). |
1768 | | */ |
1769 | 0 | static void fullinc (lua_State *L, global_State *g) { |
1770 | 0 | if (keepinvariant(g)) /* black objects? */ |
1771 | 0 | entersweep(L); /* sweep everything to turn them back to white */ |
1772 | | /* finish any pending sweep phase to start a new cycle */ |
1773 | 0 | luaC_runtilstate(L, GCSpause, 1); |
1774 | 0 | luaC_runtilstate(L, GCScallfin, 1); /* run up to finalizers */ |
1775 | 0 | luaC_runtilstate(L, GCSpause, 1); /* finish collection */ |
1776 | 0 | setpause(g); |
1777 | 0 | } |
1778 | | |
1779 | | |
1780 | | /* |
1781 | | ** Performs a full GC cycle; if 'isemergency', set a flag to avoid |
1782 | | ** some operations which could change the interpreter state in some |
1783 | | ** unexpected ways (running finalizers and shrinking some structures). |
1784 | | */ |
1785 | 0 | void luaC_fullgc (lua_State *L, int isemergency) { |
1786 | 0 | global_State *g = G(L); |
1787 | 0 | lua_assert(!g->gcemergency); |
1788 | 0 | g->gcemergency = cast_byte(isemergency); /* set flag */ |
1789 | 0 | switch (g->gckind) { |
1790 | 0 | case KGC_GENMINOR: fullgen(L, g); break; |
1791 | 0 | case KGC_INC: fullinc(L, g); break; |
1792 | 0 | case KGC_GENMAJOR: |
1793 | 0 | g->gckind = KGC_INC; |
1794 | 0 | fullinc(L, g); |
1795 | 0 | g->gckind = KGC_GENMAJOR; |
1796 | 0 | break; |
1797 | 0 | } |
1798 | 0 | g->gcemergency = 0; |
1799 | 0 | } |
1800 | | |
1801 | | /* }====================================================== */ |
1802 | | |
1803 | | |