LuaState: Fixed race condition in ref tracking. (#3529)

This commit is contained in:
Mattes D
2017-01-09 15:56:16 +01:00
committed by GitHub
parent 20c8e7474b
commit f62711f97c
4 changed files with 70 additions and 18 deletions
+67 -15
View File
@@ -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<lua_State *, cLuaState *> 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<void *>(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<cLuaState *>(lua_touserdata(m_LuaState, -1));
return cCanonLuaStates::GetCanonState(m_LuaState);
}