lua

A copy of the Lua development repository
Log | Files | Refs | README

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