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&#174; 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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.