From 854b259ef17afc51f2fd5dd2ecf90127aea52dd5 Mon Sep 17 00:00:00 2001 From: Glenn Maynard Date: Thu, 2 Nov 2006 04:23:19 +0000 Subject: [PATCH] Replace actor parameters with thread variables. This is to provide parameters to loading Actor definitions, in a way that does not need to be explicitly propagated to nested definitions; I want loading a sub-definition to remain a simple function call with no mandatory (prescribed) parameters. This provides a general context to pass information to Lua about what it's loading, such as LoadingScreen. These are scoped to the running thread, unlike global variables. This prevents possible threading problems. All thread variables are scoped: set a variable, do something, unset it; when finished, all thread variables are unset. These are no longer available after load. By design, to use these later, use upvalues: local color = lua.GetVar("color"); return Def.Quad { OnCommand=cmd(diffuse,color); } --- stepmania/src/LuaManager.cpp | 150 +++++++++++++++++++++++++++++++++++ stepmania/src/LuaManager.h | 21 +++++ 2 files changed, 171 insertions(+) diff --git a/stepmania/src/LuaManager.cpp b/stepmania/src/LuaManager.cpp index 08ff9db8cd..b23a74c2ab 100644 --- a/stepmania/src/LuaManager.cpp +++ b/stepmania/src/LuaManager.cpp @@ -295,6 +295,147 @@ void LuaManager::RegisterTypes() LUA->Release( L ); } +LuaThreadVariable::LuaThreadVariable( const RString &sName, const RString &sValue ) +{ + m_sName = sName; + m_pOldValue = new LuaReference; + + Lua *L = LUA->Get(); + LuaHelpers::Push( L, sValue ); + SetFromStack( L ); + LUA->Release( L ); +} + +LuaThreadVariable::LuaThreadVariable( const RString &sName, LuaReference &Value ) +{ + m_sName = sName; + m_pOldValue = new LuaReference; + + Lua *L = LUA->Get(); + Value.PushSelf( L ); + SetFromStack( L ); + LUA->Release( L ); +} + +RString LuaThreadVariable::GetCurrentThreadIDString() +{ + uint64_t iID = RageThread::GetCurrentThreadID(); + return ssprintf( "%08x%08x", uint32_t(iID >> 32), uint32_t(iID) ); +} + +bool LuaThreadVariable::PushThreadTable( lua_State *L, bool bCreate ) +{ + lua_getfield( L, LUA_REGISTRYINDEX, "LuaThreadVariableTable" ); + if( lua_isnil(L, -1) ) + { + lua_pop( L, 1 ); + if( !bCreate ) + return false; + lua_newtable( L ); + + lua_pushvalue( L, -1 ); + lua_setfield( L, LUA_REGISTRYINDEX, "LuaThreadVariableTable" ); + } + + RString sThreadIDString = GetCurrentThreadIDString(); + LuaHelpers::Push( L, sThreadIDString ); + lua_gettable( L, -2 ); + if( lua_isnil(L, -1) ) + { + lua_pop( L, 1 ); + if( !bCreate ) + { + lua_pop( L, 1 ); + return false; + } + lua_newtable( L ); + + lua_pushinteger( L, 0 ); + lua_rawseti( L, -2, 0 ); + + LuaHelpers::Push( L, sThreadIDString ); + lua_pushvalue( L, -2 ); + lua_settable( L, -4 ); + } + + lua_remove( L, -2 ); + return true; +} + +void LuaThreadVariable::GetThreadVariable( lua_State *L ) +{ + if( !PushThreadTable(L, false) ) + { + lua_pop( L, 1 ); + lua_pushnil( L ); + return; + } + + lua_pushvalue( L, -2 ); + lua_gettable( L, -2 ); + lua_remove( L, -2 ); + lua_remove( L, -2 ); +} + +int LuaThreadVariable::AdjustCount( lua_State *L, int iAdd ) +{ + ASSERT( lua_istable(L, -1) ); + + lua_rawgeti( L, -1, 0 ); + ASSERT( lua_isnumber(L, -1) ); + + int iCount = lua_tointeger( L, -1 ); + lua_pop( L, 1 ); + + iCount += iAdd; + lua_pushinteger( L, iCount ); + lua_rawseti( L, -2, 0 ); + + return iCount; +} + +void LuaThreadVariable::SetFromStack( lua_State *L ) +{ + ASSERT( !m_pOldValue->IsSet() ); // don't call twice + + PushThreadTable( L, true ); + + lua_getfield( L, -1, m_sName ); + m_pOldValue->SetFromStack( L ); + + lua_pushvalue( L, -2 ); + lua_setfield( L, -2, m_sName ); + + AdjustCount( L, +1 ); + + lua_pop( L, 2 ); +} + +LuaThreadVariable::~LuaThreadVariable() +{ + Lua *L = LUA->Get(); + + PushThreadTable( L, true ); + m_pOldValue->PushSelf( L ); + lua_setfield( L, -2, m_sName ); + + if( AdjustCount( L, -1 ) == 0 ) + { + // if empty, delete the table + lua_getfield( L, LUA_REGISTRYINDEX, "LuaThreadVariableTable" ); + ASSERT( lua_istable(L, -1) ); + + LuaHelpers::Push( L, GetCurrentThreadIDString() ); + lua_pushnil( L ); + lua_settable( L, -3 ); + lua_pop( L, 1 ); + } + lua_pop( L, 1 ); + + LUA->Release( L ); + + delete m_pOldValue; +} namespace { @@ -712,9 +853,18 @@ namespace } } + static int GetThreadVariable( lua_State *L ) + { + luaL_checkstring( L, 1 ); + lua_pushvalue( L, 1 ); + LuaThreadVariable::GetThreadVariable( L ); + return 1; + } + const luaL_Reg luaTable[] = { LIST_METHOD( ReadFile ), + LIST_METHOD( GetThreadVariable ), { NULL, NULL } }; } diff --git a/stepmania/src/LuaManager.h b/stepmania/src/LuaManager.h index 571edd479a..3fd161aa96 100644 --- a/stepmania/src/LuaManager.h +++ b/stepmania/src/LuaManager.h @@ -6,6 +6,7 @@ typedef lua_State Lua; typedef void (*RegisterWithLuaFn)(lua_State*); class RageMutex; class XNode; +class LuaReference; extern "C" { @@ -121,6 +122,26 @@ namespace LuaHelpers inline int AbsIndex( Lua *L, int i ) { if( i > 0 || i <= LUA_REGISTRYINDEX ) return i; return lua_gettop( L ) + i + 1; } } +class LuaThreadVariable +{ +public: + LuaThreadVariable( const RString &sName, const RString &sValue ); + LuaThreadVariable( const RString &sName, LuaReference &Value ); + ~LuaThreadVariable(); + static void GetThreadVariable( lua_State *L ); + +private: + LuaThreadVariable( const LuaThreadVariable &cpy ); // not defined + + void SetFromStack( lua_State *L ); + int AdjustCount( lua_State *L, int iAdd ); + static bool PushThreadTable( lua_State *L, bool bCreate ); + static RString GetCurrentThreadIDString(); + + RString m_sName; + LuaReference *m_pOldValue; +}; + /* Iterate over all elements in the table. For safety reasons, the key is pushed onto * the stack and can be read (safely) as a string and popped or altered in any way. * Stack management is handled automatically. That is, you need not remove all stack