Patch - Fix for cross thread instantation when using wrap base.
Nigel Atkinson <[email protected]>
| Newsgroups | gmane.comp.lang.lua.bind.user |
|---|---|
| Message-ID | <1270572183.9839.36.camel@finwe> |
I've been bug squishing. This one was a bit curly to track down, given the symptoms. I've included a test program that breaks without the patch, and I've tested the fix in a larger program that uses luabind extensively. The test program seg-faults when trying to call the method, even with the thread that made the Lua class still around - which I initially thought was the problem, but it wasn't. The bug only seems to show itself, when using wrap_base and Lua overload-able class methods. The first patch is some minor cleaning that I noticed while stepping through the code. The second is the bug fix. More explanation in the patch files themselves. Nigel PS What's the preferred patch format? I used git format-patch this time. Does that make things easier? ------------------------------------------------------------------------------ Download Intel® Parallel Studio Eval Try the new software tools for yourself. Speed compiling, find bugs proactively, and fine-tune applications for parallel performance. See why Intel Parallel Studio got high marks during beta. http://p.sf.net/sfu/intel-sw-dev _______________________________________________ luabind-user mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/luabind-user
main.cpp
(text/x-c++src, 1.5 KB)
#include <luabind/luabind.hpp>
#include <iostream>
#include <lua.hpp>
using namespace luabind;
using namespace std;
struct TestItem
{
int xx;
TestItem()
{
cout << "Create: " << this << endl;
xx = 1234;
}
virtual ~TestItem()
{
cout << "Destroy: " << this << endl;
}
virtual void method()
{
cout << "Method: " << this << endl;
}
};
struct TestItemWrapper : public TestItem, public wrap_base
{
virtual void method()
{
call<void>( "method" );
}
static void default_method( TestItem* ptr )
{
ptr->TestItem::method();
}
};
int main()
{
lua_State* L = lua_open();
luaL_openlibs( L );
luabind::open(L);
module(L)
[
class_<TestItem, TestItemWrapper>( "TestItem" )
.def( constructor<>() )
.def( "method", &TestItem::method, &TestItemWrapper::default_method )
];
lua_State* thread = lua_newthread( L );
int ref = luaL_ref( L, LUA_REGISTRYINDEX );
if( luaL_dostring( thread, "t = TestItem()" ))
cout << lua_tostring( thread, -1 ) << endl;
cout << "Call method" << endl;
if( luaL_dostring( L, "t:method()" ) )
cout << lua_tostring( L, -1 ) << endl;
luaL_unref( L, LUA_REGISTRYINDEX, ref );
cout << "Call method again after thread is dead and gone." << endl;
if( luaL_dostring( L, "t:method()" ) )
cout << lua_tostring( L, -1 ) << endl;
lua_close( L );
return 0;
}
0001-Remove-redundant-code.patch
(text/x-patch, 1 KB)
>From 33fea37699b558e78017a59d9f83062e0b4fef75 Mon Sep 17 00:00:00 2001 From: Nigel Atkinson <[email protected]> Date: Wed, 7 Apr 2010 02:47:43 +1200 Subject: [PATCH 1/2] Remove redundant code. Removed from class_rep::constructor_dispatcher() the [re]setting of the class instance's environment and metatable. These are already set in the function push_new_instance(). modified: src/class_rep.cpp --- src/class_rep.cpp | 6 ------ 1 files changed, 0 insertions(+), 6 deletions(-) diff --git a/src/class_rep.cpp b/src/class_rep.cpp index 65d798d..70bb623 100755 --- a/src/class_rep.cpp +++ b/src/class_rep.cpp @@ -142,12 +142,6 @@ int luabind::detail::class_rep::constructor_dispatcher(lua_State* L) push_new_instance(L, cls); - cls->get_table(L); - lua_setfenv(L, -2); - - lua_rawgeti(L, LUA_REGISTRYINDEX, cls->metatable_ref()); - lua_setmetatable(L, -2); - if (super_deprecation_disabled && cls->get_class_type() == class_rep::lua_class && !cls->bases().empty()) -- 1.6.3.3
0002-Changes-to-inject-backref-to-be-lua-thread-safe.patch
(text/x-patch, 1.9 KB)
>From 42cf19b7bf11f9fa73656899ec1e20a917ea5d5a Mon Sep 17 00:00:00 2001 From: Nigel Atkinson <[email protected]> Date: Wed, 7 Apr 2010 03:48:17 +1200 Subject: [PATCH 2/2] Changes to inject backref to be lua thread safe. While the actual reference was created in the main thread, by calling get_main_thread(), when another thread created an instance of a class, the userdata for that instance was left on the caller's stack, and not on the main thread's stack. This caused a back reference to the wrong data, and eventual segfault. Now the userdata is copied across to the main thread before creating the back ref, should the calling thread not be the main thread. Also lua_gettop() is used rather than a hard coded index of '1'. If we do copy the userdata from another thread, we don't know what state the main thread's stack is in. modified: luabind/detail/constructor.hpp --- luabind/detail/constructor.hpp | 14 ++++- 1 files changed, 13 insertions(+), 1 deletions(-) diff --git a/luabind/detail/constructor.hpp b/luabind/detail/constructor.hpp index 93b1f56..77e86d0 100644 --- a/luabind/detail/constructor.hpp +++ b/luabind/detail/constructor.hpp @@ -25,7 +25,19 @@ inline void inject_backref(lua_State*, void*, void*) template <class T> void inject_backref(lua_State* L, T* p, wrap_base*) { - weak_ref(get_main_thread(L), 1).swap(wrap_access::ref(*p)); + lua_State *main_thread = get_main_thread(L); + + // If the calling thread is not the main thread, + // copy the userdata across to the main thread. + if( L != main_thread ) + lua_xmove( L, main_thread, 1 ); + + weak_ref(main_thread, lua_gettop( main_thread ) ).swap(wrap_access::ref(*p)); + + // Copy the userdata back across to the calling thread. + // I'm not sure if this is really required. + if( L != main_thread ) + lua_xmove( main_thread, L, 1 ); } template <std::size_t Arity, class T, class Pointer, class Signature> -- 1.6.3.3