From f62711f97cd8b337ca4823bb6d1834f324762fc7 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Mon, 9 Jan 2017 15:56:16 +0100 Subject: LuaState: Fixed race condition in ref tracking. (#3529) --- src/Bindings/LuaState.cpp | 82 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 67 insertions(+), 15 deletions(-) (limited to 'src/Bindings/LuaState.cpp') diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp index 50ce04f7c..61d206109 100644 --- a/src/Bindings/LuaState.cpp +++ b/src/Bindings/LuaState.cpp @@ -44,9 +44,69 @@ extern "C" const cLuaState::cRet cLuaState::Return = {}; const cLuaState::cNil cLuaState::Nil = {}; -/** Each Lua state stores a pointer to its creating cLuaState in Lua globals, under this name. -This way any cLuaState can reference the main cLuaState's TrackedCallbacks, mutex etc. */ -static const char * g_CanonLuaStateGlobalName = "_CuberiteInternal_CanonLuaState"; + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cCanonLuaStates: + +/** Tracks the canon cLuaState instances for each lua_State pointer. +Used for tracked refs - the ref needs to be tracked by a single cLuaState (the canon state), despite being created from a different (attached) cLuaState. +The canon state must be available without accessing the Lua state itself (so it cannot be stored within Lua). */ +class cCanonLuaStates +{ +public: + /** Returns the canon Lua state for the specified lua_State pointer. */ + static cLuaState * GetCanonState(lua_State * a_LuaState) + { + auto & inst = GetInstance(); + cCSLock lock(inst.m_CS); + auto itr = inst.m_CanonStates.find(a_LuaState); + if (itr == inst.m_CanonStates.end()) + { + return nullptr; + } + return itr->second; + } + + /** Adds a new canon cLuaState instance to the map. + Used when a new Lua state is created, this informs the map that a new canon Lua state should be tracked. */ + static void Add(cLuaState & a_LuaState) + { + auto & inst = GetInstance(); + cCSLock lock(inst.m_CS); + ASSERT(inst.m_CanonStates.find(a_LuaState) == inst.m_CanonStates.end()); + inst.m_CanonStates[a_LuaState.operator lua_State *()] = &a_LuaState; + } + + /** Removes the bindings between the specified canon state and its lua_State pointer. + Used when a Lua state is being closed. */ + static void Remove(cLuaState & a_LuaState) + { + auto & inst = GetInstance(); + cCSLock lock(inst.m_CS); + auto itr = inst.m_CanonStates.find(a_LuaState); + ASSERT(itr != inst.m_CanonStates.end()); + inst.m_CanonStates.erase(itr); + } + +protected: + /** The mutex protecting m_CanonStates against multithreaded access. */ + cCriticalSection m_CS; + + /** Map of lua_State pointers to their canon cLuaState instances. */ + std::map m_CanonStates; + + + /** Returns the singleton instance of this class. */ + static cCanonLuaStates & GetInstance(void) + { + static cCanonLuaStates canonLuaStates; + return canonLuaStates; + } +}; @@ -426,10 +486,7 @@ void cLuaState::Create(void) luaL_openlibs(m_LuaState); m_IsOwned = true; cLuaStateTracker::Add(*this); - - // Add the CanonLuaState value into the Lua state, so that we can get it from anywhere: - lua_pushlightuserdata(m_LuaState, reinterpret_cast(this)); - lua_setglobal(m_LuaState, g_CanonLuaStateGlobalName); + cCanonLuaStates::Add(*this); } @@ -493,6 +550,7 @@ void cLuaState::Close(void) } } + cCanonLuaStates::Remove(*this); cLuaStateTracker::Del(*this); lua_close(m_LuaState); m_LuaState = nullptr; @@ -2146,15 +2204,9 @@ void cLuaState::LogStackValues(lua_State * a_LuaState, const char * a_Header) -cLuaState * cLuaState::QueryCanonLuaState(void) +cLuaState * cLuaState::QueryCanonLuaState(void) const { - // Get the CanonLuaState global from Lua: - auto cb = WalkToNamedGlobal(g_CanonLuaStateGlobalName); - if (!cb.IsValid()) - { - return nullptr; - } - return reinterpret_cast(lua_touserdata(m_LuaState, -1)); + return cCanonLuaStates::GetCanonState(m_LuaState); } -- cgit v1.2.3