"Assertion failed" with Native Extension on iOS when invoke callbacks (Solved)

I couldn’t figure it out how to safely invoke lua callbacks from c++ thread.

Little info; I’m developing a NE for our multiplayer game using uWebSockets and msgpack(it may change).
Every socket connection has its own “thread” with lua callbacks like onConnect, onError, onMessage… I’m using two threats for now but it will be just one.

Everything works fine on desktop(mac) even when I hard on it. I believe it is because the processor speed.
But on ios(ipad 3 - ARMv7) I have bunch of “Assertion failed” crashes and I have no idea how to deal with them.
I couldn’t find any pattern of crashes. If I go slow on mobile everything works for a while(launch game->wait, open connection/threat->play-> wait, close connection->wait, <-loop). Also I’m having more crashes according the debug or release builds.

I’m using @britzl 's luautils

Assertion failed: (top + 2 == lua_gettop(L)), function lua_pushlistener

void lua_pushlistener(lua_State *L, struct lua_Listener &listener)
{
	int top = lua_gettop(L);
	// get the function callback from the registry and push it to the top of the stack
	lua_rawgeti(L, LUA_REGISTRYINDEX, listener.m_Callback);
	// get self from registry and push it to the top of the stack
	lua_rawgeti(L, LUA_REGISTRYINDEX, listener.m_Self);
	// push copy of self to top of the stack
	lua_pushvalue(L, -1);
	// set current script instance from top of the stack (and pop it)
	dmScript::SetInstance(L);

	assert(top + 2 == lua_gettop(L));
}

This is the method which I invoke callbacks on thread. event is a struct

Assertion failed: (top == lua_gettop(L)), function handleEvents

void Connection::handleEvents(lua_Listener event)
{
    if (event.m_Callback == LUA_NOREF)
    {
        return;
    }

    lua_State *L = event.m_L;
    //Validate lua stack ??
    DM_LUA_STACK_CHECK(L, 0);

    int top = lua_gettop(L);
    int ret;
    //Invoke callback
    lua_pushlistener(L, event);

    if (Connection::protocolID == Protocols::JOIN_ROOM)
    {
        lua_pushinteger(L, Connection::protocolID);       /* push 2st argument */
        lua_pushstring(L, Connection::sessionid.c_str()); /* push 3nd argument */
        lua_pushstring(L, Connection::roomname.c_str());  /* push 4nd argument */
        ret = lua_pcall(L, 4, 0, 0);
    }
    else
    {
        ret = lua_pcall(L, 1, 0, 0);
    }

    if (ret != 0)
    {
        printf("Error while invoking process terminate callback: %s\n", lua_tostring(L, -1));
        lua_pop(L, 1); // pop error message
    }
    assert(top == lua_gettop(L));
}

If I go hard on it and init the connection immediately on mobile when game launch I got other random crash (those script files are not related to me)
Each time, one of these:

Assertion failed: (top == lua_gettop(L)), function GetURL, file ../src/script.cpp, line 649.
Assertion failed: (n == lua_gettop(L)), function LuaPrint, file ../src/script.cpp, line 335.
Assertion failed: (top + 1 == lua_gettop(L)), function PushHash, file ../src/script_hash.cpp, line 191.
Assertion failed: (top + 1 == lua_gettop(L)), function PushHash, file ../src/script_hash.cpp, line 191.

Sorry for the long post.
I know it is not easy to solve this kind of problem by looking at a small part of it. But multiplayer server and client is the most important part of our game and I really want to continue with Defold.
Any suggestions are most welcome.

Looks like the stack is inconsistent, the DM_LUA_STACK_CHECK macro validates (int this case) that no new values has been pushed to the stack (and none removed).

Perhaps the functions you call in your callback are pushing values to the stack but not popping them before returning?

If you have DM_LUA_STACK_CHECK in your function you don’t need to explicitly do assert(top == lua_gettop(L));

Don’t know what is really wrong here, but maybe this gets you some hints?

1 Like

Lua runs on the main thread. You shouldn’t call it from another thread as that might mess up the Lua state.

As @inactive-vilse mentions, it might be that your callback function fails for some reason?

1 Like

But everything is working incredibly fine on desktop. I never ever get any error. :disappointed_relieved:
Then there is no way of invoking a lua function from thread? This is a really bad news for me.

I’m not sure right now. As far as I remember they aren’t pushing any values(yet). I’m going to check them out.

Thank you.

I clean up everything from Lua side. All lua callback are just printing like this. But still crashing

local function onConnect(self)	
    print("General - onConnect")
end

Is this possibly about the Luajit on desktop versus pure Lua on ios thing?
If so this is just a big disappointment for me.

That’s really no guarantee. Lua isn’t thread safe, and you should write your program with that in mind.
If you use threads when programming, you need to be aware of thread safety. There really is no other option.

It’s really not different from programming UI related things in various api’s (QT, JavaFX, etc) where you need to invoke the calls on the UI thread.

There are various ways to deal with communicating between threads. One way we use is to post commands to a shared data structure, that you can then process in your UpdateExtension function.
Here’s such an example (https://github.com/defold/extension-admob/blob/master/admob/src/googlemobileads.cpp), although it should be noted that there too is an issue. In fact, we don’t protect that data queue with a lock of some kind (E.g. a mutex). To make sure that the data structure isn’t modified from two threads at once, you need to protect it.

4 Likes

It is quite a complex project and hard to understand but I’m going to dive in. I hope I can manage to fix this. Thank you.

2 Likes

Finally I manage to fix this issue as you suggested @Mathias_Westerdahl
Needs a lots of testing of course but it runs very solid so far.
Thank you again @Mathias_Westerdahl and @inactive-vilse

5 Likes