From 3aa76568232968306e8e2d31878d76f0530a41ea Mon Sep 17 00:00:00 2001 From: Mattes D Date: Wed, 2 Mar 2016 11:34:39 +0100 Subject: Added cLuaState::cCallback for representing (resettable) Lua callbacks. --- src/Bindings/LuaState.cpp | 149 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 145 insertions(+), 4 deletions(-) (limited to 'src/Bindings/LuaState.cpp') diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp index 200878cf7..8e4f48275 100644 --- a/src/Bindings/LuaState.cpp +++ b/src/Bindings/LuaState.cpp @@ -20,6 +20,10 @@ extern "C" #include "../Entities/Entity.h" #include "../BlockEntities/BlockEntity.h" + + + + // fwd: "SQLite/lsqlite3.c" extern "C" { @@ -39,6 +43,10 @@ extern "C" const cLuaState::cRet cLuaState::Return = {}; +/** 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"; + @@ -113,6 +121,72 @@ cLuaStateTracker & cLuaStateTracker::Get(void) +//////////////////////////////////////////////////////////////////////////////// +// cLuaState::cCallback: + +bool cLuaState::cCallback::RefStack(cLuaState & a_LuaState, int a_StackPos) +{ + // Check if the stack contains a function: + if (!lua_isfunction(a_LuaState, a_StackPos)) + { + return false; + } + + // Clear any previous callback: + Clear(); + + // Add self to LuaState's callback-tracking: + a_LuaState.TrackCallback(*this); + + // Store the new callback: + cCSLock Lock(m_CS); + m_Ref.RefStack(a_LuaState, a_StackPos); + return true; +} + + + + + +void cLuaState::cCallback::Clear(void) +{ + // Free the callback reference: + lua_State * luaState = nullptr; + { + cCSLock Lock(m_CS); + if (!m_Ref.IsValid()) + { + return; + } + luaState = m_Ref.GetLuaState(); + m_Ref.UnRef(); + } + + // Remove from LuaState's callback-tracking: + cLuaState(luaState).UntrackCallback(*this); +} + + + + + +void cLuaState::cCallback::Invalidate(void) +{ + cCSLock Lock(m_CS); + if (!m_Ref.IsValid()) + { + LOGD("%s: Invalidating an already invalid callback at %p, this should not happen", + __FUNCTION__, reinterpret_cast(this) + ); + return; + } + m_Ref.UnRef(); +} + + + + + //////////////////////////////////////////////////////////////////////////////// // cLuaState: @@ -170,6 +244,10 @@ 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); } @@ -206,6 +284,16 @@ void cLuaState::Close(void) Detach(); return; } + + // Invalidate all callbacks: + { + cCSLock Lock(m_CSTrackedCallbacks); + for (auto & c: m_TrackedCallbacks) + { + c->Invalidate(); + } + } + cLuaStateTracker::Del(*this); lua_close(m_LuaState); m_LuaState = nullptr; @@ -871,6 +959,15 @@ bool cLuaState::GetStackValue(int a_StackPos, cRef & a_Ref) +bool cLuaState::GetStackValue(int a_StackPos, cCallback & a_Callback) +{ + return a_Callback.RefStack(*this, a_StackPos); +} + + + + + bool cLuaState::GetStackValue(int a_StackPos, double & a_ReturnedVal) { if (lua_isnumber(m_LuaState, a_StackPos)) @@ -1626,6 +1723,52 @@ int cLuaState::BreakIntoDebugger(lua_State * a_LuaState) +void cLuaState::TrackCallback(cCallback & a_Callback) +{ + // Get the CanonLuaState global from Lua: + auto cb = WalkToNamedGlobal(g_CanonLuaStateGlobalName); + if (!cb.IsValid()) + { + LOGWARNING("%s: Lua state %p has invalid CanonLuaState!", __FUNCTION__, reinterpret_cast(m_LuaState)); + return; + } + auto & canonState = *reinterpret_cast(lua_touserdata(m_LuaState, -1)); + + // Add the callback: + cCSLock Lock(canonState.m_CSTrackedCallbacks); + canonState.m_TrackedCallbacks.push_back(&a_Callback); +} + + + + + +void cLuaState::UntrackCallback(cCallback & a_Callback) +{ + // Get the CanonLuaState global from Lua: + auto cb = WalkToNamedGlobal(g_CanonLuaStateGlobalName); + if (!cb.IsValid()) + { + LOGWARNING("%s: Lua state %p has invalid CanonLuaState!", __FUNCTION__, reinterpret_cast(m_LuaState)); + return; + } + auto & canonState = *reinterpret_cast(lua_touserdata(m_LuaState, -1)); + + // Remove the callback: + cCSLock Lock(canonState.m_CSTrackedCallbacks); + auto & trackedCallbacks = canonState.m_TrackedCallbacks; + trackedCallbacks.erase(std::remove_if(trackedCallbacks.begin(), trackedCallbacks.end(), + [&a_Callback](cCallback * a_StoredCallback) + { + return (a_StoredCallback == &a_Callback); + } + )); +} + + + + + //////////////////////////////////////////////////////////////////////////////// // cLuaState::cRef: @@ -1681,7 +1824,7 @@ void cLuaState::cRef::RefStack(cLuaState & a_LuaState, int a_StackPos) { UnRef(); } - m_LuaState = &a_LuaState; + m_LuaState = a_LuaState; lua_pushvalue(a_LuaState, a_StackPos); // Push a copy of the value at a_StackPos onto the stack m_Ref = luaL_ref(a_LuaState, LUA_REGISTRYINDEX); } @@ -1692,11 +1835,9 @@ void cLuaState::cRef::RefStack(cLuaState & a_LuaState, int a_StackPos) void cLuaState::cRef::UnRef(void) { - ASSERT(m_LuaState->IsValid()); // The reference should be destroyed before destroying the LuaState - if (IsValid()) { - luaL_unref(*m_LuaState, LUA_REGISTRYINDEX, m_Ref); + luaL_unref(m_LuaState, LUA_REGISTRYINDEX, m_Ref); } m_LuaState = nullptr; m_Ref = LUA_REFNIL; -- cgit v1.2.3