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