Lua yield allows waiting for threads in Lua?
#4
This is the test case I cooked up so far.
Two threads are trying to access a plugin simultaneously and each time the plugin calls a function in C++ the "lua thread" yields and does its thing which allows the other thread to execute the plugin.
Each time a "lua thread" yields, all locks for the plugin are released making it impossible to create a deadlock (even though a deadlock cannot even exist in this setting, even if I didn't use yielding)

I also figured out some cool template magic which makes it really easy to wrap plugins to support yielding.

main.cpp
cCriticalSection LuaStateLock;
cCriticalSection LuaMessageLock;
cCriticalSection LogLock;


extern int tolua_StringSplit(lua_State* tolua_S);
static int tolua_GetNumber(lua_State* a_pLuaState)
{
	tolua_pushnumber( a_pLuaState, 1.234f );
	return 1;
}

class cBaseMessage
{
public:
	virtual int Run() = 0;
	lua_State* pLuaState;
};

typedef std::map<lua_State*, cBaseMessage*> LuaMessageMap;
LuaMessageMap m_LuaMessages;

template< int (*Func1)(lua_State *L) >
static int Lua_Yield_Template( lua_State* a_pLuaState )
{
	class cLuaMessage : public cBaseMessage
	{
	public:
		virtual int Run() override
		{
			return Func1( pLuaState );
		}
	};
	cBaseMessage* pLuaMessage = new cLuaMessage();
	pLuaMessage->pLuaState = a_pLuaState;
	LuaMessageLock.Lock();
	m_LuaMessages[a_pLuaState] = pLuaMessage;
	LuaMessageLock.Unlock();
	
	printf("top: %i\n", lua_gettop(a_pLuaState) );
	return lua_yield(a_pLuaState, lua_gettop(a_pLuaState));
}

// A function to make sure two threads cannot log at the same time (C++ vs Lua)
static int LUA_SafeLog(lua_State* tolua_S)
{
	cCSLock Lock( LogLock );
	const char* str = tolua_tocppstring(tolua_S,1,0);
	printf("%s\n", str);
	return 0;
}


class cTestThread : public cIsThread
{
public:
	cTestThread( const AString & a_ThreadName ) : cIsThread( a_ThreadName ) {}
	void Execute(void) override
	{
		for (int i = 0; i < 400; ++i)
		{
			lua_getglobal(pLuaThread, FunctionToCall.c_str());
			int res = LUA_YIELD;
			do 
			{
				int nargs = 0;
				LuaMessageLock.Lock();
				LuaMessageMap::iterator itr = m_LuaMessages.find(pLuaThread);
				if (itr != m_LuaMessages.end())
				{
					cBaseMessage* pMessage = itr->second;
					m_LuaMessages.erase(itr);
					LuaMessageLock.Unlock();
					
					LuaStateLock.Lock();
					nargs = pMessage->Run();
					LuaStateLock.Unlock();
					delete pMessage;
				}
				else
				{
					LuaMessageLock.Unlock();
				}
				

				LuaStateLock.Lock();
				res = lua_resume(pLuaThread, nargs);
				LuaStateLock.Unlock();
				if (res != LUA_YIELD && res != 0)
				{
					report_errors( pLuaThread, res );
				}
				else if (res == 0) // no error, function returned
				{
					//cCSLock Lock(LogLock);
					//printf("returned type: %s %s\n", luaL_typename(pThread, -1) );
				}
			} while (res == LUA_YIELD);
		}
	}
	lua_State* pLuaThread;
	int LuaThreadReferenceIdx;
	AString FunctionToCall;
};

int main( int argc, char **argv )
{
	cTestThread TestThread1("TestThread1");
	TestThread1.FunctionToCall = "fromOtherThread";
	cTestThread TestThread2("TestThread2");
	TestThread2.FunctionToCall = "myFunc";

	lua_State* pLuaState = lua_open();
	luaL_openlibs( pLuaState );

	lua_register(pLuaState, "SafeLog", LUA_SafeLog);
	lua_register(pLuaState, "YieldGetNumber", Lua_Yield_Template<tolua_GetNumber> );
	lua_register(pLuaState, "YieldStringSplit", Lua_Yield_Template<tolua_StringSplit> );

	int s = luaL_loadfile(pLuaState, "Plugins/test.lua" );
	s = lua_pcall(pLuaState, 0, LUA_MULTRET, 0);

	for (int i = 0; i < 5; ++i)
	{
		LuaStateLock.Lock();
		TestThread1.pLuaThread = lua_newthread(pLuaState);
		TestThread1.LuaThreadReferenceIdx = luaL_ref(pLuaState, LUA_REGISTRYINDEX);
		TestThread2.pLuaThread = lua_newthread(pLuaState);
		TestThread2.LuaThreadReferenceIdx = luaL_ref(pLuaState, LUA_REGISTRYINDEX);
		LuaStateLock.Unlock();

		TestThread1.Start();
		TestThread2.Start();

		TestThread1.Wait();
		TestThread2.Wait();

		luaL_unref(pLuaState,LUA_REGISTRYINDEX,TestThread1.LuaThreadReferenceIdx ); 
		luaL_unref(pLuaState,LUA_REGISTRYINDEX,TestThread2.LuaThreadReferenceIdx ); 
	}

	printf("Lua stack size: %i\n", lua_gettop(pLuaState));
	lua_close( pLuaState );
}

test.lua
local localVar = 0
function GetLocalVar()
	localVar = localVar + 1
	return localVar;
end

function myFunc()
	SafeLog("in myfunc " .. GetLocalVar());
	local blaa = YieldGetNumber()
	SafeLog("blaa=" .. blaa)
	SafeLog("after yielding " .. GetLocalVar())
	return "num: " .. blaa;
end

function fromOtherThread()
	SafeLog("in other thread " .. GetLocalVar())
	local split = YieldStringSplit("one,2,three", ",")
	SafeLog("split: [" .. split[1] .. " - " .. split[2] .. " - " .. split[3] .. "]" )
end

SafeLog("lulwut")
Reply
Thanks given by:


Messages In This Thread
RE: Lua yield allows waiting for threads in Lua? - by FakeTruth - 02-02-2013, 11:44 AM



Users browsing this thread: 3 Guest(s)