mirror of
https://github.com/lua/lua
synced 2024-11-30 00:23:24 +03:00
0dc5deca1c
OLD1 objects can be potentially anywhere in the 'allgc' list (up to 'reallyold'), but frequently they are all after 'old1' (natural evolution of survivals) or do not exist at all (when all objects die young). So, instead of 'markold' starts looking for them always from the start of 'allgc', the collector keeps an extra pointer, 'firstold1', that points to the first OLD1 object in the 'allgc' list, or is NULL if there are no OLD1 objects in that list.
156 lines
4.4 KiB
Lua
156 lines
4.4 KiB
Lua
-- $Id: testes/gengc.lua $
|
|
-- See Copyright Notice in file all.lua
|
|
|
|
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", 0)
|
|
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", 0)
|
|
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", 0) -- 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", 0) -- 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", 0) -- 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", 0) -- 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", 0) -- 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", 0) -- 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", 0) -- hit the barrier
|
|
assert(f() == 123 and old[1][1] == "hello")
|
|
collectgarbage("step", 0) -- run the collector once more
|
|
-- make sure old[1] was not collected
|
|
assert(f() == 123 and old[1][1] == "hello")
|
|
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", 0)
|
|
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", 0)
|
|
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')
|
|
|
|
|
|
|
|
-- just to make sure
|
|
assert(collectgarbage'isrunning')
|
|
|
|
collectgarbage(oldmode)
|
|
|
|
print('OK')
|
|
|