123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710 |
- -- $Id: testes/gc.lua $
- -- See Copyright Notice in file lua.h
- print('testing incremental garbage collection')
- local debug = require"debug"
- assert(collectgarbage("isrunning"))
- collectgarbage()
- local oldmode = collectgarbage("incremental")
- -- changing modes should return previous mode
- assert(collectgarbage("generational") == "incremental")
- assert(collectgarbage("generational") == "generational")
- assert(collectgarbage("incremental") == "generational")
- assert(collectgarbage("incremental") == "incremental")
- local function nop () end
- local function gcinfo ()
- return collectgarbage"count" * 1024
- end
- -- test weird parameters to 'collectgarbage'
- do
- collectgarbage("incremental")
- local opause = collectgarbage("param", "pause", 100)
- local ostepmul = collectgarbage("param", "stepmul", 100)
- assert(collectgarbage("param", "pause") == 100)
- assert(collectgarbage("param", "stepmul") == 100)
- local t = {0, 2, 10, 90, 500, 5000, 30000, 0x7ffffffe}
- for i = 1, #t do
- collectgarbage("param", "pause", t[i])
- for j = 1, #t do
- collectgarbage("param", "stepmul", t[j])
- collectgarbage("step", t[j])
- end
- end
- -- restore original parameters
- collectgarbage("param", "pause", opause)
- collectgarbage("param", "stepmul", ostepmul)
- collectgarbage()
- end
- --
- -- test the "size" of basic GC steps (whatever they mean...)
- --
- do print("steps")
- local function dosteps (siz)
- collectgarbage()
- local a = {}
- for i=1,100 do a[i] = {{}}; local b = {} end
- local x = gcinfo()
- local i = 0
- repeat -- do steps until it completes a collection cycle
- i = i+1
- until collectgarbage("step", siz)
- assert(gcinfo() < x)
- return i -- number of steps
- end
- if not _port then
- collectgarbage"stop"
- assert(dosteps(10) < dosteps(2))
- collectgarbage"restart"
- end
- end
- _G["while"] = 234
- --
- -- tests for GC activation when creating different kinds of objects
- --
- local function GC1 ()
- local u
- local b -- (above 'u' it in the stack)
- local finish = false
- u = setmetatable({}, {__gc = function () finish = true end})
- b = {34}
- repeat u = {} until finish
- assert(b[1] == 34) -- 'u' was collected, but 'b' was not
- finish = false; local i = 1
- u = setmetatable({}, {__gc = function () finish = true end})
- repeat i = i + 1; u = tostring(i) .. tostring(i) until finish
- assert(b[1] == 34) -- 'u' was collected, but 'b' was not
- finish = false
- u = setmetatable({}, {__gc = function () finish = true end})
- repeat local i; u = function () return i end until finish
- assert(b[1] == 34) -- 'u' was collected, but 'b' was not
- end
- local function GC2 ()
- local u
- local finish = false
- u = {setmetatable({}, {__gc = function () finish = true end})}
- local b = {34}
- repeat u = {{}} until finish
- assert(b[1] == 34) -- 'u' was collected, but 'b' was not
- finish = false; local i = 1
- u = {setmetatable({}, {__gc = function () finish = true end})}
- repeat i = i + 1; u = {tostring(i) .. tostring(i)} until finish
- assert(b[1] == 34) -- 'u' was collected, but 'b' was not
- finish = false
- u = {setmetatable({}, {__gc = function () finish = true end})}
- repeat local i; u = {function () return i end} until finish
- assert(b[1] == 34) -- 'u' was collected, but 'b' was not
- end
- local function GC() GC1(); GC2() end
- do
- print("creating many objects")
- local limit = 5000
- for i = 1, limit do
- local a = {}; a = nil
- end
- local a = "a"
- for i = 1, limit do
- a = i .. "b";
- a = string.gsub(a, '(%d%d*)', "%1 %1")
- a = "a"
- end
- a = {}
- function a:test ()
- for i = 1, limit do
- load(string.format("function temp(a) return 'a%d' end", i), "")()
- assert(temp() == string.format('a%d', i))
- end
- end
- a:test()
- _G.temp = nil
- end
- -- collection of functions without locals, globals, etc.
- do local f = function () end end
- print("functions with errors")
- local 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
- ]]
- do
- local step = 1
- if _soft then step = 13 end
- for i=1, string.len(prog), step do
- for j=i, string.len(prog), step do
- pcall(load(string.sub(prog, i, j), ""))
- end
- end
- end
- rawset(_G, "a", nil)
- _G.x = nil
- do
- foo = nil
- print('long strings')
- local x = "01234567890123456789012345678901234567890123456789012345678901234567890123456789"
- assert(string.len(x)==80)
- local s = ''
- local k = math.min(300, (math.maxinteger // 80) // 2)
- for n = 1, k do s = s..x; local j=tostring(n) end
- assert(string.len(s) == k*80)
- s = string.sub(s, 1, 10000)
- local s, i = string.gsub(s, '(%d%d%d%d)', '')
- assert(i==10000 // 4)
- assert(_G["while"] == 234)
- _G["while"] = nil
- end
- if not _port then
- -- test the pace of the collector
- collectgarbage(); collectgarbage()
- local x = gcinfo()
- collectgarbage"stop"
- repeat
- local a = {}
- until gcinfo() > 3 * x
- collectgarbage"restart"
- assert(collectgarbage("isrunning"))
- repeat
- local a = {}
- until gcinfo() <= x * 2
- end
- print("clearing tables")
- local lim = 15
- local a = {}
- -- fill a with `collectable' indices
- for i=1,lim do a[{}] = i end
- b = {}
- for k,v in pairs(a) do b[k]=v end
- -- remove all indices and collect them
- for n in pairs(b) do
- a[n] = undef
- assert(type(n) == 'table' and next(n) == nil)
- collectgarbage()
- end
- b = nil
- collectgarbage()
- for n in pairs(a) do error'cannot be here' end
- for i=1,lim do a[i] = i end
- for i=1,lim do assert(a[i] == i) end
- print('weak tables')
- a = {}; setmetatable(a, {__mode = 'k'});
- -- fill a with some `collectable' indices
- for i=1,lim do a[{}] = i end
- -- and some non-collectable ones
- for i=1,lim do a[i] = i end
- for i=1,lim do local s=string.rep('@', i); a[s] = s..'#' end
- collectgarbage()
- local i = 0
- for k,v in pairs(a) do assert(k==v or k..'#'==v); i=i+1 end
- assert(i == 2*lim)
- a = {}; setmetatable(a, {__mode = 'v'});
- a[1] = string.rep('b', 21)
- collectgarbage()
- assert(a[1]) -- strings are *values*
- a[1] = undef
- -- fill a with some `collectable' values (in both parts of the table)
- for i=1,lim do a[i] = {} end
- for i=1,lim do a[i..'x'] = {} end
- -- and some non-collectable ones
- for i=1,lim do local t={}; a[t]=t end
- for i=1,lim do a[i+lim]=i..'x' end
- collectgarbage()
- local i = 0
- for k,v in pairs(a) do assert(k==v or k-lim..'x' == v); i=i+1 end
- assert(i == 2*lim)
- a = {}; setmetatable(a, {__mode = 'kv'});
- local x, y, z = {}, {}, {}
- -- keep only some items
- a[1], a[2], a[3] = x, y, z
- a[string.rep('$', 11)] = string.rep('$', 11)
- -- fill a with some `collectable' values
- for i=4,lim do a[i] = {} end
- for i=1,lim do a[{}] = i end
- for i=1,lim do local t={}; a[t]=t end
- collectgarbage()
- assert(next(a) ~= nil)
- local i = 0
- for k,v in pairs(a) do
- assert((k == 1 and v == x) or
- (k == 2 and v == y) or
- (k == 3 and v == z) or k==v);
- i = i+1
- end
- assert(i == 4)
- x,y,z=nil
- collectgarbage()
- assert(next(a) == string.rep('$', 11))
- do -- invalid mode
- local a = setmetatable({}, {__mode = 34})
- collectgarbage()
- end
- if T then -- bug since 5.3: all-weak tables are not being revisited
- T.gcstate("propagate")
- local t = setmetatable({}, {__mode = "kv"})
- T.gcstate("enteratomic") -- 't' was visited
- setmetatable(t, {__mode = "kv"})
- T.gcstate("pause") -- its new metatable is not being visited
- assert(getmetatable(t).__mode == "kv")
- end
- -- 'bug' in 5.1
- a = {}
- local t = {x = 10}
- local C = setmetatable({key = t}, {__mode = 'v'})
- local C1 = setmetatable({[t] = 1}, {__mode = 'k'})
- a.x = t -- this should not prevent 't' from being removed from
- -- weak table 'C' by the time 'a' is finalized
- setmetatable(a, {__gc = function (u)
- assert(C.key == nil)
- assert(type(next(C1)) == 'table')
- end})
- a, t = nil
- collectgarbage()
- collectgarbage()
- assert(next(C) == nil and next(C1) == nil)
- C, C1 = nil
- -- ephemerons
- local mt = {__mode = 'k'}
- a = {{10},{20},{30},{40}}; setmetatable(a, mt)
- x = nil
- for i = 1, 100 do local n = {}; a[n] = {k = {x}}; x = n end
- GC()
- local n = x
- local i = 0
- while n do n = a[n].k[1]; i = i + 1 end
- assert(i == 100)
- x = nil
- GC()
- for i = 1, 4 do assert(a[i][1] == i * 10); a[i] = undef end
- assert(next(a) == nil)
- local K = {}
- a[K] = {}
- for i=1,10 do a[K][i] = {}; a[a[K][i]] = setmetatable({}, mt) end
- x = nil
- local k = 1
- for j = 1,100 do
- local n = {}; local nk = k%10 + 1
- a[a[K][nk]][n] = {x, k = k}; x = n; k = nk
- end
- GC()
- local n = x
- local i = 0
- while n do local t = a[a[K][k]][n]; n = t[1]; k = t.k; i = i + 1 end
- assert(i == 100)
- K = nil
- GC()
- -- assert(next(a) == nil)
- -- testing errors during GC
- if T then
- collectgarbage("stop") -- stop collection
- local u = {}
- local s = {}; setmetatable(s, {__mode = 'k'})
- setmetatable(u, {__gc = function (o)
- local i = s[o]
- s[i] = true
- assert(not s[i - 1]) -- check proper finalization order
- if i == 8 then error("@expected@") end -- error during GC
- end})
- for i = 6, 10 do
- local n = setmetatable({}, getmetatable(u))
- s[n] = i
- end
- warn("@on"); warn("@store")
- collectgarbage()
- assert(string.find(_WARN, "error in __gc"))
- assert(string.match(_WARN, "@(.-)@") == "expected"); _WARN = false
- for i = 8, 10 do assert(s[i]) end
- for i = 1, 5 do
- local n = setmetatable({}, getmetatable(u))
- s[n] = i
- end
- collectgarbage()
- for i = 1, 10 do assert(s[i]) end
- getmetatable(u).__gc = nil
- warn("@normal")
- end
- print '+'
- -- testing userdata
- if T==nil then
- (Message or print)('\n >>> testC not active: skipping userdata GC tests <<<\n')
- else
- local function newproxy(u)
- return debug.setmetatable(T.newuserdata(0), debug.getmetatable(u))
- end
- collectgarbage("stop") -- stop collection
- local u = newproxy(nil)
- debug.setmetatable(u, {__gc = true})
- local s = 0
- local a = {[u] = 0}; setmetatable(a, {__mode = 'vk'})
- for i=1,10 do a[newproxy(u)] = i end
- for k in pairs(a) do assert(getmetatable(k) == getmetatable(u)) end
- local a1 = {}; for k,v in pairs(a) do a1[k] = v end
- for k,v in pairs(a1) do a[v] = k end
- for i =1,10 do assert(a[i]) end
- getmetatable(u).a = a1
- getmetatable(u).u = u
- do
- local u = u
- getmetatable(u).__gc = function (o)
- assert(a[o] == 10-s)
- assert(a[10-s] == undef) -- udata already removed from weak table
- assert(getmetatable(o) == getmetatable(u))
- assert(getmetatable(o).a[o] == 10-s)
- s=s+1
- end
- end
- a1, u = nil
- assert(next(a) ~= nil)
- collectgarbage()
- assert(s==11)
- collectgarbage()
- assert(next(a) == nil) -- finalized keys are removed in two cycles
- end
- -- __gc x weak tables
- local u = setmetatable({}, {__gc = true})
- -- __gc metamethod should be collected before running
- setmetatable(getmetatable(u), {__mode = "v"})
- getmetatable(u).__gc = function (o) os.exit(1) end -- cannot happen
- 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 = function (o)
- assert(next(getmetatable(o).x) == nil)
- m = 10
- end
- u, m = nil
- collectgarbage()
- assert(m==10)
- do -- tests for string keys in weak tables
- collectgarbage(); collectgarbage()
- local m = collectgarbage("count") -- current memory
- local a = setmetatable({}, {__mode = "kv"})
- a[string.rep("a", 2^22)] = 25 -- long string key -> number value
- a[string.rep("b", 2^22)] = {} -- long string key -> collectable value
- a[{}] = 14 -- collectable key
- collectgarbage()
- local k, v = next(a) -- string key with number value preserved
- assert(k == string.rep("a", 2^22) and v == 25)
- assert(next(a, k) == nil) -- everything else cleared
- assert(a[string.rep("b", 2^22)] == undef)
- a[k] = undef -- erase this last entry
- k = nil
- collectgarbage()
- assert(next(a) == nil)
- -- make sure will not try to compare with dead key
- assert(a[string.rep("b", 100)] == undef)
- assert(collectgarbage("count") <= m + 1) -- everything collected
- end
- -- errors during collection
- if T then
- warn("@store")
- u = setmetatable({}, {__gc = function () error "@expected error" end})
- u = nil
- collectgarbage()
- assert(string.find(_WARN, "@expected error")); _WARN = false
- warn("@normal")
- end
- if not _soft then
- print("long list")
- local a = {}
- for i = 1,200000 do
- a = {next = a}
- end
- a = nil
- collectgarbage()
- end
- -- create many threads with self-references and open upvalues
- print("self-referenced threads")
- local thread_id = 0
- local threads = {}
- local function fn (thread)
- local x = {}
- threads[thread_id] = function()
- thread = x
- end
- coroutine.yield()
- end
- while thread_id < 1000 do
- local thread = coroutine.create(fn)
- coroutine.resume(thread, thread)
- thread_id = thread_id + 1
- end
- -- Create a closure (function inside 'f') with an upvalue ('param') that
- -- points (through a table) to the closure itself and to the thread
- -- ('co' and the initial value of 'param') where closure is running.
- -- Then, assert that table (and therefore everything else) will be
- -- collected.
- do
- local collected = false -- to detect collection
- collectgarbage(); collectgarbage("stop")
- do
- local function f (param)
- ;(function ()
- assert(type(f) == 'function' and type(param) == 'thread')
- param = {param, f}
- setmetatable(param, {__gc = function () collected = true end})
- coroutine.yield(100)
- end)()
- end
- local co = coroutine.create(f)
- assert(coroutine.resume(co, co))
- end
- -- Now, thread and closure are not reachable any more.
- collectgarbage()
- assert(collected)
- collectgarbage("restart")
- end
- do
- collectgarbage()
- collectgarbage"stop"
- collectgarbage("step") -- steps should not unblock the collector
- local x = gcinfo()
- repeat
- for i=1,1000 do _ENV.a = {} end -- no collection during the loop
- until gcinfo() > 2 * x
- collectgarbage"restart"
- _ENV.a = nil
- end
- if T then -- tests for weird cases collecting upvalues
- local function foo ()
- local a = {x = 20}
- coroutine.yield(function () return a.x end) -- will run collector
- assert(a.x == 20) -- 'a' is 'ok'
- a = {x = 30} -- create a new object
- assert(T.gccolor(a) == "white") -- of course it is new...
- coroutine.yield(100) -- 'a' is still local to this thread
- end
- local t = setmetatable({}, {__mode = "kv"})
- collectgarbage(); collectgarbage('stop')
- -- create coroutine in a weak table, so it will never be marked
- t.co = coroutine.wrap(foo)
- local f = t.co() -- create function to access local 'a'
- T.gcstate("enteratomic") -- ensure all objects are traversed
- assert(T.gcstate() == "enteratomic")
- assert(t.co() == 100) -- resume coroutine, creating new table for 'a'
- assert(T.gccolor(t.co) == "white") -- thread was not traversed
- T.gcstate("pause") -- collect thread, but should mark 'a' before that
- assert(t.co == nil and f() == 30) -- ensure correct access to 'a'
- collectgarbage("restart")
- -- test barrier in sweep phase (backing userdata to gray)
- local u = T.newuserdata(0, 1) -- create a userdata
- collectgarbage()
- collectgarbage"stop"
- local a = {} -- avoid 'u' as first element in 'allgc'
- T.gcstate"enteratomic"
- T.gcstate"sweepallgc"
- local x = {}
- assert(T.gccolor(u) == "black") -- userdata is "old" (black)
- assert(T.gccolor(x) == "white") -- table is "new" (white)
- debug.setuservalue(u, x) -- trigger barrier
- assert(T.gccolor(u) == "gray") -- userdata changed back to gray
- collectgarbage"restart"
- print"+"
- end
- if T then
- local debug = require "debug"
- collectgarbage("stop")
- local x = T.newuserdata(0)
- local y = T.newuserdata(0)
- debug.setmetatable(y, {__gc = nop}) -- bless the new udata before...
- debug.setmetatable(x, {__gc = nop}) -- ...the old one
- assert(T.gccolor(y) == "white")
- T.checkmemory()
- collectgarbage("restart")
- end
- if T then
- collectgarbage("stop")
- T.gcstate("pause")
- local sup = {x = 0}
- local a = setmetatable({}, {__newindex = sup})
- T.gcstate("enteratomic")
- assert(T.gccolor(sup) == "black")
- a.x = {} -- should not break the invariant
- assert(not (T.gccolor(sup) == "black" and T.gccolor(sup.x) == "white"))
- T.gcstate("pause") -- complete the GC cycle
- sup.x.y = 10
- collectgarbage("restart")
- end
- if T then
- print("emergency collections")
- collectgarbage()
- collectgarbage()
- T.totalmem(T.totalmem() + 200)
- for i=1,200 do local a = {} end
- T.totalmem(0)
- collectgarbage()
- local t = T.totalmem("table")
- local a = {{}, {}, {}} -- create 4 new tables
- assert(T.totalmem("table") == t + 4)
- t = T.totalmem("function")
- a = function () end -- create 1 new closure
- assert(T.totalmem("function") == t + 1)
- t = T.totalmem("thread")
- a = coroutine.create(function () end) -- create 1 new coroutine
- assert(T.totalmem("thread") == t + 1)
- end
- -- create an object to be collected when state is closed
- do
- local setmetatable,assert,type,print,getmetatable =
- setmetatable,assert,type,print,getmetatable
- local tt = {}
- tt.__gc = function (o)
- assert(getmetatable(o) == tt)
- -- create new objects during GC
- local a = 'xuxu'..(10+3)..'joao', {}
- ___Glob = o -- resurrect object!
- setmetatable({}, tt) -- creates a new one with same metatable
- print(">>> closing state " .. "<<<\n")
- end
- local u = setmetatable({}, tt)
- ___Glob = {u} -- avoid object being collected before program end
- end
- -- create several objects to raise errors when collected while closing state
- if T then
- local error, assert, find, warn = error, assert, string.find, warn
- local n = 0
- local lastmsg
- local mt = {__gc = function (o)
- n = n + 1
- assert(n == o[1])
- if n == 1 then
- _WARN = false
- elseif n == 2 then
- assert(find(_WARN, "@expected warning"))
- lastmsg = _WARN -- get message from previous error (first 'o')
- else
- assert(lastmsg == _WARN) -- subsequent error messages are equal
- end
- warn("@store"); _WARN = false
- error"@expected warning"
- end}
- for i = 10, 1, -1 do
- -- create object and preserve it until the end
- table.insert(___Glob, setmetatable({i}, mt))
- end
- end
- -- just to make sure
- assert(collectgarbage'isrunning')
- do -- check that the collector is not reentrant in incremental mode
- local res = true
- setmetatable({}, {__gc = function ()
- res = collectgarbage()
- end})
- collectgarbage()
- assert(not res)
- end
- collectgarbage(oldmode)
- print('OK')
|