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