2
votes

I have a line of code in Lua like this:

mv.getLabelChildByNameSequence("confirm_btn|button|no_icon_txt").setVisible(false);

getLabelChildByNameSequence() returns a userdata object (created c-side), and then setVisible() calls a function on that object. The problem is, that works most of the time, but sometimes, the garbage collector decides to destroy the intermediate object immediately after creation, and before the second part of the statement happens.

I've found this to be more successful:

local no_icon_txt = mv.getLabelChildByNameSequence("confirm_btn|button|no_icon_txt");
no_icon_txt.setVisible(false);

I suspect that with the explicit local variable, the ref counting works as expected, and the object doesn't get garbage collected until the local variable falls out of scope.

I would much prefer the first example to the second - it's a lot more compact and pretty looking, and more like what you would see in a language like c++. How can I achieve this?

BTW, when the crash happens, it does seem to try to resolve the call to setVisible() to the object's old memory address, despite having just garbage collected that data. Curious.

EDIT: I want to mention I'm using LunaFive for the bindings (http://lua-users.org/wiki/LunaFive). The user data push fn looks like this:

    static void push(lua_State * L, T* instance )
    {
        T **a = (T **) lua_newuserdata(L, sizeof(T *)); // Create userdata
        *a = instance;

        luaL_getmetatable(L, T::className);

        lua_setmetatable(L, -2);
    }

and the function dispatch looks like this:

    static int function_dispatch(lua_State * L)
    {
        int i = (int) lua_tonumber(L, lua_upvalueindex(1));
        T** obj = static_cast < T ** >(lua_touserdata(L, lua_upvalueindex(2)));

        return ((*obj)->*(T::methods[i].func)) (L);
    }

getLabelChildByNameSequence() pushes a new instance of the Label class (let's say), and setVisible() is a method on that class.

1
This crash could sometimes happen in get_userdata().func() and never in get_userdata():func()Egor Skriptunoff
Are you by chance returning this expression? Because otherwise, I'm not sure how you're seeing what you describe. Are you sure this is what is going on? Also, which version of Lua are you using?Nicol Bolas
userData.setVisible(false) doesn't actually call a function on userData, because the dot notation doesn't supply userData as an argument to the userData.setVisible function. The only argument it supplies is false. Colon notation, userData:setVisible(false), would though, because it is roughly equivalent to userData.setVisible(userData, false). I don't know how the userData.setVisible function is supposed to access the userData if it is not receiving it as an argument. Maybe you could show the C code for userData.setVisible.cyclaminist
@cyclaminist explanation is sound. You retrieve the function value from the userdata returned by getLabelChildByNameSequence(), and there's nothing preventing that object collection after retrieving the function. You need the reference to userdata somewhere - either passed as first argument to setVisible() by using colon syntax, or by saving the reference in setVisible() upvalues. You need the reference to userdata to modify its visibility either way.Vlad
@Vlad - Upvalues of a C function setVisible() are NOT automatically anchored somewhere until you anchor them manually.Egor Skriptunoff

1 Answers

0
votes

Based on the feedback in the comments to my question, what I did is removed the lua_upvalueindex() business in the function dispatch, and instead require the userdata to be the first argument of the function callback. Then, I switched the dot notation to colon notation, supplying the implicit self. Haven't been able to reproduce the crashes since.

I still don't understand why the upvalue isn't being reference counted correctly before the function invocation. Maybe there's a piece missing in object creation or something.