1-- $Id: testes/gengc.lua $
2-- See Copyright Notice in file all.lua
3
4print('testing generational garbage collection')
5
6local debug = require"debug"
7
8assert(collectgarbage("isrunning"))
9
10collectgarbage()
11
12local oldmode = collectgarbage("generational")
13
14
15-- ensure that table barrier evolves correctly
16do
17 local U = {}
18 -- full collection makes 'U' old
19 collectgarbage()
20 assert(not T or T.gcage(U) == "old")
21
22 -- U refers to a new table, so it becomes 'touched1'
23 U[1] = {x = {234}}
24 assert(not T or (T.gcage(U) == "touched1" and T.gcage(U[1]) == "new"))
25
26 -- both U and the table survive one more collection
27 collectgarbage("step", 0)
28 assert(not T or (T.gcage(U) == "touched2" and T.gcage(U[1]) == "survival"))
29
30 -- both U and the table survive yet another collection
31 -- now everything is old
32 collectgarbage("step", 0)
33 assert(not T or (T.gcage(U) == "old" and T.gcage(U[1]) == "old1"))
34
35 -- data was not corrupted
36 assert(U[1].x[1] == 234)
37end
38
39
40do
41 -- ensure that 'firstold1' is corrected when object is removed from
42 -- the 'allgc' list
43 local function foo () end
44 local old = {10}
45 collectgarbage() -- make 'old' old
46 assert(not T or T.gcage(old) == "old")
47 setmetatable(old, {}) -- new table becomes OLD0 (barrier)
48 assert(not T or T.gcage(getmetatable(old)) == "old0")
49 collectgarbage("step", 0) -- new table becomes OLD1 and firstold1
50 assert(not T or T.gcage(getmetatable(old)) == "old1")
51 setmetatable(getmetatable(old), {__gc = foo}) -- get it out of allgc list
52 collectgarbage("step", 0) -- should not seg. fault
53end
54
55
56do -- bug in 5.4.0
57-- When an object aged OLD1 is finalized, it is moved from the list
58-- 'finobj' to the *beginning* of the list 'allgc', but that part of the
59-- list was not being visited by 'markold'.
60 local A = {}
61 A[1] = false -- old anchor for object
62
63 -- obj finalizer
64 local function gcf (obj)
65 A[1] = obj -- anchor object
66 assert(not T or T.gcage(obj) == "old1")
67 obj = nil -- remove it from the stack
68 collectgarbage("step", 0) -- do a young collection
69 print(getmetatable(A[1]).x) -- metatable was collected
70 end
71
72 collectgarbage() -- make A old
73 local obj = {} -- create a new object
74 collectgarbage("step", 0) -- make it a survival
75 assert(not T or T.gcage(obj) == "survival")
76 setmetatable(obj, {__gc = gcf, x = "+"}) -- create its metatable
77 assert(not T or T.gcage(getmetatable(obj)) == "new")
78 obj = nil -- clear object
79 collectgarbage("step", 0) -- will call obj's finalizer
80end
81
82
83do -- another bug in 5.4.0
84 local old = {10}
85 collectgarbage() -- make 'old' old
86 local co = coroutine.create(
87 function ()
88 local x = nil
89 local f = function ()
90 return x[1]
91 end
92 x = coroutine.yield(f)
93 coroutine.yield()
94 end
95 )
96 local _, f = coroutine.resume(co) -- create closure over 'x' in coroutine
97 collectgarbage("step", 0) -- make upvalue a survival
98 old[1] = {"hello"} -- 'old' go to grayagain as 'touched1'
99 coroutine.resume(co, {123}) -- its value will be new
100 co = nil
101 collectgarbage("step", 0) -- hit the barrier
102 assert(f() == 123 and old[1][1] == "hello")
103 collectgarbage("step", 0) -- run the collector once more
104 -- make sure old[1] was not collected
105 assert(f() == 123 and old[1][1] == "hello")
106end
107
108
109do -- bug introduced in commit 9cf3299fa
110 local t = setmetatable({}, {__mode = "kv"}) -- all-weak table
111 collectgarbage() -- full collection
112 assert(not T or T.gcage(t) == "old")
113 t[1] = {10}
114 assert(not T or (T.gcage(t) == "touched1" and T.gccolor(t) == "gray"))
115 collectgarbage("step", 0) -- minor collection
116 assert(not T or (T.gcage(t) == "touched2" and T.gccolor(t) == "black"))
117 collectgarbage("step", 0) -- minor collection
118 assert(not T or T.gcage(t) == "old") -- t should be black, but it was gray
119 t[1] = {10} -- no barrier here, so t was still old
120 collectgarbage("step", 0) -- minor collection
121 -- t, being old, is ignored by the collection, so it is not cleared
122 assert(t[1] == nil) -- fails with the bug
123end
124
125
126if T == nil then
127 (Message or print)('\n >>> testC not active: \z
128 skipping some generational tests <<<\n')
129 print 'OK'
130 return
131end
132
133
134-- ensure that userdata barrier evolves correctly
135do
136 local U = T.newuserdata(0, 1)
137 -- full collection makes 'U' old
138 collectgarbage()
139 assert(T.gcage(U) == "old")
140
141 -- U refers to a new table, so it becomes 'touched1'
142 debug.setuservalue(U, {x = {234}})
143 assert(T.gcage(U) == "touched1" and
144 T.gcage(debug.getuservalue(U)) == "new")
145
146 -- both U and the table survive one more collection
147 collectgarbage("step", 0)
148 assert(T.gcage(U) == "touched2" and
149 T.gcage(debug.getuservalue(U)) == "survival")
150
151 -- both U and the table survive yet another collection
152 -- now everything is old
153 collectgarbage("step", 0)
154 assert(T.gcage(U) == "old" and
155 T.gcage(debug.getuservalue(U)) == "old1")
156
157 -- data was not corrupted
158 assert(debug.getuservalue(U).x[1] == 234)
159end
160
161-- just to make sure
162assert(collectgarbage'isrunning')
163
164
165
166-- just to make sure
167assert(collectgarbage'isrunning')
168
169collectgarbage(oldmode)
170
171print('OK')
172