Re: Crash in wrapper_base.cpp

Drew McLean <[email protected]> Mon, 1 Aug 2011 14:18:59 -0700
Newsgroups gmane.comp.lang.lua.bind.user
Message-ID <CAGo0s8Pv_HEMpVgPM-qcy=FXkaSEkNSQ=1bP5efYYw1CHSU7SA@mail.gmail.com>
Hey all,

I'm still investigating the problem with m_self becoming invalid after the
game runs for a while.  Nigel has been helping me troubleshoot and we've
eliminated several cases.  Here is a more detailed depiction of the
scenario.


C++ class declarations:

class RTTI
class Agent : public RTTI
class ScriptableAgent : public Agent
struct ScriptableAgent_luabind : public ScriptableAgent, public
luabind::wrap_base


C++ class bindings to lua:

class_< apg::RTTI >("RTTI")
   ...
class_< apg::Agent, apg::RTTI  >("Agent")
   ...
class_< apg::ScriptableAgent, apg::ScriptableAgent_luabind,
bases<apg::Agent, apg::RTTI> >("ScriptableAgent")
   ...


Lua code to create/destroy lua class instances:

g_LuaAgents = {}


function createScriptableAgent(klass, ...)
    -- create the agent
    local agent = _G[klass]( getContextPool(), 0 )
    agent:initialize(unpack(arg))
    agent.name = klass .. agent.actorId

    -- track the agent so it won't be garbage collected
    assert(g_LuaAgents[agent.actorId] == nil)
    g_LuaAgents[agent.actorId] = agent

    if APG_DEBUG then
        print('Registered lua agent: ' .. tostring(agent.actorId) .. '\n')
    end
    getContextPool().agentContainer:addAgent(agent)

    return agent
end

function destroyScriptableAgent(agent)
    if agent ~= nil then
        agent:cleanup()
        if APG_DEBUG then
            print('Unregistering lua agent: ' .. tostring(agent.actorId) ..
'\n')
        end

        -- remove the reference to the agent so it can be garbage collected
        assert(agent ~= nil)
        assert(g_LuaAgents[agent.actorId] == agent)
        g_LuaAgents[agent.actorId] = nil
    end
end



Here is the lua code that extends the C++ class:

class 'TubeTravelSequenceAgent' (ScriptableAgent)

function TubeTravelSequenceAgent:__init(contextPool, aid)
    ScriptableAgent.__init(self, contextPool, aid)
end

function TubeTravelSequenceAgent:__finalize()
     print('TubeTravelSequenceAgent:__finalize() [' ..
tostring(self.actorId) .. ']\n')
end

function TubeTravelSequenceAgent:initialize()
    ScriptableAgent.initialize(self)
    ...
end

function TubeTravelSequenceAgent:update(delta)
    ScriptableAgent.update(self, delta)
    if ... then
        self.contextPool.agentContainer:destroyAgent(self)
    end
end


The TubeTravelSequenceAgent instance is created in Lua by calling
createScriptableAgent which creates the instance, adds it to the global lua
table so it won't be garbage collected and then adds it to the AgentContainer
in C++ which is essentially a list of objects which has update() called on
them every frame.

The instance is updated every frame until it decides that it should be
destroyed in which case it calls AgentContainer.destroyAgent() which will in
turn remove the instance from the internal list and call the Lua function
destroyScriptableAgent() with the instance as the parameter.  This function
removes the instance from the global table and allows it to be garbage
collected.


C++ code catching the error:

void ScriptableEntity_luabind::Update(real delta)
{
     lua_State *L = m_self.state();
    m_self.get(L);
    void* pData = lua_touserdata(L, 1);
    luabind::detail::object_rep* instance =
static_cast<luabind::detail::object_rep*>(pData);
    luabind::detail::class_rep* crep = instance ? instance->crep() : NULL;
    lua_pop(L,1);

    if (!instance)
    {
        luabind::object g_LuaAgents = luabind::globals(L)["g_LuaAgents"];
        luabind::object actor = g_LuaAgents[m_ActorId];
        ASSERT(luabind::type(actor) != LUA_TNIL);

        apg::Agent* pMe = luabind::object_cast<apg::Agent*>(actor);
        ASSERT(pMe == this);
    }

    call<void>("update", delta);
}


Above is the code in the wrapped Update() function which calls into the
Lua-derived class.  I added a bunch of error checking code to examine
the m_self
reference for the instance.  When the problem occurs the code will enter
into the if block where I do some sanity checking. So what I'm seeing is
that pData is NULL which  means the userdata is NULL. Then when I dig into
the Lua global table holding all the agent references I see that the entry
is still there and pointing to the correct Agent (which is this). So the
question is who is clearing the userdata to NULL? It shouldn't be a garbage
collection.  Does this look like slicing?

I'm personally suspecting a bug outside of my own code.  Any help anyone can
give would be greatly appreciated.

   Drew

------------------------------------------------------------------------------
BlackBerry&reg; DevCon Americas, Oct. 18-20, San Francisco, CA
The must-attend event for mobile developers. Connect with experts. 
Get tools for creating Super Apps. See the latest technologies.
Sessions, hands-on labs, demos & much more. Register early & save!
http://p.sf.net/sfu/rim-blackberry-1

_______________________________________________
luabind-user mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/luabind-user