Do not produce duplicate hook on duplicate source.scripting-soc
authorRui Guo <firemeteor.guo@gmail.com>
Mon, 15 Jun 2009 15:24:32 +0000 (15 23:24 +0800)
committerRui Guo <firemeteor.guo@gmail.com>
Mon, 15 Jun 2009 15:24:32 +0000 (15 23:24 +0800)
src/lua.c

index d5fb1d9..041249d 100644 (file)
--- 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);