c++lualua-apilua-c++-connection

Does Lua automatically free userdata?


I have code where I create userdata (a circle) which gets passed to Lua that I also store as a pointer in C++ and put into a vector. After I call lua_close(L), I try to delete the pointer, but this causes the program to crash. I also tried free, but that had the same result

I then learned from this post that Lua will automatically free userdata and therefore I do not need to delete the pointer. Is this true?

I am skeptical because after I close Lua using lua_close(), I can still access the circle, moving it, drawing it, etc.

If I don't delete the pointer to the userdata will this cause a memory leak?

I've tried deleting and freeing the pointer, doing it before I close Lua, but all just crashes

Here's how I create the userdata


int wrap_createCircle(lua_State* luaState){

    float radius = lua_tonumber(luaState, 1);
    CircleShape* circle = (CircleShape*)lua_newuserdata(luaState, sizeof(CircleShape));
    new (circle) CircleShape(radius);
    luaL_getmetatable(luaState, "CircleMetaTable");
    lua_setmetatable(luaState, -2);

    return 1;
}

Solution

  • When you call lua_newuserdata, you're creating a full userdata. The pointer returned by that function will be freed automatically by Lua when the corresponding userdata object goes away, so there will not be a memory leak, and you should not try to free it yourself.

    I am skeptical because after I close Lua using lua_close() I can still access the circle, moving it, drawing it, etc.

    Use-after-free in C++ is Undefined Behavior, which means literally anything can happen, such as accesses succeeding as if the memory were still valid.

    Another note about your function: the C++ standard says this under section 6.7.3 [basic.life]:

    For an object of a class type, the program is not required to call the destructor explicitly before the storage which the object occupies is reused or released; however, if there is no explicit call to the destructor or if a delete-expression (7.6.2.8) is not used to release the storage, the destructor is not implicitly called and any program that depends on the side effects produced by the destructor has undefined behavior.

    So you should probably add a __gc metamethod that calls circle->~CircleShape();.