From 116879b40517c86f5c167751483596be6195bfb3 Mon Sep 17 00:00:00 2001 From: Rui Guo Date: Mon, 15 Jun 2009 23:24:32 +0800 Subject: [PATCH] Do not produce duplicate hook on duplicate source. --- src/lua.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 67 insertions(+), 8 deletions(-) diff --git a/src/lua.c b/src/lua.c index d5fb1d9..041249d 100644 --- a/src/lua.c +++ b/src/lua.c @@ -321,7 +321,7 @@ struct_register(lua_State *L, const char *name, const luaL_reg fn_methods[], con PUSH_TYPE(callback, struct listener) static int -callback_unhook(lua_State *L) +internal_unhook(lua_State *L, int warn) { /*Emulate check_callback() */ struct listener **ppl; @@ -333,18 +333,27 @@ callback_unhook(lua_State *L) if (!*ppl) { - lua_pushboolean(L, 0); - lua_pushstring(L, "Callback already unhooked."); - LuaShowErr(L); + if (warn) + { + lua_pushboolean(L, 0); + lua_pushstring(L, "Callback already unhooked."); + LuaShowErr(L); + } } else { LuaFreeHandler(L, (lua_handler *)&(*ppl)->handler); unregister_listener(*ppl); *ppl = 0; - lua_pushboolean(L, 1); + if (warn) + lua_pushboolean(L, 1); } - return 1; + return warn != 0; +} +static int +callback_unhook(lua_State *L) +{ + return internal_unhook(L, 1); } static const luaL_reg callback_methods[] = { @@ -352,7 +361,14 @@ static const luaL_reg callback_methods[] = { {0, 0} }; +int +callback_collect(lua_State *L) +{ + return internal_unhook(L, 0); +} + static const luaL_reg callback_metamethods[] = { + {"__gc", callback_collect}, {0, 0} }; @@ -824,9 +840,35 @@ int LuaInit(void) /* To store handler funcs */ luaL_getmetatable(L, "screen"); - /* _keyfunc[key]->func */ + + /*_two kinds of information in this table: + * funcreg[key]->func + * funcreg[func]->listener + * The table is weak. That means the registered funcs will be collected once + * the func is no longer available (e.g. overwritten by a new instance of + * the script) + * + * The listener is the unhook ticket of the hook. which should be collected + * once the func is collected. The gc metamethod will be triggered + * accordingly. + * + * To make this process happens faster, GC is forced at the end of each + * source. + * + * TODO: What if some events are triggered with in the sourcing + * procedure? + * */ lua_pushstring(L, "_funcreg"); lua_newtable(L); + + /* prepare a metatable to indicate weak table */ + lua_newtable(L); + lua_pushstring(L, "__mode"); + lua_pushstring(L, "kv"); + lua_rawset(L, -3); + /* Mark weak table */ + lua_setmetatable(L, -2); + lua_rawset(L, -3); lua_pop(L, 1); @@ -889,6 +931,8 @@ int LuaSource(const char *file, int async) LuaShowErr(L); return 0; } + else + lua_gc(L, LUA_GCCOLLECT, 0); return 1; } return 0; @@ -1118,7 +1162,7 @@ LuaRegEvent(lua_State *L) /* signature: hook(obj, event, handler, priv); * or: hook(event, handler, priv) * returns: A ticket for later unregister. */ - int idx = 1, argc = lua_gettop(L); + int idx = 1, func, argc = lua_gettop(L); unsigned int priv = 31; /* Default privilege */ lua_handler lh; @@ -1148,6 +1192,7 @@ LuaRegEvent(lua_State *L) snprintf(evbuf, SEVNAME_MAX, "%s_%s", objname, event); /* Check and get the handler */ + func = idx; lh = LuaCheckHandler(L, idx++, 1); if (!lh) luaL_error(L, "Out of memory"); @@ -1161,6 +1206,7 @@ LuaRegEvent(lua_State *L) if (sev) { struct listener *l; + int ticket; l = (struct listener *)malloc(sizeof(struct listener)); if (!l) return luaL_error(L, "Out of memory"); @@ -1170,6 +1216,19 @@ LuaRegEvent(lua_State *L) register_listener(sev, l); /* Return the ticket for un-register */ push_callback(L, &l); + ticket = lua_gettop(L); + + if (lh->type == LUA_HANDLER_TYPE_F) + { + int sc, funcreg; + luaL_getmetatable(L, "screen"); + sc = lua_gettop(L); + funcreg = LuaPushHTable(L, sc, "_funcreg"); + lua_pushvalue(L, func); + lua_pushvalue(L, ticket); + lua_rawset(L, funcreg); + lua_pop(L, 2); + } } else return luaL_error(L, "Invalid event specified: %s for object %s", event, objname); -- 2.11.4.GIT