--- source: src/main.rs expression: compiled input_file: test-data/lua5.4-tests/gc.lua --- print('testing incremental garbage collection'); local debug = require("debug") assert(collectgarbage("isrunning")); collectgarbage(); local oldmode = collectgarbage("incremental") assert(collectgarbage("generational") == "incremental"); assert(collectgarbage("generational") == "generational"); assert(collectgarbage("incremental") == "generational"); assert(collectgarbage("incremental") == "incremental"); local fn nop() { } local fn gcinfo() { return collectgarbage("count") * 1024 } { local a = collectgarbage("setpause", 200) local b = collectgarbage("setstepmul", 200) local t = { 0, 2, 10, 90, 500, 5000, 30000, 0x7ffffffe } for i = 1, #t { local p = t[(i)] for j = 1, #t { local m = t[(j)] collectgarbage("setpause", p); collectgarbage("setstepmul", m); collectgarbage("step", 0); collectgarbage("step", 10000); } } collectgarbage("setpause", a); collectgarbage("setstepmul", b); collectgarbage(); } _G[("while")] = 234 local fn GC1() { local u local b local finish = false u = setmetatable({}, { __gc = fn () { finish = true } }) b = { 34 } loop { u = {} } until finish assert(b[(1)] == 34); finish = false local i = 1 u = setmetatable({}, { __gc = fn () { finish = true } }) loop { i = i + 1 u = tostring(i) .. tostring(i) } until finish assert(b[(1)] == 34); finish = false u = setmetatable({}, { __gc = fn () { finish = true } }) loop { local i u = fn () { return i } } until finish assert(b[(1)] == 34); } local fn GC2() { local u local finish = false u = { setmetatable({}, { __gc = fn () { finish = true } }) } local b = { 34 } loop { u = { {} } } until finish assert(b[(1)] == 34); finish = false local i = 1 u = { setmetatable({}, { __gc = fn () { finish = true } }) } loop { i = i + 1 u = { tostring(i) .. tostring(i) } } until finish assert(b[(1)] == 34); finish = false u = { setmetatable({}, { __gc = fn () { finish = true } }) } loop { local i u = { fn () { return i } } } until finish assert(b[(1)] == 34); } local fn GC() { GC1(); GC2(); } { print("creating many objects"); local limit = 5000 for i = 1, limit { local a = {} a = nil } local a = "a" for i = 1, limit { a = i .. "b" a = string.gsub(a, '(%d%d*)', "%1 %1") a = "a" } a = {} method a::test() { for i = 1, limit { load(string.format("function temp(a) return 'a%d' end", i), "")(); assert(temp() == string.format('a%d', i)); } } a::test(); } { local f = fn () { } } print("functions with errors"); prog = ` do a = 10; function foo(x,y) a = sin(a+0.456-0.23e-12); return function (z) return sin(%x+z) end end local x = function (w) a=a+w; end end ` { local step = 1 if _soft { step = 13 } for i = 1, string.len(prog), step { for j = i, string.len(prog), step { pcall(load(string.sub(prog, i, j), "")); } } } foo = nil print('long strings'); x = "01234567890123456789012345678901234567890123456789012345678901234567890123456789" assert(string.len(x) == 80); s = '' k = math.min(300, (math.maxinteger /_ 80) /_ 2) for n = 1, k { s = s .. x j = tostring(n) } assert(string.len(s) == k * 80); s = string.sub(s, 1, 10000) s, i = string.gsub(s, '(%d%d%d%d)', '') assert(i == 10000 /_ 4); s = nil x = nil assert(_G[("while")] == 234); { print("steps"); print("steps (2)"); local fn dosteps(siz) { collectgarbage(); local a = {} for i = 1, 100 { a[(i)] = { {} } local b = {} } local x = gcinfo() local i = 0 loop { i = i + 1 } until collectgarbage("step", siz) assert(gcinfo() < x); return i } collectgarbage("stop"); if !_port { assert(dosteps(10) < dosteps(2)); } assert(dosteps(20000) == 1); assert(collectgarbage("step", 20000) == true); assert(collectgarbage("step", 20000) == true); assert(!collectgarbage("isrunning")); collectgarbage("restart"); assert(collectgarbage("isrunning")); } if !_port { collectgarbage(); collectgarbage(); local x = gcinfo() collectgarbage("stop"); loop { local a = {} } until gcinfo() > 3 * x collectgarbage("restart"); assert(collectgarbage("isrunning")); loop { local a = {} } until gcinfo() <= x * 2 } print("clearing tables"); lim = 15 a = {} for i = 1, lim { a[({})] = i } b = {} for k, v with pairs(a) { b[(k)] = v } for n with pairs(b) { a[(n)] = undef assert(type(n) == 'table' && next(n) == nil); collectgarbage(); } b = nil collectgarbage(); for n with pairs(a) { error('cannot be here'); } for i = 1, lim { a[(i)] = i } for i = 1, lim { assert(a[(i)] == i); } print('weak tables'); a = {} setmetatable(a, { __mode = 'k' }); for i = 1, lim { a[({})] = i } for i = 1, lim { a[(i)] = i } for i = 1, lim { local s = string.rep('@', i) a[(s)] = s .. '#' } collectgarbage(); local i = 0 for k, v with pairs(a) { assert(k == v || k .. '#' == v); i = i + 1 } assert(i == 2 * lim); a = {} setmetatable(a, { __mode = 'v' }); a[(1)] = string.rep('b', 21) collectgarbage(); assert(a[(1)]); a[(1)] = undef for i = 1, lim { a[(i)] = {} } for i = 1, lim { a[(i .. 'x')] = {} } for i = 1, lim { local t = {} a[(t)] = t } for i = 1, lim { a[(i + lim)] = i .. 'x' } collectgarbage(); local i = 0 for k, v with pairs(a) { assert(k == v || k - lim .. 'x' == v); i = i + 1 } assert(i == 2 * lim); a = {} setmetatable(a, { __mode = 'kv' }); local x, y, z = {}, {}, {} a[(1)], a[(2)], a[(3)] = x, y, z a[(string.rep('$', 11))] = string.rep('$', 11) for i = 4, lim { a[(i)] = {} } for i = 1, lim { a[({})] = i } for i = 1, lim { local t = {} a[(t)] = t } collectgarbage(); assert(next(a) != nil); local i = 0 for k, v with pairs(a) { assert((k == 1 && v == x) || (k == 2 && v == y) || (k == 3 && v == z) || k == v); i = i + 1 } assert(i == 4); x, y, z = nil collectgarbage(); assert(next(a) == string.rep('$', 11)); a = {} local t = { x = 10 } local C = setmetatable({ key = t }, { __mode = 'v' }) local C1 = setmetatable({ t = 1 }, { __mode = 'k' }) a.x = t setmetatable(a, { __gc = fn (u) { assert(C.key == nil); assert(type(next(C1)) == 'table'); } }); a, t = nil collectgarbage(); collectgarbage(); assert(next(C) == nil && next(C1) == nil); C, C1 = nil local mt = { __mode = 'k' } a = { { 10 }, { 20 }, { 30 }, { 40 } } setmetatable(a, mt); x = nil for i = 1, 100 { local n = {} a[(n)] = { k = { x } } x = n } GC(); local n = x local i = 0 while n { n = a[(n)].k[(1)] i = i + 1 } assert(i == 100); x = nil GC(); for i = 1, 4 { assert(a[(i)][(1)] == i * 10); a[(i)] = undef } assert(next(a) == nil); local K = {} a[(K)] = {} for i = 1, 10 { a[(K)][(i)] = {} a[(a[(K)][(i)])] = setmetatable({}, mt) } x = nil local k = 1 for j = 1, 100 { local n = {} local nk = k % 10 + 1 a[(a[(K)][(nk)])][(n)] = { x, k = k } x = n k = nk } GC(); local n = x local i = 0 while n { local t = a[(a[(K)][(k)])][(n)] n = t[(1)] k = t.k i = i + 1 } assert(i == 100); K = nil GC(); if T { collectgarbage("stop"); local u = {} local s = {} setmetatable(s, { __mode = 'k' }); setmetatable(u, { __gc = fn (o) { local i = s[(o)] s[(i)] = true assert(!s[(i - 1)]); if i == 8 { error("@expected@"); } } }); for i = 6, 10 { local n = setmetatable({}, getmetatable(u)) s[(n)] = i } warn("@on"); warn("@store"); collectgarbage(); assert(string.find(_WARN, "error in __gc")); assert(string.match(_WARN, "@(.-)@") == "expected"); _WARN = false for i = 8, 10 { assert(s[(i)]); } for i = 1, 5 { local n = setmetatable({}, getmetatable(u)) s[(n)] = i } collectgarbage(); for i = 1, 10 { assert(s[(i)]); } getmetatable(u).__gc = nil warn("@normal"); } print('+'); if T == nil { (Message || print)('\n >>> testC not active: skipping userdata GC tests <<<\n'); } else { local fn newproxy(u) { return debug.setmetatable(T.newuserdata(0), debug.getmetatable(u)) } collectgarbage("stop"); local u = newproxy(nil) debug.setmetatable(u, { __gc = true }); local s = 0 local a = { u = 0 } setmetatable(a, { __mode = 'vk' }); for i = 1, 10 { a[(newproxy(u))] = i } for k with pairs(a) { assert(getmetatable(k) == getmetatable(u)); } local a1 = {} for k, v with pairs(a) { a1[(k)] = v } for k, v with pairs(a1) { a[(v)] = k } for i = 1, 10 { assert(a[(i)]); } getmetatable(u).a = a1 getmetatable(u).u = u { local u = u getmetatable(u).__gc = fn (o) { assert(a[(o)] == 10 - s); assert(a[(10 - s)] == undef); assert(getmetatable(o) == getmetatable(u)); assert(getmetatable(o).a[(o)] == 10 - s); s = s + 1 } } a1, u = nil assert(next(a) != nil); collectgarbage(); assert(s == 11); collectgarbage(); assert(next(a) == nil); } local u = setmetatable({}, { __gc = true }) setmetatable(getmetatable(u), { __mode = "v" }); getmetatable(u).__gc = fn (o) { os.exit(1); } u = nil collectgarbage(); local u = setmetatable({}, { __gc = true }) local m = getmetatable(u) m.x = { { 0 } = 1, 0 = { 1 } } setmetatable(m.x, { __mode = "kv" }); m.__gc = fn (o) { assert(next(getmetatable(o).x) == nil); m = 10 } u, m = nil collectgarbage(); assert(m == 10); { collectgarbage(); collectgarbage(); local m = collectgarbage("count") local a = setmetatable({}, { __mode = "kv" }) a[(string.rep("a", 2 ^ 22))] = 25 a[(string.rep("b", 2 ^ 22))] = {} a[({})] = 14 assert(collectgarbage("count") > m + 2 ^ 13); collectgarbage(); assert(collectgarbage("count") >= m + 2 ^ 12 && collectgarbage("count") < m + 2 ^ 13); local k, v = next(a) assert(k == string.rep("a", 2 ^ 22) && v == 25); assert(next(a, k) == nil); assert(a[(string.rep("b", 2 ^ 22))] == undef); a[(k)] = undef k = nil collectgarbage(); assert(next(a) == nil); assert(a[(string.rep("b", 100))] == undef); assert(collectgarbage("count") <= m + 1); } if T { warn("@store"); u = setmetatable({}, { __gc = fn () { error("@expected error"); } }) u = nil collectgarbage(); assert(string.find(_WARN, "@expected error")); _WARN = false warn("@normal"); } if !_soft { print("long list"); local a = {} for i = 1, 200000 { a = { next = a } } a = nil collectgarbage(); } print("self-referenced threads"); local thread_id = 0 local threads = {} local fn fn(thread) { local x = {} threads[(thread_id)] = fn () { thread = x } coroutine.yield(); } while thread_id < 1000 { local thread = coroutine.create(fn) coroutine.resume(thread, thread); thread_id = thread_id + 1 } { local collected = false collectgarbage(); collectgarbage("stop"); { local fn f(param) { (fn () { assert(type(f) == 'function' && type(param) == 'thread'); param = { param, f } setmetatable(param, { __gc = fn () { collected = true } }); coroutine.yield(100); })(); } local co = coroutine.create(f) assert(coroutine.resume(co, co)); } collectgarbage(); assert(collected); collectgarbage("restart"); } { collectgarbage(); collectgarbage("stop"); collectgarbage("step", 0); local x = gcinfo() loop { for i = 1, 1000 { _ENV.a = {} } } until gcinfo() > 2 * x collectgarbage("restart"); } if T { local fn foo() { local a = { x = 20 } coroutine.yield(fn () { return a.x }); assert(a.x == 20); a = { x = 30 } assert(T.gccolor(a) == "white"); coroutine.yield(100); } local t = setmetatable({}, { __mode = "kv" }) collectgarbage(); collectgarbage('stop'); t.co = coroutine.wrap(foo) local f = t.co() T.gcstate("atomic"); assert(T.gcstate() == "atomic"); assert(t.co() == 100); assert(T.gccolor(t.co) == "white"); T.gcstate("pause"); assert(t.co == nil && f() == 30); collectgarbage("restart"); local u = T.newuserdata(0, 1) collectgarbage(); collectgarbage("stop"); local a = {} T.gcstate("atomic"); T.gcstate("sweepallgc"); local x = {} assert(T.gccolor(u) == "black"); assert(T.gccolor(x) == "white"); debug.setuservalue(u, x); assert(T.gccolor(u) == "gray"); collectgarbage("restart"); print("+"); } if T { local debug = require("debug") collectgarbage("stop"); local x = T.newuserdata(0) local y = T.newuserdata(0) debug.setmetatable(y, { __gc = nop }); debug.setmetatable(x, { __gc = nop }); assert(T.gccolor(y) == "white"); T.checkmemory(); collectgarbage("restart"); } if T { print("emergency collections"); collectgarbage(); collectgarbage(); T.totalmem(T.totalmem() + 200); for i = 1, 200 { local a = {} } T.totalmem(0); collectgarbage(); local t = T.totalmem("table") local a = { {}, {}, {} } assert(T.totalmem("table") == t + 4); t = T.totalmem("function") a = fn () { } assert(T.totalmem("function") == t + 1); t = T.totalmem("thread") a = coroutine.create(fn () { }) assert(T.totalmem("thread") == t + 1); } { local setmetatable, assert, type, print, getmetatable = setmetatable, assert, type, print, getmetatable local tt = {} tt.__gc = fn (o) { assert(getmetatable(o) == tt); local a = 'xuxu' .. (10 + 3) .. 'joao', {} ___Glob = o setmetatable({}, tt); print(">>> closing state " .. "<<<\n"); } local u = setmetatable({}, tt) ___Glob = { u } } if T { local error, assert, find, warn = error, assert, string.find, warn local n = 0 local lastmsg local mt = { __gc = fn (o) { n = n + 1 assert(n == o[(1)]); if n == 1 { _WARN = false } elseif if n == 2 { assert(find(_WARN, "@expected warning")); lastmsg = _WARN } else { assert(lastmsg == _WARN); } warn("@store"); _WARN = false error("@expected warning"); } } for i = 10, 1, -1 { table.insert(___Glob, setmetatable({ i }, mt)); } } assert(collectgarbage('isrunning')); { local res = true setmetatable({}, { __gc = fn () { res = collectgarbage() } }); collectgarbage(); assert(!res); } collectgarbage(oldmode); print('OK');