From c8a8572a21a0e8dadcf6d71b8a6d8efe997fe10d Mon Sep 17 00:00:00 2001 From: Glenn Maynard Date: Sun, 15 Oct 2006 01:58:36 +0000 Subject: [PATCH] Allow yielding the Lua engine. This fixes a long-standing limitation with Lua bindings: they couldn't read from disk, or render, or do anything else potentially time-consuming, because that would hold the Lua lock, preventing other threads from using it. --- stepmania/src/LuaManager.cpp | 55 ++++++++++++++++++++++++++++++++++-- stepmania/src/LuaManager.h | 5 ++++ 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/stepmania/src/LuaManager.cpp b/stepmania/src/LuaManager.cpp index 99af8fe295..f5c7e1e16f 100644 --- a/stepmania/src/LuaManager.cpp +++ b/stepmania/src/LuaManager.cpp @@ -13,9 +13,11 @@ #include #include #include +#include LuaManager *LUA = NULL; static vector g_FreeStateList; +static map g_ActiveStates; static LuaFunctionList *g_LuaFunctions = NULL; #if defined(_MSC_VER) || defined (_XBOX) @@ -184,11 +186,18 @@ LuaManager::~LuaManager() lua_close( m_pLuaMain ); delete m_pLock; g_FreeStateList.clear(); + ASSERT( g_ActiveStates.empty() ); } Lua *LuaManager::Get() { - m_pLock->Lock(); + bool bLocked = false; + if( !m_pLock->IsLockedByThisThread() ) + { + m_pLock->Lock(); + bLocked = true; + } + ASSERT( lua_gettop(m_pLuaMain) == 1 ); lua_State *pRet; @@ -206,6 +215,7 @@ Lua *LuaManager::Get() g_FreeStateList.pop_back(); } + g_ActiveStates[pRet] = bLocked; return pRet; } @@ -214,10 +224,51 @@ void LuaManager::Release( Lua *&p ) g_FreeStateList.push_back( p ); ASSERT( lua_gettop(p) == 0 ); - m_pLock->Unlock(); + bool bDoUnlock = g_ActiveStates[p]; + g_ActiveStates.erase( p ); + + if( bDoUnlock ) + m_pLock->Unlock(); p = NULL; } +/* + * Low-level access to Lua is always serialized through m_pLock; we never run the Lua + * core simultaneously from multiple threads. However, when a thread has an acquired + * lua_State, it can release Lua for use by other threads. This allows Lua bindings + * to process long-running actions, without blocking all other threads from using Lua + * until it finishes. + * + * Lua *L = LUA->Get(); // acquires L and locks Lua + * lua_newtable(L); // does something with Lua + * LUA->YieldLua(); // unlocks Lua for lengthy operation; L is still owned, but can't be used + * RString s = ReadFile("/filename.txt"); // time-consuming operation; other threads may use Lua in the meantime + * LUA->UnyieldLua(); // relock Lua + * lua_pushstring( L, s ); // finish working with it + * LUA->Release( L ); // release L and unlock Lua + * + * YieldLua() must not be called when already yielded, or when a lua_State has not been + * acquired (you have nothing to yield), and always unyield before releasing the + * state. Recursive handling is OK: + * + * L1 = LUA->Get(); + * LUA->YieldLua(); // yields + * L2 = LUA->Get(); // unyields + * LUA->Release(L2); // re-yields + * LUA->UnyieldLua(); + * LUA->Release(L1); + */ +void LuaManager::YieldLua() +{ + ASSERT( m_pLock->IsLockedByThisThread() ); + + m_pLock->Unlock(); +} + +void LuaManager::UnyieldLua() +{ + m_pLock->Lock(); +} void LuaManager::RegisterTypes() { diff --git a/stepmania/src/LuaManager.h b/stepmania/src/LuaManager.h index c643be3412..d39869eb7f 100644 --- a/stepmania/src/LuaManager.h +++ b/stepmania/src/LuaManager.h @@ -29,6 +29,11 @@ public: Lua *Get(); void Release( Lua *&p ); + /* Explicitly lock and unlock Lua access. This is done automatically by Get() and + * Release(). */ + void YieldLua(); + void UnyieldLua(); + /* Register all subscribing types. There's no harm in registering when already registered. */ void RegisterTypes();