gengc.lua (5537B)
1 -- $Id: testes/gengc.lua $ 2 -- See Copyright Notice in file all.lua 3 4 print('testing generational garbage collection') 5 6 local debug = require"debug" 7 8 assert(collectgarbage("isrunning")) 9 10 collectgarbage() 11 12 local oldmode = collectgarbage("generational") 13 14 15 -- ensure that table barrier evolves correctly 16 do 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") 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") 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) 37 end 38 39 40 do 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") -- 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") -- should not seg. fault 53 end 54 55 56 do -- 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") -- 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") -- 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") -- will call obj's finalizer 80 end 81 82 83 do -- 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") -- 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") -- hit the barrier 102 assert(f() == 123 and old[1][1] == "hello") 103 collectgarbage("step") -- run the collector once more 104 -- make sure old[1] was not collected 105 assert(f() == 123 and old[1][1] == "hello") 106 end 107 108 109 do -- 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") -- minor collection 116 assert(not T or (T.gcage(t) == "touched2" and T.gccolor(t) == "black")) 117 collectgarbage("step") -- 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") -- 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 123 end 124 125 126 if T == nil then 127 (Message or print)('\n >>> testC not active: \z 128 skipping some generational tests <<<\n') 129 print 'OK' 130 return 131 end 132 133 134 -- ensure that userdata barrier evolves correctly 135 do 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") 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") 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) 159 end 160 161 -- just to make sure 162 assert(collectgarbage'isrunning') 163 164 165 do print"testing stop-the-world collection" 166 local step = collectgarbage("param", "stepsize", 0); 167 collectgarbage("incremental") 168 assert(collectgarbage("param", "stepsize") == 0) 169 170 -- each step does a complete cycle 171 assert(collectgarbage("step")) 172 assert(collectgarbage("step")) 173 174 -- back to default value 175 collectgarbage("param", "stepsize", step); 176 assert(collectgarbage("param", "stepsize") == step) 177 end 178 179 collectgarbage(oldmode) 180 181 print('OK') 182