diff options
Diffstat (limited to 'testes/gc.lua')
-rw-r--r-- | testes/gc.lua | 661 |
1 files changed, 661 insertions, 0 deletions
diff --git a/testes/gc.lua b/testes/gc.lua new file mode 100644 index 00000000..9647cd54 --- /dev/null +++ b/testes/gc.lua | |||
@@ -0,0 +1,661 @@ | |||
1 | -- $Id: gc.lua,v 1.82 2018/03/12 14:19:36 roberto Exp $ | ||
2 | -- See Copyright Notice in file all.lua | ||
3 | |||
4 | print('testing garbage collection') | ||
5 | |||
6 | local debug = require"debug" | ||
7 | |||
8 | assert(collectgarbage("isrunning")) | ||
9 | |||
10 | collectgarbage() | ||
11 | |||
12 | local oldmode = collectgarbage("incremental") | ||
13 | |||
14 | |||
15 | local function gcinfo () | ||
16 | return collectgarbage"count" * 1024 | ||
17 | end | ||
18 | |||
19 | |||
20 | -- test weird parameters to 'collectgarbage' | ||
21 | do | ||
22 | -- save original parameters | ||
23 | local a = collectgarbage("setpause", 200) | ||
24 | local b = collectgarbage("setstepmul", 200) | ||
25 | local t = {0, 2, 10, 90, 500, 5000, 30000, 0x7ffffffe} | ||
26 | for i = 1, #t do | ||
27 | local p = t[i] | ||
28 | for j = 1, #t do | ||
29 | local m = t[j] | ||
30 | collectgarbage("setpause", p) | ||
31 | collectgarbage("setstepmul", m) | ||
32 | collectgarbage("step", 0) | ||
33 | collectgarbage("step", 10000) | ||
34 | end | ||
35 | end | ||
36 | -- restore original parameters | ||
37 | collectgarbage("setpause", a) | ||
38 | collectgarbage("setstepmul", b) | ||
39 | collectgarbage() | ||
40 | end | ||
41 | |||
42 | |||
43 | _G["while"] = 234 | ||
44 | |||
45 | |||
46 | -- | ||
47 | -- tests for GC activation when creating different kinds of objects | ||
48 | -- | ||
49 | local function GC1 () | ||
50 | local u | ||
51 | local b -- (above 'u' it in the stack) | ||
52 | local finish = false | ||
53 | u = setmetatable({}, {__gc = function () finish = true end}) | ||
54 | b = {34} | ||
55 | repeat u = {} until finish | ||
56 | assert(b[1] == 34) -- 'u' was collected, but 'b' was not | ||
57 | |||
58 | finish = false; local i = 1 | ||
59 | u = setmetatable({}, {__gc = function () finish = true end}) | ||
60 | repeat i = i + 1; u = tostring(i) .. tostring(i) until finish | ||
61 | assert(b[1] == 34) -- 'u' was collected, but 'b' was not | ||
62 | |||
63 | finish = false | ||
64 | u = setmetatable({}, {__gc = function () finish = true end}) | ||
65 | repeat local i; u = function () return i end until finish | ||
66 | assert(b[1] == 34) -- 'u' was collected, but 'b' was not | ||
67 | end | ||
68 | |||
69 | local function GC2 () | ||
70 | local u | ||
71 | local finish = false | ||
72 | u = {setmetatable({}, {__gc = function () finish = true end})} | ||
73 | local b = {34} | ||
74 | repeat u = {{}} until finish | ||
75 | assert(b[1] == 34) -- 'u' was collected, but 'b' was not | ||
76 | |||
77 | finish = false; local i = 1 | ||
78 | u = {setmetatable({}, {__gc = function () finish = true end})} | ||
79 | repeat i = i + 1; u = {tostring(i) .. tostring(i)} until finish | ||
80 | assert(b[1] == 34) -- 'u' was collected, but 'b' was not | ||
81 | |||
82 | finish = false | ||
83 | u = {setmetatable({}, {__gc = function () finish = true end})} | ||
84 | repeat local i; u = {function () return i end} until finish | ||
85 | assert(b[1] == 34) -- 'u' was collected, but 'b' was not | ||
86 | end | ||
87 | |||
88 | local function GC() GC1(); GC2() end | ||
89 | |||
90 | |||
91 | do | ||
92 | print("creating many objects") | ||
93 | |||
94 | local contCreate = 0 | ||
95 | |||
96 | local limit = 5000 | ||
97 | |||
98 | while contCreate <= limit do | ||
99 | local a = {}; a = nil | ||
100 | contCreate = contCreate+1 | ||
101 | end | ||
102 | |||
103 | local a = "a" | ||
104 | |||
105 | contCreate = 0 | ||
106 | while contCreate <= limit do | ||
107 | a = contCreate .. "b"; | ||
108 | a = string.gsub(a, '(%d%d*)', string.upper) | ||
109 | a = "a" | ||
110 | contCreate = contCreate+1 | ||
111 | end | ||
112 | |||
113 | |||
114 | contCreate = 0 | ||
115 | |||
116 | a = {} | ||
117 | |||
118 | function a:test () | ||
119 | while contCreate <= limit do | ||
120 | load(string.format("function temp(a) return 'a%d' end", contCreate), "")() | ||
121 | assert(temp() == string.format('a%d', contCreate)) | ||
122 | contCreate = contCreate+1 | ||
123 | end | ||
124 | end | ||
125 | |||
126 | a:test() | ||
127 | |||
128 | end | ||
129 | |||
130 | |||
131 | -- collection of functions without locals, globals, etc. | ||
132 | do local f = function () end end | ||
133 | |||
134 | |||
135 | print("functions with errors") | ||
136 | prog = [[ | ||
137 | do | ||
138 | a = 10; | ||
139 | function foo(x,y) | ||
140 | a = sin(a+0.456-0.23e-12); | ||
141 | return function (z) return sin(%x+z) end | ||
142 | end | ||
143 | local x = function (w) a=a+w; end | ||
144 | end | ||
145 | ]] | ||
146 | do | ||
147 | local step = 1 | ||
148 | if _soft then step = 13 end | ||
149 | for i=1, string.len(prog), step do | ||
150 | for j=i, string.len(prog), step do | ||
151 | pcall(load(string.sub(prog, i, j), "")) | ||
152 | end | ||
153 | end | ||
154 | end | ||
155 | |||
156 | foo = nil | ||
157 | print('long strings') | ||
158 | x = "01234567890123456789012345678901234567890123456789012345678901234567890123456789" | ||
159 | assert(string.len(x)==80) | ||
160 | s = '' | ||
161 | n = 0 | ||
162 | k = math.min(300, (math.maxinteger // 80) // 2) | ||
163 | while n < k do s = s..x; n=n+1; j=tostring(n) end | ||
164 | assert(string.len(s) == k*80) | ||
165 | s = string.sub(s, 1, 10000) | ||
166 | s, i = string.gsub(s, '(%d%d%d%d)', '') | ||
167 | assert(i==10000 // 4) | ||
168 | s = nil | ||
169 | x = nil | ||
170 | |||
171 | assert(_G["while"] == 234) | ||
172 | |||
173 | |||
174 | -- | ||
175 | -- test the "size" of basic GC steps (whatever they mean...) | ||
176 | -- | ||
177 | do | ||
178 | print("steps") | ||
179 | |||
180 | print("steps (2)") | ||
181 | |||
182 | local function dosteps (siz) | ||
183 | collectgarbage() | ||
184 | local a = {} | ||
185 | for i=1,100 do a[i] = {{}}; local b = {} end | ||
186 | local x = gcinfo() | ||
187 | local i = 0 | ||
188 | repeat -- do steps until it completes a collection cycle | ||
189 | i = i+1 | ||
190 | until collectgarbage("step", siz) | ||
191 | assert(gcinfo() < x) | ||
192 | return i -- number of steps | ||
193 | end | ||
194 | |||
195 | collectgarbage"stop" | ||
196 | |||
197 | if not _port then | ||
198 | assert(dosteps(10) < dosteps(2)) | ||
199 | end | ||
200 | |||
201 | -- collector should do a full collection with so many steps | ||
202 | assert(dosteps(20000) == 1) | ||
203 | assert(collectgarbage("step", 20000) == true) | ||
204 | assert(collectgarbage("step", 20000) == true) | ||
205 | |||
206 | assert(not collectgarbage("isrunning")) | ||
207 | collectgarbage"restart" | ||
208 | assert(collectgarbage("isrunning")) | ||
209 | |||
210 | end | ||
211 | |||
212 | |||
213 | if not _port then | ||
214 | -- test the pace of the collector | ||
215 | collectgarbage(); collectgarbage() | ||
216 | local x = gcinfo() | ||
217 | collectgarbage"stop" | ||
218 | repeat | ||
219 | local a = {} | ||
220 | until gcinfo() > 3 * x | ||
221 | collectgarbage"restart" | ||
222 | assert(collectgarbage("isrunning")) | ||
223 | repeat | ||
224 | local a = {} | ||
225 | until gcinfo() <= x * 2 | ||
226 | end | ||
227 | |||
228 | |||
229 | print("clearing tables") | ||
230 | lim = 15 | ||
231 | a = {} | ||
232 | -- fill a with `collectable' indices | ||
233 | for i=1,lim do a[{}] = i end | ||
234 | b = {} | ||
235 | for k,v in pairs(a) do b[k]=v end | ||
236 | -- remove all indices and collect them | ||
237 | for n in pairs(b) do | ||
238 | a[n] = undef | ||
239 | assert(type(n) == 'table' and next(n) == nil) | ||
240 | collectgarbage() | ||
241 | end | ||
242 | b = nil | ||
243 | collectgarbage() | ||
244 | for n in pairs(a) do error'cannot be here' end | ||
245 | for i=1,lim do a[i] = i end | ||
246 | for i=1,lim do assert(a[i] == i) end | ||
247 | |||
248 | |||
249 | print('weak tables') | ||
250 | a = {}; setmetatable(a, {__mode = 'k'}); | ||
251 | -- fill a with some `collectable' indices | ||
252 | for i=1,lim do a[{}] = i end | ||
253 | -- and some non-collectable ones | ||
254 | for i=1,lim do a[i] = i end | ||
255 | for i=1,lim do local s=string.rep('@', i); a[s] = s..'#' end | ||
256 | collectgarbage() | ||
257 | local i = 0 | ||
258 | for k,v in pairs(a) do assert(k==v or k..'#'==v); i=i+1 end | ||
259 | assert(i == 2*lim) | ||
260 | |||
261 | a = {}; setmetatable(a, {__mode = 'v'}); | ||
262 | a[1] = string.rep('b', 21) | ||
263 | collectgarbage() | ||
264 | assert(a[1]) -- strings are *values* | ||
265 | a[1] = undef | ||
266 | -- fill a with some `collectable' values (in both parts of the table) | ||
267 | for i=1,lim do a[i] = {} end | ||
268 | for i=1,lim do a[i..'x'] = {} end | ||
269 | -- and some non-collectable ones | ||
270 | for i=1,lim do local t={}; a[t]=t end | ||
271 | for i=1,lim do a[i+lim]=i..'x' end | ||
272 | collectgarbage() | ||
273 | local i = 0 | ||
274 | for k,v in pairs(a) do assert(k==v or k-lim..'x' == v); i=i+1 end | ||
275 | assert(i == 2*lim) | ||
276 | |||
277 | a = {}; setmetatable(a, {__mode = 'kv'}); | ||
278 | local x, y, z = {}, {}, {} | ||
279 | -- keep only some items | ||
280 | a[1], a[2], a[3] = x, y, z | ||
281 | a[string.rep('$', 11)] = string.rep('$', 11) | ||
282 | -- fill a with some `collectable' values | ||
283 | for i=4,lim do a[i] = {} end | ||
284 | for i=1,lim do a[{}] = i end | ||
285 | for i=1,lim do local t={}; a[t]=t end | ||
286 | collectgarbage() | ||
287 | assert(next(a) ~= nil) | ||
288 | local i = 0 | ||
289 | for k,v in pairs(a) do | ||
290 | assert((k == 1 and v == x) or | ||
291 | (k == 2 and v == y) or | ||
292 | (k == 3 and v == z) or k==v); | ||
293 | i = i+1 | ||
294 | end | ||
295 | assert(i == 4) | ||
296 | x,y,z=nil | ||
297 | collectgarbage() | ||
298 | assert(next(a) == string.rep('$', 11)) | ||
299 | |||
300 | |||
301 | -- 'bug' in 5.1 | ||
302 | a = {} | ||
303 | local t = {x = 10} | ||
304 | local C = setmetatable({key = t}, {__mode = 'v'}) | ||
305 | local C1 = setmetatable({[t] = 1}, {__mode = 'k'}) | ||
306 | a.x = t -- this should not prevent 't' from being removed from | ||
307 | -- weak table 'C' by the time 'a' is finalized | ||
308 | |||
309 | setmetatable(a, {__gc = function (u) | ||
310 | assert(C.key == nil) | ||
311 | assert(type(next(C1)) == 'table') | ||
312 | end}) | ||
313 | |||
314 | a, t = nil | ||
315 | collectgarbage() | ||
316 | collectgarbage() | ||
317 | assert(next(C) == nil and next(C1) == nil) | ||
318 | C, C1 = nil | ||
319 | |||
320 | |||
321 | -- ephemerons | ||
322 | local mt = {__mode = 'k'} | ||
323 | a = {{10},{20},{30},{40}}; setmetatable(a, mt) | ||
324 | x = nil | ||
325 | for i = 1, 100 do local n = {}; a[n] = {k = {x}}; x = n end | ||
326 | GC() | ||
327 | local n = x | ||
328 | local i = 0 | ||
329 | while n do n = a[n].k[1]; i = i + 1 end | ||
330 | assert(i == 100) | ||
331 | x = nil | ||
332 | GC() | ||
333 | for i = 1, 4 do assert(a[i][1] == i * 10); a[i] = undef end | ||
334 | assert(next(a) == nil) | ||
335 | |||
336 | local K = {} | ||
337 | a[K] = {} | ||
338 | for i=1,10 do a[K][i] = {}; a[a[K][i]] = setmetatable({}, mt) end | ||
339 | x = nil | ||
340 | local k = 1 | ||
341 | for j = 1,100 do | ||
342 | local n = {}; local nk = k%10 + 1 | ||
343 | a[a[K][nk]][n] = {x, k = k}; x = n; k = nk | ||
344 | end | ||
345 | GC() | ||
346 | local n = x | ||
347 | local i = 0 | ||
348 | while n do local t = a[a[K][k]][n]; n = t[1]; k = t.k; i = i + 1 end | ||
349 | assert(i == 100) | ||
350 | K = nil | ||
351 | GC() | ||
352 | -- assert(next(a) == nil) | ||
353 | |||
354 | |||
355 | -- testing errors during GC | ||
356 | do | ||
357 | collectgarbage("stop") -- stop collection | ||
358 | local u = {} | ||
359 | local s = {}; setmetatable(s, {__mode = 'k'}) | ||
360 | setmetatable(u, {__gc = function (o) | ||
361 | local i = s[o] | ||
362 | s[i] = true | ||
363 | assert(not s[i - 1]) -- check proper finalization order | ||
364 | if i == 8 then error("here") end -- error during GC | ||
365 | end}) | ||
366 | |||
367 | for i = 6, 10 do | ||
368 | local n = setmetatable({}, getmetatable(u)) | ||
369 | s[n] = i | ||
370 | end | ||
371 | |||
372 | assert(not pcall(collectgarbage)) | ||
373 | for i = 8, 10 do assert(s[i]) end | ||
374 | |||
375 | for i = 1, 5 do | ||
376 | local n = setmetatable({}, getmetatable(u)) | ||
377 | s[n] = i | ||
378 | end | ||
379 | |||
380 | collectgarbage() | ||
381 | for i = 1, 10 do assert(s[i]) end | ||
382 | |||
383 | getmetatable(u).__gc = false | ||
384 | |||
385 | |||
386 | -- __gc errors with non-string messages | ||
387 | setmetatable({}, {__gc = function () error{} end}) | ||
388 | local a, b = pcall(collectgarbage) | ||
389 | assert(not a and type(b) == "string" and string.find(b, "error in __gc")) | ||
390 | |||
391 | end | ||
392 | print '+' | ||
393 | |||
394 | |||
395 | -- testing userdata | ||
396 | if T==nil then | ||
397 | (Message or print)('\n >>> testC not active: skipping userdata GC tests <<<\n') | ||
398 | |||
399 | else | ||
400 | |||
401 | local function newproxy(u) | ||
402 | return debug.setmetatable(T.newuserdata(0), debug.getmetatable(u)) | ||
403 | end | ||
404 | |||
405 | collectgarbage("stop") -- stop collection | ||
406 | local u = newproxy(nil) | ||
407 | debug.setmetatable(u, {__gc = true}) | ||
408 | local s = 0 | ||
409 | local a = {[u] = 0}; setmetatable(a, {__mode = 'vk'}) | ||
410 | for i=1,10 do a[newproxy(u)] = i end | ||
411 | for k in pairs(a) do assert(getmetatable(k) == getmetatable(u)) end | ||
412 | local a1 = {}; for k,v in pairs(a) do a1[k] = v end | ||
413 | for k,v in pairs(a1) do a[v] = k end | ||
414 | for i =1,10 do assert(a[i]) end | ||
415 | getmetatable(u).a = a1 | ||
416 | getmetatable(u).u = u | ||
417 | do | ||
418 | local u = u | ||
419 | getmetatable(u).__gc = function (o) | ||
420 | assert(a[o] == 10-s) | ||
421 | assert(a[10-s] == undef) -- udata already removed from weak table | ||
422 | assert(getmetatable(o) == getmetatable(u)) | ||
423 | assert(getmetatable(o).a[o] == 10-s) | ||
424 | s=s+1 | ||
425 | end | ||
426 | end | ||
427 | a1, u = nil | ||
428 | assert(next(a) ~= nil) | ||
429 | collectgarbage() | ||
430 | assert(s==11) | ||
431 | collectgarbage() | ||
432 | assert(next(a) == nil) -- finalized keys are removed in two cycles | ||
433 | end | ||
434 | |||
435 | |||
436 | -- __gc x weak tables | ||
437 | local u = setmetatable({}, {__gc = true}) | ||
438 | -- __gc metamethod should be collected before running | ||
439 | setmetatable(getmetatable(u), {__mode = "v"}) | ||
440 | getmetatable(u).__gc = function (o) os.exit(1) end -- cannot happen | ||
441 | u = nil | ||
442 | collectgarbage() | ||
443 | |||
444 | local u = setmetatable({}, {__gc = true}) | ||
445 | local m = getmetatable(u) | ||
446 | m.x = {[{0}] = 1; [0] = {1}}; setmetatable(m.x, {__mode = "kv"}); | ||
447 | m.__gc = function (o) | ||
448 | assert(next(getmetatable(o).x) == nil) | ||
449 | m = 10 | ||
450 | end | ||
451 | u, m = nil | ||
452 | collectgarbage() | ||
453 | assert(m==10) | ||
454 | |||
455 | do -- tests for string keys in weak tables | ||
456 | collectgarbage(); collectgarbage() | ||
457 | local m = collectgarbage("count") -- current memory | ||
458 | local a = setmetatable({}, {__mode = "kv"}) | ||
459 | a[string.rep("a", 2^22)] = 25 -- long string key -> number value | ||
460 | a[string.rep("b", 2^22)] = {} -- long string key -> colectable value | ||
461 | a[{}] = 14 -- colectable key | ||
462 | assert(collectgarbage("count") > m + 2^13) -- 2^13 == 2 * 2^22 in KB | ||
463 | collectgarbage() | ||
464 | assert(collectgarbage("count") >= m + 2^12 and | ||
465 | collectgarbage("count") < m + 2^13) -- one key was collected | ||
466 | local k, v = next(a) -- string key with number value preserved | ||
467 | assert(k == string.rep("a", 2^22) and v == 25) | ||
468 | assert(next(a, k) == nil) -- everything else cleared | ||
469 | assert(a[string.rep("b", 2^22)] == undef) | ||
470 | a[k] = undef -- erase this last entry | ||
471 | k = nil | ||
472 | collectgarbage() | ||
473 | assert(next(a) == nil) | ||
474 | -- make sure will not try to compare with dead key | ||
475 | assert(a[string.rep("b", 100)] == undef) | ||
476 | assert(collectgarbage("count") <= m + 1) -- eveything collected | ||
477 | end | ||
478 | |||
479 | |||
480 | -- errors during collection | ||
481 | u = setmetatable({}, {__gc = function () error "!!!" end}) | ||
482 | u = nil | ||
483 | assert(not pcall(collectgarbage)) | ||
484 | |||
485 | |||
486 | if not _soft then | ||
487 | print("long list") | ||
488 | local a = {} | ||
489 | for i = 1,200000 do | ||
490 | a = {next = a} | ||
491 | end | ||
492 | a = nil | ||
493 | collectgarbage() | ||
494 | end | ||
495 | |||
496 | -- create many threads with self-references and open upvalues | ||
497 | print("self-referenced threads") | ||
498 | local thread_id = 0 | ||
499 | local threads = {} | ||
500 | |||
501 | local function fn (thread) | ||
502 | local x = {} | ||
503 | threads[thread_id] = function() | ||
504 | thread = x | ||
505 | end | ||
506 | coroutine.yield() | ||
507 | end | ||
508 | |||
509 | while thread_id < 1000 do | ||
510 | local thread = coroutine.create(fn) | ||
511 | coroutine.resume(thread, thread) | ||
512 | thread_id = thread_id + 1 | ||
513 | end | ||
514 | |||
515 | |||
516 | -- Create a closure (function inside 'f') with an upvalue ('param') that | ||
517 | -- points (through a table) to the closure itself and to the thread | ||
518 | -- ('co' and the initial value of 'param') where closure is running. | ||
519 | -- Then, assert that table (and therefore everything else) will be | ||
520 | -- collected. | ||
521 | do | ||
522 | local collected = false -- to detect collection | ||
523 | collectgarbage(); collectgarbage("stop") | ||
524 | do | ||
525 | local function f (param) | ||
526 | ;(function () | ||
527 | assert(type(f) == 'function' and type(param) == 'thread') | ||
528 | param = {param, f} | ||
529 | setmetatable(param, {__gc = function () collected = true end}) | ||
530 | coroutine.yield(100) | ||
531 | end)() | ||
532 | end | ||
533 | local co = coroutine.create(f) | ||
534 | assert(coroutine.resume(co, co)) | ||
535 | end | ||
536 | -- Now, thread and closure are not reacheable any more. | ||
537 | collectgarbage() | ||
538 | assert(collected) | ||
539 | collectgarbage("restart") | ||
540 | end | ||
541 | |||
542 | |||
543 | do | ||
544 | collectgarbage() | ||
545 | collectgarbage"stop" | ||
546 | collectgarbage("step", 0) -- steps should not unblock the collector | ||
547 | local x = gcinfo() | ||
548 | repeat | ||
549 | for i=1,1000 do _ENV.a = {} end -- no collection during the loop | ||
550 | until gcinfo() > 2 * x | ||
551 | collectgarbage"restart" | ||
552 | end | ||
553 | |||
554 | |||
555 | if T then -- tests for weird cases collecting upvalues | ||
556 | |||
557 | local function foo () | ||
558 | local a = {x = 20} | ||
559 | coroutine.yield(function () return a.x end) -- will run collector | ||
560 | assert(a.x == 20) -- 'a' is 'ok' | ||
561 | a = {x = 30} -- create a new object | ||
562 | assert(T.gccolor(a) == "white") -- of course it is new... | ||
563 | coroutine.yield(100) -- 'a' is still local to this thread | ||
564 | end | ||
565 | |||
566 | local t = setmetatable({}, {__mode = "kv"}) | ||
567 | collectgarbage(); collectgarbage('stop') | ||
568 | -- create coroutine in a weak table, so it will never be marked | ||
569 | t.co = coroutine.wrap(foo) | ||
570 | local f = t.co() -- create function to access local 'a' | ||
571 | T.gcstate("atomic") -- ensure all objects are traversed | ||
572 | assert(T.gcstate() == "atomic") | ||
573 | assert(t.co() == 100) -- resume coroutine, creating new table for 'a' | ||
574 | assert(T.gccolor(t.co) == "white") -- thread was not traversed | ||
575 | T.gcstate("pause") -- collect thread, but should mark 'a' before that | ||
576 | assert(t.co == nil and f() == 30) -- ensure correct access to 'a' | ||
577 | |||
578 | collectgarbage("restart") | ||
579 | |||
580 | -- test barrier in sweep phase (backing userdata to gray) | ||
581 | local u = T.newuserdata(0, 1) -- create a userdata | ||
582 | collectgarbage() | ||
583 | collectgarbage"stop" | ||
584 | local a = {} -- avoid 'u' as first element in 'allgc' | ||
585 | T.gcstate"atomic" | ||
586 | T.gcstate"sweepallgc" | ||
587 | local x = {} | ||
588 | assert(T.gccolor(u) == "black") -- userdata is "old" (black) | ||
589 | assert(T.gccolor(x) == "white") -- table is "new" (white) | ||
590 | debug.setuservalue(u, x) -- trigger barrier | ||
591 | assert(T.gccolor(u) == "gray") -- userdata changed back to gray | ||
592 | collectgarbage"restart" | ||
593 | |||
594 | print"+" | ||
595 | end | ||
596 | |||
597 | |||
598 | if T then | ||
599 | local debug = require "debug" | ||
600 | collectgarbage("stop") | ||
601 | local x = T.newuserdata(0) | ||
602 | local y = T.newuserdata(0) | ||
603 | debug.setmetatable(y, {__gc = true}) -- bless the new udata before... | ||
604 | debug.setmetatable(x, {__gc = true}) -- ...the old one | ||
605 | assert(T.gccolor(y) == "white") | ||
606 | T.checkmemory() | ||
607 | collectgarbage("restart") | ||
608 | end | ||
609 | |||
610 | |||
611 | if T then | ||
612 | print("emergency collections") | ||
613 | collectgarbage() | ||
614 | collectgarbage() | ||
615 | T.totalmem(T.totalmem() + 200) | ||
616 | for i=1,200 do local a = {} end | ||
617 | T.totalmem(0) | ||
618 | collectgarbage() | ||
619 | local t = T.totalmem("table") | ||
620 | local a = {{}, {}, {}} -- create 4 new tables | ||
621 | assert(T.totalmem("table") == t + 4) | ||
622 | t = T.totalmem("function") | ||
623 | a = function () end -- create 1 new closure | ||
624 | assert(T.totalmem("function") == t + 1) | ||
625 | t = T.totalmem("thread") | ||
626 | a = coroutine.create(function () end) -- create 1 new coroutine | ||
627 | assert(T.totalmem("thread") == t + 1) | ||
628 | end | ||
629 | |||
630 | -- create an object to be collected when state is closed | ||
631 | do | ||
632 | local setmetatable,assert,type,print,getmetatable = | ||
633 | setmetatable,assert,type,print,getmetatable | ||
634 | local tt = {} | ||
635 | tt.__gc = function (o) | ||
636 | assert(getmetatable(o) == tt) | ||
637 | -- create new objects during GC | ||
638 | local a = 'xuxu'..(10+3)..'joao', {} | ||
639 | ___Glob = o -- ressurect object! | ||
640 | setmetatable({}, tt) -- creates a new one with same metatable | ||
641 | print(">>> closing state " .. "<<<\n") | ||
642 | end | ||
643 | local u = setmetatable({}, tt) | ||
644 | ___Glob = {u} -- avoid object being collected before program end | ||
645 | end | ||
646 | |||
647 | -- create several objects to raise errors when collected while closing state | ||
648 | do | ||
649 | local mt = {__gc = function (o) return o + 1 end} | ||
650 | for i = 1,10 do | ||
651 | -- create object and preserve it until the end | ||
652 | table.insert(___Glob, setmetatable({}, mt)) | ||
653 | end | ||
654 | end | ||
655 | |||
656 | -- just to make sure | ||
657 | assert(collectgarbage'isrunning') | ||
658 | |||
659 | collectgarbage(oldmode) | ||
660 | |||
661 | print('OK') | ||