Oberon00 / luabind

Luabind is a library that helps you create bindings between C++ and Lua.
http://oberon00.github.io/luabind/
Other
46 stars 13 forks source link

[enhancement] is_state_unreferenced/set_state_unreferenced_callback extension for luabind::object (luabind::handle) #5

Closed tripleslash closed 11 years ago

tripleslash commented 11 years ago

Opening this as issue as I don't know how to PM someone on github :P

As the title says this is more of an addition to current reference check support, it would make releasing the lua_State even more secure.

Oberon00 commented 11 years ago

I think, this is beyond the scope of Luabind. If you have an luabind::object, you logically depend on the lua_State, (which is not the case with shared_ptrs) and should be able to ensure that it is only released if you have no objects from it with your program logic.

tripleslash commented 11 years ago

I see what you mean, however I don't see a problem with reference counted objects that keep alive the lua_State as long as they depend on it. Thats my current memory design model. However lua_State is hold in a raw pointer and therefore makes my life a bit harder. Such an addition would help however :P

One could eventually enable it from a define toggle (performance reasons / whatever). The result of this addition would match the reference counted object memory model perfectly fine and does not represent a logic error in my opinion.

tripleslash commented 11 years ago

I dont see another clean solution. I got no other resources that require the lua state unless those luabind::objects (lua functions) and I want to release it when the last luabind::object dies (callback handler system). I don't think there is already a way to do this easily :(

Oberon00 commented 11 years ago

If you only have the references in your callback handler system, you could just wrap the Lua objects inside you own reference counting object.

tripleslash commented 11 years ago

Thanks for you answer. So you mean I should hold a shared reference to the lua_State in my callback handler? That would cause a lot of trouble. Imagine it looks like that:

struct callback_delegate {
    luabind::object function;
    boost::shared_ptr<lua_State> back_reference;
};

How would I know that if callback_delegate gets destroyed, that it will destroy the luabind::object before decrementing the lua_State reference count and eventually freeing the lua_State (wrapped with a custom deleter), before the object is destroyed? It would depend on the order of the destructor calls which could be entirely random.

The references could probably be everywhere as well. I'm using those luabind::objects all over the place. With that in mind, this could indeed be seen as a real issue. Example: I want to create a lua_State and load a buffer and keep it alive as long as I have function objects to call. Once I got no more objects I would close the state.

Therefore I would prefer a more general usability of is_state_unreferenced to only return true when it is really unreferenced and safe to release (well that should be the purpose of it) :)

Edit: Übrigens grade gesehen, dass du aus Österreich kommst im github Profil. Können also auch gern auf Deutsch schreiben :D

Oberon00 commented 11 years ago

It would depend on the order of the destructor calls which could be entirely random.

It is not: In C++, the order of destruction is always the reverse of construction (except for new/delete of course). Additionally, members are constructed in the order in which they appear in the class definition, which means that you should have back_reference before function.

However, I would not use shared_ptr but an reference count embedded in the lua_State (as is done for the shared_ptr_converter implementation), probably together with a custom dependent_object pointer-like object that increments/decrements this count (this again, could rely on boost::intrusive_ptr but I'm not sure if that is not just adding complexity here).

I want to release it [the lua_State] when the last luabind::object dies

Couldn't you do the reverse, i.e. make sure that your lua functions are removed from your callback system when their __gc/__finalize method is called?

Edit: Übrigens grade gesehen, dass du aus Österreich kommst im github Profil. Können also auch gern auf Deutsch schreiben :D

Ich würde lieber bei Englisch bleiben, da vielleicht auch noch andere an dieser Diskussion interessiert sind, die nicht Deutsch können.

tripleslash commented 11 years ago

How would I do that gc / finalize callback?

Would it have any side effects if I would modify luabind::handle so it calls the alter_use_count function upon construction / deletion?

I think I see the implementation problem: does luabind hold handles somewhere which would prevent the closing of my lua_State and therefore cause a leak?

Oberon00 commented 11 years ago

Would it have any side effects if I would modify luabind::handle so it calls the alter_use_count function upon construction / deletion?

Yes: Luabind has handles stored inside the lua_State so you would (logically) create cyclic references (i.e. the lua_State keeps itself alive) and end up never closing it.

It would be possible for objects but, as I already said, you could also create your own wrapper for object that does this for your own equivalent of alter_use_count (with the only downside that you would have to check two reference counts before closing the state).

If I put a __gc callback there I would need to make sure that my callback event still exists.

Is it really impossible for your use case to define clear ownership semantics so that you can know that e.g. your callback events always outlive the lua_State?

tripleslash commented 11 years ago

Is it really impossible for your use case to define clear ownership semantics so that you can know that e.g. your callback events always outlive the lua_State?

No, I will just store a shared_ptr along with it, seems to be the easiest way around it :p

Edit: Unless you could explain how I could do that __gc callback

Oberon00 commented 11 years ago

Suppose you have a game loop and want to register a function to be called every frame. You could have a API in Lua, where this would look like:

connection = mainloop.onFrame(function() print("Hello!") end)

Then this connection object would (if programmed in C++) disconnect the function in its destructor, which is automatically called by Luabind when the object is collected by the GC (and everything is collected when lua_close() is called). In fact, the corresponding event could also "notify" it's connections when it is destroyed, so that this scenario would be save too. It's basically just RAII.