lua-users home
lua-l archive

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


On Jul 25, 2016 11:05 AM, "Marc Balmer" <marc@msys.ch> wrote:
>
> I am hunting a ghost.  This is my ghost:
>
> Previously, we used the following construct in C code:
>
> p = malloc(sizeof int)
> *p = 42
>
> /* Do a Lua API call that might error (longjmp) out */
>
> free(p)
>
> Daurnimator rightfully pointed out that this would leak memory
> if the Lua API call actually errors out and we changed that to:
>
> p = lua_newuserdate(L, sizeof int)
> *p = 42
>
> /* Do a Lua API call that might error (longjmp) out */
>
> Withe the latter, however, we see sporadic program crashes,
> valgrind emits all kind of "Invalid write of size 8" error messages
> and our customers pick up the phone to call us...
>
> Is it possible that a userdata value get garbage collected when
> it has been created, leading to a dangling pointer?
>
> What would happen if I luaL_ref() the userdata value directly
> after creating it?  To my understanding it would then be
> locked in memory forever, unless I unref it?
>
> So the following would leak the memory as well, if the API call
> errors out?
>
> p = lua_newuserdate(L, sizeof int)
> l = luaL_ref(L, -1)
> *p = 42
>
> /* Do a Lua API call that might error (longjmp) out */
>
> luaL_unref(l)
>
> (The code in question is the Lua PostgreSQL interface on
> github.com/arcapos/luapgsql, the functions conn_execPrepared()
> and get_sql_params())
>
> Thanks for any ideas,
> Marc
>
>

You're correct; userdata values are Lua values, and if you don't leave a reference to them somewhere (either by luaL_ref or returning the value) it will get collected. Lua has no way to know if you've kept the pointer around somewhere.

If you're only using it for temporary storage within a function (and can't use a local variable/calloc instead), just leave it on the stack until the end, then return whatever number of values excluding the userdata, and it will get collected later (or if your function errors out it will be collected at some point after that).

If you need it to persist after your function returns, then you need to correct your design so that either the value is returned and kept around in Lua as long as it's needed, or it's LuaL_ref'd and later unref'd even if the function throws an error.

If you're using C++, Lua can be compiled to use exceptions; that would allow you to use try/finally to clean up after your function finishes, whether it throws or not. Otherwise, you just have to make sure you account for all code paths to avoid leaks.