123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182 |
- -- $Id: testes/gengc.lua $
- -- See Copyright Notice in file lua.h
- print('testing generational garbage collection')
- local debug = require"debug"
- assert(collectgarbage("isrunning"))
- collectgarbage()
- local oldmode = collectgarbage("generational")
- -- ensure that table barrier evolves correctly
- do
- local U = {}
- -- full collection makes 'U' old
- collectgarbage()
- assert(not T or T.gcage(U) == "old")
- -- U refers to a new table, so it becomes 'touched1'
- U[1] = {x = {234}}
- assert(not T or (T.gcage(U) == "touched1" and T.gcage(U[1]) == "new"))
- -- both U and the table survive one more collection
- collectgarbage("step")
- assert(not T or (T.gcage(U) == "touched2" and T.gcage(U[1]) == "survival"))
- -- both U and the table survive yet another collection
- -- now everything is old
- collectgarbage("step")
- assert(not T or (T.gcage(U) == "old" and T.gcage(U[1]) == "old1"))
- -- data was not corrupted
- assert(U[1].x[1] == 234)
- end
- do
- -- ensure that 'firstold1' is corrected when object is removed from
- -- the 'allgc' list
- local function foo () end
- local old = {10}
- collectgarbage() -- make 'old' old
- assert(not T or T.gcage(old) == "old")
- setmetatable(old, {}) -- new table becomes OLD0 (barrier)
- assert(not T or T.gcage(getmetatable(old)) == "old0")
- collectgarbage("step") -- new table becomes OLD1 and firstold1
- assert(not T or T.gcage(getmetatable(old)) == "old1")
- setmetatable(getmetatable(old), {__gc = foo}) -- get it out of allgc list
- collectgarbage("step") -- should not seg. fault
- end
- do -- bug in 5.4.0
- -- When an object aged OLD1 is finalized, it is moved from the list
- -- 'finobj' to the *beginning* of the list 'allgc', but that part of the
- -- list was not being visited by 'markold'.
- local A = {}
- A[1] = false -- old anchor for object
- -- obj finalizer
- local function gcf (obj)
- A[1] = obj -- anchor object
- assert(not T or T.gcage(obj) == "old1")
- obj = nil -- remove it from the stack
- collectgarbage("step") -- do a young collection
- print(getmetatable(A[1]).x) -- metatable was collected
- end
- collectgarbage() -- make A old
- local obj = {} -- create a new object
- collectgarbage("step") -- make it a survival
- assert(not T or T.gcage(obj) == "survival")
- setmetatable(obj, {__gc = gcf, x = "+"}) -- create its metatable
- assert(not T or T.gcage(getmetatable(obj)) == "new")
- obj = nil -- clear object
- collectgarbage("step") -- will call obj's finalizer
- end
- do -- another bug in 5.4.0
- local old = {10}
- collectgarbage() -- make 'old' old
- local co = coroutine.create(
- function ()
- local x = nil
- local f = function ()
- return x[1]
- end
- x = coroutine.yield(f)
- coroutine.yield()
- end
- )
- local _, f = coroutine.resume(co) -- create closure over 'x' in coroutine
- collectgarbage("step") -- make upvalue a survival
- old[1] = {"hello"} -- 'old' go to grayagain as 'touched1'
- coroutine.resume(co, {123}) -- its value will be new
- co = nil
- collectgarbage("step") -- hit the barrier
- assert(f() == 123 and old[1][1] == "hello")
- collectgarbage("step") -- run the collector once more
- -- make sure old[1] was not collected
- assert(f() == 123 and old[1][1] == "hello")
- end
- do -- bug introduced in commit 9cf3299fa
- local t = setmetatable({}, {__mode = "kv"}) -- all-weak table
- collectgarbage() -- full collection
- assert(not T or T.gcage(t) == "old")
- t[1] = {10}
- assert(not T or (T.gcage(t) == "touched1" and T.gccolor(t) == "gray"))
- collectgarbage("step") -- minor collection
- assert(not T or (T.gcage(t) == "touched2" and T.gccolor(t) == "black"))
- collectgarbage("step") -- minor collection
- assert(not T or T.gcage(t) == "old") -- t should be black, but it was gray
- t[1] = {10} -- no barrier here, so t was still old
- collectgarbage("step") -- minor collection
- -- t, being old, is ignored by the collection, so it is not cleared
- assert(t[1] == nil) -- fails with the bug
- end
- if T == nil then
- (Message or print)('\n >>> testC not active: \z
- skipping some generational tests <<<\n')
- print 'OK'
- return
- end
- -- ensure that userdata barrier evolves correctly
- do
- local U = T.newuserdata(0, 1)
- -- full collection makes 'U' old
- collectgarbage()
- assert(T.gcage(U) == "old")
- -- U refers to a new table, so it becomes 'touched1'
- debug.setuservalue(U, {x = {234}})
- assert(T.gcage(U) == "touched1" and
- T.gcage(debug.getuservalue(U)) == "new")
- -- both U and the table survive one more collection
- collectgarbage("step")
- assert(T.gcage(U) == "touched2" and
- T.gcage(debug.getuservalue(U)) == "survival")
- -- both U and the table survive yet another collection
- -- now everything is old
- collectgarbage("step")
- assert(T.gcage(U) == "old" and
- T.gcage(debug.getuservalue(U)) == "old1")
- -- data was not corrupted
- assert(debug.getuservalue(U).x[1] == 234)
- end
- -- just to make sure
- assert(collectgarbage'isrunning')
- do print"testing stop-the-world collection"
- local step = collectgarbage("param", "stepsize", 0);
- collectgarbage("incremental")
- assert(collectgarbage("param", "stepsize") == 0)
- -- each step does a complete cycle
- assert(collectgarbage("step"))
- assert(collectgarbage("step"))
- -- back to default value
- collectgarbage("param", "stepsize", step);
- assert(collectgarbage("param", "stepsize") == step)
- end
- collectgarbage(oldmode)
- print('OK')
|