lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


On Wed, Nov 25, 2009 at 05:17:44PM -0200, Roberto Ierusalimschy wrote:
> > I haven't started using LuaJIT yet, but I have been working with some
> > design patterns that I wonder whether could still be relied on there.
> > 
> > For instance, if I do this:
> > 
> > local bar
> > 
> > local function foo()
> >     local U = newproxy(true)
> >     getmetatable(U).__gc = function(self) print "goodbye world" end
> >     return bar(U)
> > end
> > 
> > function bar(U)
> >     ...lots of processing that might force a garbage-collection...
> >     ...nowhere here do we do anything with U...
> > end
> > 
> > In straight Lua, U will still be alive during the execution of bar, so
> > it won't be gc'd until after bar returns. Could that still be relied on
> > in LuaJIT? Or might the JIT compiler see that U is never touched inside bar,
> > and optimize foo's tail-call in such a way that U is no longer
> > referenced by anybody once control switches to bar?
> > 
> > I realize there are work-arounds. For instance, one could change foo so
> > that the call to bar was no longer a tail-call. Or one could keep U
> > alive during bar's execution in another way (what I ended up doing). I
> > just suspected this might be something I'd need to attend to when using
> > n optimizing compiler.
> 
> A finalizer should be callable at soon as its corresponding value is
> disposable. You should not consider whether there is still a pointer to
> the value, but whether the value is going to be (really) used again.
> 
> If you are not going to use U again, Lua (or LuaJIT or whatever) should
> be able to call its finalizer without interfering with the correctness
> of the program.  Maybe you should not be using __gc for that specific
> task.

In this particular case, there is good reason to keep U alive for a
determinate period, even though we're not going to touch it again.
Perhaps you're right that these reasons are not likely to arise in
general.

The context it came up in was a module providing a try/finally
mechanism. I was guided by earlier discussion on this list, and
especially the techniques used in:

    <http://coxpcall.luaforge.net>
    <http://lua-users.org/wiki/FinalizedExceptions>
    <http://lua-users.org/lists/lua-l/2008-02/msg00243.html>

For the usual cases---that is, successful completion of the protected
block, and ALSO errors raised during the execution of the protected
block---then my wrapper functions will explicitly take care of
running all the finalizers that have been registered.

But you made the point in an earlier thread that if protected blocks
were allowed to yield, their thread might eventually end up being gc'd
instead of resuming. So you said a fully general exception-handling
mechanism had to make use of the __gc mechanism.

What I end up doing then is to create a userdatum U whose sole purpose
is to run any finalizers if they're still registered when it's gc'd. U
should be kept alive as long as the thread it's created in is running.
But we don't need to actually do anything with it.

I ended up with a foo/bar construct like in my example: U is created in
one function, which then tail calls a second one. I wondered whether it
was sufficient to keep U alive that it be passed as an argument to the
bar function. In the end, I ended up not doing it that way. I made an
entry in the table that keeps track of finalizers that pointed to U and
so kept it alive that way. But I wondered whether the reasoning I was
reporting above was right:

Namely, in standard Lua, U wouldn't be collectable inside bar because it
would still be alive on bar's call stack, even if it's never used inside
bar. But in LuaJIT the reference to U might be optimized away, and so
a different technique for keeping it alive would be required.

-- 
Profjim
profjim@jimpryor.net