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