[Date Prev][Date Next][Thread Prev][Thread Next]
- Subject: Re: Userdata and the GC
- From: Mark Hamburg <mark@...>
- Date: Fri, 23 Oct 2009 16:09:40 -0700
Here's the Lightroom solution. Note that this is working with
reference counted Objective-C. Note also that it only matters for
Objective-C objects that need to persist their Lua proxy because, for
example, they have userdata environments.
We keep a weak table mapping light userdata (the Objective-C object
pointer) to full userdata (the Lua proxy for the object). Pushing an
existing object from it's Objective-C reference is just a lookup. More
clever code would probably use metamethods to do the proxy
construction if it didn't already exist. That's all pretty much
standard stuff. The weak table means that we will generate at most one
proxy for the object in Lua and that that proxy will only stick around
as long as there is a Lua reference to the object.
But what if we want it to stick around longer because, for example,
the Objective-C code needs to vector through the userdata environment
to find other objects?
Well, obviously, we need to create another reference in Lua. So, we
maintain a table with a set of strongly referenced objects. An object
needs to be in this table if we need to preserve it's Lua proxy and
there exist references to it on the Objective-C side of the world.
There's our invariant.
How do we know if there are references on the Objective-C side of the
world? There will be a reference other than the Lua proxy and hence
the retain count will be greater than 1. So, we refine our invariant
to, if an object needs to have its Lua proxy preserved and it has a
retain count greater than 1, then it needs to be in the strong set.
When we create the proxy, we know there must have been an Objective-C
side reference, so we need to add the object to the strong set.
When we read the pointer out of the proxy, the conservative behavior
is to do a retain and an autorelease on the pointer and add it to the
strong set. This will avoid having the pointer floating loose and
potentially having its count get incremented from 1 to 2 where you
can't see it happen. You avoid it by doing the increment yourself. Of
course, that gets a bit expensive in auto-release pool space. You can
reduce that cost by special casing self in your bridging code.
So, now we've got things so that if the retain count goes above 1, we
will guarantee membership in the strong set. How do we get things out
of the set?
Create a userdata object that exists purely to get collected and have
its __gc metamethod called. When the metamethod gets called, scan the
strong set looking for objects with retain counts of 1 and remove them
from the set. Then create a new object to get collected on the next GC
cycle. Provided the number of objects is small, you can do the scan