[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Order of __gc calls of userdata in a dead table.
- From: M Joonas Pihlaja <jpihlaja@...>
- Date: Fri, 22 Jan 2010 12:55:35 +0200 (EET)
Hi all,
I've been trying today to find out a way to portably set an on-unload
function for my C modules and after cogitating on loadlib.c for a
while, determined that the best way to do it was to store in the
registry a userdatum a __gc metamethod. This store would happen at
luaopen_<lib>() time. This ought to work, as far as I can tell,
because the userdatum stored into the registry by
loadlib.c:ll_require() to unload my module should be finalized _after_
the userdatum I store there. The relevant bit of the manual here is
in 2.10.1 where it says:
"At the end of each garbage-collection cycle, the finalizers
for userdata are called in reverse order of their creation,
among those collected in that cycle. That is, the first
finalizer to be called is the one associated with the
userdata created last in the program."
What worries me a little is that it doesn't actually say that the
userdata in a table which is garbage are all collected in the same
cycle, leading to the possibility that the LIFO ordering implied above
is violated over multiple cycles. Whipping up a small test case shows
that something is indeed going wrong:
$ lua /tmp/gctest.lua
fail 16900 finalized after 8957
fail 100000 finalized after 16900
$
The test case creates 100000 userdata with individual __gc metamethods
using newproxy() and stores them into a table. It then nils the
variable holding the table and calls collectgarbage(). The __gc
metamethods check that they are called in LIFO order.
So what that test shows is that the 16000th created userdatum in the
test is finalized after the 8957th userdatum, and likewise for the
100000'th userdatum, so the test seems to trigger a finalizer order
violation. Inspecting the order of finalization with more prints, it
seems that the order of finalized userdata is: 8957, 16900, 100000,
99999, 99998, ...
I'm not well versed in Lua's garbage collector code and would be
obliged if someone could explain what's going on. Any hints on how to
make a non-hacky on-unload function would be great too.
Cheers,
Joonas
8<--------------8<-----------------8<---------------
local last_finalized = math.huge
local function mkgc(i)
return function ()
if i > last_finalized then
print("fail", i, "finalized after", last_finalized)
end
last_finalized = i
end
end
local t = {}
for i=1,1e5 do
local key = tostring(math.random())
local u = newproxy(true)
getmetatable(u).__gc = mkgc(i)
t[key] = u
end
t = nil
collectgarbage()