summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorThorbjørn Lindeijer <thorbjorn@lindeijer.nl>2012-02-02 22:42:09 +0100
committerThorbjørn Lindeijer <thorbjorn@lindeijer.nl>2012-02-03 20:54:20 +0100
commit55d2cf8c86df8655fd826443ebe34b1005a9cf90 (patch)
treed5f06084ffac68074963e2038fe0dd8b2c995e18 /src
parent48d44a13e525375ef289ef577e5fc6abf1735e19 (diff)
downloadmanaserv-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.cpp4
-rw-r--r--src/scripting/luautil.cpp59
-rw-r--r--src/scripting/luautil.h52
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 *)&registryKey);
+ lua_pushlightuserdata(mState, const_cast<char *>(&registryKey));
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;
}