1

I'm looking at solutions to add garbage collection to my tables (objects) in Lua 5.1. I have found that this can be worked around using newproxy() and __gc:

Lua 5.1 workaround for __gc metamethod for tables https://github.com/katlogic/__gc

What I don't understand is the author's use of inserting the userdata as a field in the table.

All objects you set a metatable on through this wrapper get "polluted" with special key __gc_proxy (can be any string, user definable through __GC_PROXY global). You'll have to special-case it if you iterate over the fields of tables (next(), pairs() ...).

and

There is one thing to concern while using suggested solution - if you will traverse the table by pairs() you will get one addition key. It is possibly to avoid it by using proxy object with proper metamethods in place of original table.

Here is a copy/paste example from the Stack Overflow thread:

function setmt__gc(t, mt)
  local prox = newproxy(true)
  getmetatable(prox).__gc = function() mt.__gc(t) end
  t[prox] = true
  return setmetatable(t, mt)
end
iscollected = false
function gctest(self)
  iscollected = true
  print("cleaning up:", self)
end

test = setmt__gc({}, {__gc = gctest})
collectgarbage()
assert(not iscollected)

for k, v in pairs(test) do
   print(tostring(k) .. " " .. tostring(v))
end

The output is:

userdata: 0003BEB0 true
cleaning up:    table: 00039D58

But this cleanup is from the script ending and not at the call of collectgarbage().

This can be demonstrated by a slightly modified version that ends in a loop. The output should be "cleaning up":

function setmt__gc(t, mt)
  local prox = newproxy(true)
  getmetatable(prox).__gc = function() mt.__gc(t) end
  t[prox] = true
  return setmetatable(t, mt)
end

function gctest(self)
   print("cleaning up:", self)
   io.flush()
end

test = setmt__gc({}, {__gc = gctest})
collectgarbage()

while (true) do
end

Instead, by removing the offending t[prox] = true, the collection works as expected:

function setmt__gc(t, mt)
  local prox = newproxy(true)
  getmetatable(prox).__gc = function() mt.__gc(t) end
  t[prox] = true
  return setmetatable(t, mt)
end

function gctest(self)
   print("cleaning up")
   io.flush()
end

test = setmt__gc({}, {__gc = gctest})
collectgarbage()

while (true) do
end

Output:

cleaning up
Zhro
  • 2,546
  • 2
  • 29
  • 39
  • `But this cleanup is from the script ending and not at the call of collectgarbage()` - This happens because global variable `test` anchors the table, so the table couldn't be garbage collected until program exits – Egor Skriptunoff Aug 30 '19 at 15:57

0 Answers0