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