lua-users home
lua-l archive

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


On Fri, May 8, 2009 at 1:36 PM, Julien Danjou <julien@danjou.info> wrote:
> Hi,
>
> I've a got a problem right now, and I wonder how I can fix it.
> Let me expose you.
>
> I've a type of object that we will call `ninja' (because ninjas
> are cool).
> Each `ninja' has a weapon: a callback function!
>
> So basically I get a struct defined as:
> typedef struct ninja_t {
>    int weapon;
> } ninja_t;
>
> So far so good. Now, when I give a weapon (function) to a ninja, I do:
> myninja->weapon = luaL_ref(L, LUA_REGISTRYINDEX);
> That set myninja->weapon to a reference number in the registry.
>
> Now, as you know, ninja get killed often, so I store them in a
> weak-keyed table:
> battles = setmetatable({}, { __mode 'k' })
>
> In the `battles' table, I will store a `mutant' as the key, and a ninja
> has the value (we all know that ninja fight mutants).
>
> battles[mymutant] = myninja
>
> So far, so good: as soon as `mymutant' get killed, since it's a
> weak-keyed table, myninja will get unref'ed.
>
> BUT.
>
> Now imagine I add a weapon to myninja and then store it:
>
> myninja.weapon = function () kick_ass_of(mymutant) end
> battles[mymutant] = myninja
>
> Now, I've something which is like an "island" and will be never garbage
> collected. Why? As I understand, that's because myninja.weapon reference
> `mymutant'. But, since it's a function stored in the Lua registry, Lua
> has no clue that this function is used and store for `myninja'. So it
> basically thinks that mymutant has a valid reference.
>
> Schematic:
>
>  From my programmer's point of view:
>
> battles[mymutant] --ref--> myninja --ref--> weapon
>   ^----------------ref------------------------'
>
>  Cyclic ref, but Lua is smart so can collect.
>
>
>  From real Lua point of view
>
> REGISTRY                            battles[mymutant]
>  \                                       /
>   \                                     /
>    \ ref                          ref  /
>     \                                 /
>      `-> mymutant                 myninja
>
>  Uncollectable: mymutant is refed by the callback function (weapon)
>  of the ninja in a table (registry), so no reason to kill
>  battles[mymutant].
>
> I clearly need to tell Lua: "hey buddy, I'm storing things in the
> registry for `myninja', so don't think it's totally unrelated".
> I am not sure I can do that.
>
> What's the solution guys?
>
> Cheers,
> --
> Julien Danjou
> // ᐰ <julien@danjou.info>   http://julien.danjou.info
> // 9A0D 5FD9 EB42 22F6 8974  C95C A462 B51E C2FE E5CD
> // Don't give up.
>
> -----BEGIN PGP SIGNATURE-----
> Version: GnuPG v1.4.9 (GNU/Linux)
>
> iEYEARECAAYFAkoEJzAACgkQpGK1HsL+5c1srwCfSp1R2dd49565Vxk5AXBLqc/K
> NbAAn0qwAlrYa5q2l9vTIIJfe6ISnptm
> =ArOx
> -----END PGP SIGNATURE-----
>
>

There is probably a better way than using luaL_ref and luaL_unref to
associate a callback with your myninja object. I assume myninja is a
full userdata in the Lua universe - if so, I would give myninja a
unique "environment" table (lua_setfenv and lua_getfenv work on full
userdata, and are only available from the API), and have the weapon
callback be a normal key-value entry in that. If not, what is it, a
light userdata?

-Duncan