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