From 1f75d4522210c4774f3de34ddea084cfa764e9e3 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.h | 89 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 86 insertions(+), 3 deletions(-) (limited to 'src/Bindings/LuaState.h') diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h index b795a80d4..bce08f0fe 100644 --- a/src/Bindings/LuaState.h +++ b/src/Bindings/LuaState.h @@ -80,8 +80,11 @@ public: /** Allows to use this class wherever an int (i. e. ref) is to be used */ explicit operator int(void) const { return m_Ref; } + /** Returns the Lua state associated with the value. */ + lua_State * GetLuaState(void) { return m_LuaState; } + protected: - cLuaState * m_LuaState; + lua_State * m_LuaState; int m_Ref; // Remove the copy-constructor: @@ -112,6 +115,69 @@ public: } ; + /** Represents a callback to Lua that C++ code can call. + Is thread-safe and unload-safe. + When the Lua state is unloaded, the callback returns an error instead of calling into non-existent code. + To receive the callback instance from the Lua side, use RefStack() or (better) cLuaState::GetStackValue(). + Note that instances of this class are tracked in the canon LuaState instance, so that they can be invalidated + when the LuaState is unloaded; due to multithreading issues they can only be tracked by-ptr, which has + an unfortunate effect of disabling the copy and move constructors. */ + class cCallback + { + public: + /** Creates an unbound callback instance. */ + cCallback(void) = default; + + ~cCallback() + { + Clear(); + } + + /** Calls the Lua callback, if still available. + Returns true if callback has been called. + Returns false if the Lua state isn't valid anymore. */ + template + bool Call(Args &&... args) + { + cCSLock Lock(m_CS); + if (!m_Ref.IsValid()) + { + return false; + } + cLuaState(m_Ref.GetLuaState()).Call(m_Ref, std::forward(args)...); + return true; + } + + /** Set the contained callback to the function in the specified Lua state's stack position. + If a callback has been previously contained, it is freed first. */ + bool RefStack(cLuaState & a_LuaState, int a_StackPos); + + /** Frees the contained callback, if any. */ + void Clear(void); + + protected: + friend class cLuaState; + + /** The mutex protecting m_Ref against multithreaded access */ + cCriticalSection m_CS; + + /** Reference to the Lua callback */ + cRef m_Ref; + + + /** Invalidates the callback, without untracking it from the cLuaState. + Called only from cLuaState when closing the Lua state. */ + void Invalidate(void); + + /** This class cannot be copied, because it is tracked in the LuaState by-ptr. */ + cCallback(const cCallback &) = delete; + + /** This class cannot be moved, because it is tracked in the LuaState by-ptr. */ + cCallback(cCallback &&) = delete; + }; + typedef SharedPtr cCallbackPtr; + + /** A dummy class that's used only to delimit function args from return values for cLuaState::Call() */ class cRet { @@ -268,6 +334,7 @@ public: bool GetStackValue(int a_StackPos, bool & a_Value); bool GetStackValue(int a_StackPos, cPluginManager::CommandResult & a_Result); bool GetStackValue(int a_StackPos, cRef & a_Ref); + bool GetStackValue(int a_StackPos, cCallback & a_Ref); bool GetStackValue(int a_StackPos, double & a_Value); bool GetStackValue(int a_StackPos, eBlockFace & a_Value); bool GetStackValue(int a_StackPos, eWeather & a_Value); @@ -453,8 +520,7 @@ protected: bool m_IsOwned; /** The subsystem name is used for reporting errors to the console, it is either "plugin %s" or "LuaScript" - whatever is given to the constructor - */ + whatever is given to the constructor. */ AString m_SubsystemName; /** Name of the currently pushed function (for the Push / Call chain) */ @@ -463,6 +529,15 @@ protected: /** Number of arguments currently pushed (for the Push / Call chain) */ int m_NumCurrentFunctionArgs; + /** The tracked callbacks. + This object will invalidate all of these when it is about to be closed. + Protected against multithreaded access by m_CSTrackedCallbacks. */ + std::vector m_TrackedCallbacks; + + /** Protects m_TrackedTallbacks against multithreaded access. */ + cCriticalSection m_CSTrackedCallbacks; + + /** Variadic template terminator: If there's nothing more to push / pop, just call the function. Note that there are no return values either, because those are prefixed by a cRet value, so the arg list is never empty. */ bool PushCallPop(void) @@ -545,6 +620,14 @@ protected: /** Tries to break into the MobDebug debugger, if it is installed. */ static int BreakIntoDebugger(lua_State * a_LuaState); + + /** Adds the specified callback to tracking. + The callback will be invalidated when this Lua state is about to be closed. */ + void TrackCallback(cCallback & a_Callback); + + /** Removes the specified callback from tracking. + The callback will no longer be invalidated when this Lua state is about to be closed. */ + void UntrackCallback(cCallback & a_Callback); } ; -- cgit v1.2.3