Am 24.11.2015 um 15:04 schröbte Roberto Ierusalimschy:
>>> First, if the library does not protect metatables, any code can get the
>>> __gc metamethod and call it explicitly.
>>
>> IMHO, protecting the metatable is a good idea anyway so that no-one
>> removes the `__gc` metamethod and you run out of resources somewhere
>> else.
>
> Someone can trivially hold a reference to the object so that you
> run out of resources. There is no need to remove __gc.
Even if I provide a custom environment, or `lua_close()` the Lua state?
>
>
>>> Second, it is a good practice (well, I think it is) to provide an
>>> explicit way to "close" an object that needs finalization.
>>>
>>> The io library, for instance, does that.
>>
>> It's easy for the io library, because it stores pointers. Downside
>> is that the garbage collector isn't aware of the extra memory, and
>> memory fragmentation could be higher since there are multiple
>> allocations for each userdata now. For every non-pointer userdata
>> you'd have to add an extra field to indicate the state of
>> finalization.
>
> 1 bit is not that expensive.
The size is not the issue (although with padding the one bit can cost
you up to 8 bytes). The point is that you cannot use
lua_newuserdata( L, sizeof( Fl_Input ) );
You have to write
typedef struct {
Fl_Input obj;
char valid;
} Fl_Input_UD;
//...
lua_newuserdata( L, sizeof( Fl_Input_UD ) );
(Unless you add some flags to the Lua `Userdata` type and an API to
access those, see below).
>
>
>> But now every (meta-)method has to check that the userdata is still
>> valid, and most Lua code that uses those (meta-)methods has to check
>> again unless it is ok with a simple getter/setter throwing an error.
>
> Most of these methods have to check that the userdata has the correct
> type, anyway. It should be easy to bundle together these two tests
> (the object has the correct type *and* it is not closed.)
I have written a wrapper around `luaL_newmetatable()`[1],
`lua_newuserdata()`[2][3], and `luaL_checkudata()`[4] to do just
that[5]. It also handles the struct vs pointer difference transparently
(i.e. the check function always returns `Fl_Input*` regardless of
whether an `Fl_Input` or an `Fl_Input*` is stored in the userdata) and
can check the validity of the parent object if you want to expose
members of structs, unions or classes as userdata.
[1]:
https://github.com/siffiejoe/lua-moon#moon_defobject [2]:
https://github.com/siffiejoe/lua-moon#moon_newobject [3]:
https://github.com/siffiejoe/lua-moon#moon_newpointer [4]:
https://github.com/siffiejoe/lua-moon#moon_checkobject [5]:
https://github.com/siffiejoe/lua-moon#moon_killobject
>
>
>> Also there are cases where explicitly "closing" an object is unsafe,
>> e.g. if another object holds a pointer to that object. You can
>> ensure the correct `__gc` order by storing references in the
>> appropriate uservalue tables, but invalidating dependent objects is
>> harder -- especially if the dependencies might change at runtime.
>> Concrete examples from the last three libraries I created bindings
>> for are renderers and textures in libSDL, memory pools and any other
>> APR object in the Apache Portable Runtime library, and
>> Fl_Input_Choice and its Fl_Input and Fl_Menu_Button subwidgets in
>> FLTK.
>
> It is worth remembering that, by using simple tricks with weak tables,
> it is not difficult to allow an object to be naturally finalized (that
> is, it has its __gc called by the collector) *and* to keep a reference
> to it. So, if you are worried about that situation, you need some
> kind of extra protection anyway; it is not enough to hide the __gc
> from the user.
Ah, I didn't realize that. It seems I have to remove the metatable from
some of my finalized objects to avoid that issue for now.
> (A better technique in some cases is to hide the
> object itself from the user.)
I have (another) unrelated question:
After a `lua_pcall(L, 0, LUA_MULTRET, 0)`, does Lua have `EXTRA_STACK`
stack slots available for its API functions? I.e. if I don't want to
call `lua_checkstack()`, do I have to free only the stack slots I need
myself or the `EXTRA_STACK` stack slots for the Lua API as well?
>
> -- Roberto
>
Philipp