[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: Userdata finalization order
- From: Roberto Ierusalimschy <roberto@...>
- Date: Mon, 18 Oct 2010 10:04:09 -0200
> 2010/10/17 Javier Guerra Giraldez <javier@guerrag.com>:
> > is that true?
> >
> > what i would expect is that "a" is unreachable, but GC considers "b"
> > reachable by "a", even if "a" isn't, so "b" would only be collected
> > after "a" (in a different GC cycle)
>
> I was thinking like you before discovering that it is not actually the
> case. I've actually experienced this problem and it took me some time
> to find it.
>
> See this thread http://lua-users.org/lists/lua-l/2010-08/msg00024.html
> and the answer of Benoit Germain.
There is a difference between "finalization" (calling the __gc
metamethod) and "disposal" or "collecting" (actually freeing the object).
Any object that is going to be finalized, plus any object accessible
from it, is never collected before the finalization, no matter the
finalization order. In particular, when Lua closes a state, it calls all
finalizers before disposing any object. There is no way to crash Lua by
accessing Lua objects during finalization (bar bugs in Lua).
C/C++ objects is a different matter. They may be (and often are)
disposed by the finalizer of its proxy in Lua. So, if the finalizer for
object A tries to access C/C++ data associated with object B, and the
finalizer for B disposes that data, then you should ensure that B is
finalized after A. No manipulation of references or weak tables will
solve your problem here. You have basically two solutions:
- If A cannot live without B, then it should be created after B
(otherwise it would have to live without B until B is created).
If it is created after B, Lua ensures that its finalizer will be
called before B's (as pointed out by E.T.)
- If A can live without B, then it could avoid accessing B's data during
its finalization. For instance, B finalization could mark in B's Lua
part that the object is already finalized, by changing its pointer to
C/C++ data to NULL. The finalizer for A then checks whether the pointer
is NULL before accessing that data. (Remember that the pointer itself,
stored in the userdata memory, is always accessible, even after B's
finalization.)
-- Roberto