diff options
author | Thorbjørn Lindeijer <thorbjorn@lindeijer.nl> | 2012-02-02 22:42:09 +0100 |
---|---|---|
committer | Thorbjørn Lindeijer <thorbjorn@lindeijer.nl> | 2012-02-03 20:54:20 +0100 |
commit | 55d2cf8c86df8655fd826443ebe34b1005a9cf90 (patch) | |
tree | d5f06084ffac68074963e2038fe0dd8b2c995e18 /src | |
parent | 48d44a13e525375ef289ef577e5fc6abf1735e19 (diff) | |
download | manaserv-55d2cf8c86df8655fd826443ebe34b1005a9cf90.tar.gz manaserv-55d2cf8c86df8655fd826443ebe34b1005a9cf90.tar.bz2 manaserv-55d2cf8c86df8655fd826443ebe34b1005a9cf90.tar.xz manaserv-55d2cf8c86df8655fd826443ebe34b1005a9cf90.zip |
Added a generic Lua user data cache
Based on a native Lua table with weak values, so that the user data objects
that are created can be garbage collected when no longer referenced.
Reviewed-by: Yohann Ferreira
Diffstat (limited to 'src')
-rw-r--r-- | src/scripting/lua.cpp | 4 | ||||
-rw-r--r-- | src/scripting/luautil.cpp | 59 | ||||
-rw-r--r-- | src/scripting/luautil.h | 52 |
3 files changed, 106 insertions, 9 deletions
diff --git a/src/scripting/lua.cpp b/src/scripting/lua.cpp index 63149792..b9c76c23 100644 --- a/src/scripting/lua.cpp +++ b/src/scripting/lua.cpp @@ -2703,9 +2703,9 @@ LuaScript::LuaScript(): LuaMapObject::registerType(mState, "MapObject", members_MapObject); // Make script object available to callback functions. - lua_pushlightuserdata(mState, (void *)®istryKey); + lua_pushlightuserdata(mState, const_cast<char *>(®istryKey)); lua_pushlightuserdata(mState, this); - lua_settable(mState, LUA_REGISTRYINDEX); + lua_rawset(mState, LUA_REGISTRYINDEX); // Push the error handler to first index of the stack lua_getglobal(mState, "debug"); diff --git a/src/scripting/luautil.cpp b/src/scripting/luautil.cpp index b7680c6b..7580ac21 100644 --- a/src/scripting/luautil.cpp +++ b/src/scripting/luautil.cpp @@ -51,6 +51,65 @@ void raiseWarning(lua_State *, const char *format, ...) LOG_WARN("Lua script error: "<< message); } + +char UserDataCache::mRegistryKey; + +bool UserDataCache::retrieve(lua_State *s, void *object) +{ + // Retrieve the cache table + lua_pushlightuserdata(s, &mRegistryKey); // key + lua_rawget(s, LUA_REGISTRYINDEX); // Cache? + + if (lua_isnil(s, -1)) + { + lua_pop(s, 1); + return false; + } + + lua_pushlightuserdata(s, object); // Cache, object + lua_rawget(s, -2); // Cache, UD? + + if (lua_isnil(s, -1)) + { + lua_pop(s, 2); // ... + return false; + } + + lua_replace(s, -2); // UD + return true; +} + +void UserDataCache::insert(lua_State *s, void *object) +{ + // Retrieve the cache table + lua_pushlightuserdata(s, &mRegistryKey); // UD, key + lua_rawget(s, LUA_REGISTRYINDEX); // UD, Cache? + + // Create the cache when it doesn't exist yet + if (lua_isnil(s, -1)) + { + lua_pop(s, 1); // UD + lua_newtable(s); // UD, Cache + + // The metatable that makes the values in the table above weak + lua_newtable(s); // UD, Cache, {} + lua_pushstring(s, "__mode"); + lua_pushstring(s, "v"); + lua_rawset(s, -3); // UD, Cache, { __mode = "v" } + lua_setmetatable(s, -2); // UD, Cache + + lua_pushlightuserdata(s, &mRegistryKey);// UD, Cache, key + lua_pushvalue(s, -2); // UD, Cache, key, Cache + lua_rawset(s, LUA_REGISTRYINDEX); // UD, Cache + } + + lua_pushlightuserdata(s, object); // UD, Cache, object + lua_pushvalue(s, -3); // UD, Cache, object, UD + lua_rawset(s, -3); // UD, Cache { object = UD } + lua_pop(s, 1); // UD +} + + /* Functions below are unsafe, as they assume the script has passed pointers to objects which have not yet been destroyed. If the script never keeps pointers around, there will be no problem. In order to be safe, the engine diff --git a/src/scripting/luautil.h b/src/scripting/luautil.h index 4f456ab6..049276fe 100644 --- a/src/scripting/luautil.h +++ b/src/scripting/luautil.h @@ -45,14 +45,47 @@ void raiseScriptError(lua_State *s, const char *format, ...); void raiseWarning(lua_State *s, const char *format, ...); /** - * A helper class for pushing and checking custom Lua user data types. + * A generic userdata cache based on a native Lua table with weak values. + * + * Caching the created userdata instances has two main advantages: + * + * - It limits memory consumption and is a little faster. Creating these values + * is relatively slow (compared to light userdata). + * + * - It makes sure that two userdata objects that refer to the same C++ object + * compare as equal (because they will be the same userdata object). + */ +class UserDataCache +{ +public: + /** + * Attempts to retrieve a userdata associated with the given object from + * the cache and pushes it on the stack when available. + * + * Returns whether a userdata was pushed. + */ + static bool retrieve(lua_State *s, void *object); + + /** + * Inserts the userdata at the top of the stack in the cache using + * the given object as the key. Leaves the userdata on the stack. + */ + static void insert(lua_State *s, void *object); + +private: + static char mRegistryKey; +}; + + +/** + * A helper class for pushing and checking custom Lua userdata types. */ template <typename T> class LuaUserData { public: /** - * Creates a metatable to be used for the user data associated with the + * Creates a metatable to be used for the userdata associated with the * type. Then, registers the \a members with a library named \a typeName, * and sets the '__index' member of the metatable to this library. */ @@ -73,13 +106,18 @@ public: * Pushes a userdata reference to the given object on the stack. Either by * creating one, or reusing an existing one. */ - static int push(lua_State *L, T *object) + static int push(lua_State *s, T *object) { - T **userData = static_cast<T**>(lua_newuserdata(L, sizeof(T*))); - *userData = object; + if (!UserDataCache::retrieve(s, object)) + { + void *userData = lua_newuserdata(s, sizeof(T*)); + * static_cast<T**>(userData) = object; + + luaL_newmetatable(s, mTypeName); + lua_setmetatable(s, -2); - luaL_newmetatable(L, mTypeName); - lua_setmetatable(L, -2); + UserDataCache::insert(s, object); + } return 1; } |