lua-users home
lua-l archive

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

Am 01.03.2016 um 03:56 schröbte Tim Hill:
My reading of section 4.6 of the (5.3) reference is that many Lua APIs can raise an error (for example, as a result of a memory allocation failure), which means that these APIs are not guaranteed to return to the caller. This also presumably applies to any Lua APIs that call metamethods that in turn raise errors.

This model is causing us huge problems in any C functions that have to manage resources such as heap memory. I cannot see any way to guarantee that these resources are freed should an error occur (except for lua_call(), which can be replaced with lua_pcall()).

One option appears to be to use C++ try to catch the error, but much of our code if legacy C, and retrofitting it for C++ is something I’d like to avoid if possible. The only way I can see of doing this is to have each C function coded as a “shell” function that then uses lua_pcall() to call *another* C function to do the work, thus setting up a protected environment for the C function so raised errors care caught locally, and this approach seems cumbersome to say the least, nor the least because the shared resources must be managed in the content of the “shell” function so that they can be cleaned up on an error.

How do other people here handle this?

The answer is simple: Don't allocate resources that Lua won't clean up on its own. I.e. for temporary buffers you use `lua_newuserdata()` instead of `malloc()` and just leave the resulting temporary userdata on the Lua stack as long as you need it. For non-memory temporary resources I once had a pair of functions

    /* allocates a userdata that calls `releasef` in its `__gc`
     * on `*result` if it is non-NULL */
    void** moon_resource( lua_State* L,
                          void (*releasef)( void* ) );
    /* calls `releasef` now instead of in the `__gc` */
    void moon_release( void** ptr );

in my personal toolbox, but I rarely needed them, so they are gone now.
You could keep the temporary resource around longer by putting it in another userdata's uservalue table, but most userdata probably will take ownership and handle the resource in their own `__gc` metamethod (in which case plain `malloc()` is fine).

I only use the nested function calls with `lua_pcall()` when I really need deterministic cleanup, e.g. if I need a flag set during a certain function call only.