Use separate lua_State for different script files.
authorRui Guo <firemeteor.guo@gmail.com>
Tue, 18 Aug 2009 17:10:51 +0000 (19 01:10 +0800)
committerRui Guo <firemeteor.guo@gmail.com>
Tue, 18 Aug 2009 17:10:51 +0000 (19 01:10 +0800)
This totally solves issues with sourcing a script multiple times and make
anonymous hook functions work again.

src/lua.c
src/scripts/cmdcallback.lua

index 08048ea..110eb88 100644 (file)
--- a/src/lua.c
+++ b/src/lua.c
@@ -94,6 +94,7 @@ enum
 struct lua_handler
 {
   int type;
+  lua_State *L;
   union
   {
     const char *name;
@@ -102,18 +103,19 @@ struct lua_handler
 };
 
 typedef struct lua_handler *lua_handler;
-void LuaPushHandler(lua_State *L, lua_handler lh);
+void LuaPushHandler(lua_handler lh);
 void LuaHUnRef(lua_State *L, int key);
 lua_handler LuaCheckHandler(lua_State *L, int idx, int ref);
 
 lua_handler 
-LuaAllocHandler(const char *name, int ref)
+LuaAllocHandler(lua_State *L, const char *name, int ref)
 {
   struct lua_handler *lh;
   lh = (struct lua_handler*) malloc(sizeof(struct lua_handler));
   if (!lh)
     return NULL;
 
+  lh->L = L;
   if (name)
     {
       lh->type = LUA_HANDLER_TYPE_N;
@@ -1068,7 +1070,7 @@ script_input_fn(char *buf, int len, char *priv)
   lua_handler lh = sidata->lh;
   lua_State *L = sidata->L;
 
-  LuaPushHandler(L, lh);
+  LuaPushHandler(lh);
   lua_pushstring(L, buf);
   if (lua_pcall(L, 1, 0, 0) == LUA_ERRRUN)
     {
@@ -1133,7 +1135,7 @@ sev_schedule_fn(struct event *ev, char *data)
   evdeq(ev);
   Free(ev);
 
-  LuaPushHandler(L, lh);
+  LuaPushHandler(lh);
   if (lua_pcall(L, 1, 0, 0) == LUA_ERRRUN)
     {
       if(lua_isstring(L, -1))
@@ -1228,10 +1230,22 @@ prepare_weak_table(lua_State *L, const char *name, const char *mode)
   lua_pop(L, 1);
 }
 
-static lua_State *L;
+struct sfile {
+    lua_State *L;
+    ino_t inode;
+    struct sfile *next;
+};
+
+struct sfile *scripts = NULL;
+
 int LuaInit(void)
 {
-  L = luaL_newstate();
+  return 0;
+}
+lua_State *
+LuaNewState(struct sfile *slist)
+{
+  lua_State *L = luaL_newstate();
 
   luaL_openlibs(L);
 
@@ -1258,15 +1272,22 @@ int LuaInit(void)
    * TODO: What if some events are triggered within the sourcing
    * procedure?
    * */
-  prepare_weak_table(L, "_funcreg", "v");
+  prepare_weak_table(L, "_funcreg", " ");
 
   /* funcunhook[func]->listener
    * 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. */
-  prepare_weak_table(L, "_funcunhook", "k");
+  prepare_weak_table(L, "_funcunhook", " ");
 
-  return 0;
+  /* Hold a reference to the sfile structure to ease unloading the script.*/
+  luaL_getmetatable(L, "screen");
+  lua_pushstring(L, "_sfile");
+  lua_pushlightuserdata(L, slist);
+  lua_rawset(L, -3);
+  lua_pop(L, 1);
+  slist->L = L;
+  return L;
 }
 
 /* An error message on top of the stack. */
@@ -1291,13 +1312,25 @@ static int
 LuaCallProcess(const char *name, struct fn_def defs[])
 {
   int argc = 0;
+  lua_State *L;
+  struct sfile *slist = scripts;
+
+  while (slist) {
+      L = slist->L;
+
+      lua_getfield(L, LUA_GLOBALSINDEX, name);
+      if (lua_isnil(L, -1))
+        {
+          lua_pop(L,1);
+          slist = slist->next;
+        }
+      else
+        break;
+  }
+
+  if (!slist)
+    return 0;
 
-  lua_getfield(L, LUA_GLOBALSINDEX, name);
-  if (lua_isnil(L, -1))
-    {
-      lua_pop(L,1);
-      return 0;
-    }
   for (argc = 0; defs[argc].push_fn; argc++)
     defs[argc].push_fn(L, defs[argc].value);
   if (lua_pcall(L, argc, 0, 0) == LUA_ERRRUN && lua_isstring(L, -1))
@@ -1308,22 +1341,54 @@ LuaCallProcess(const char *name, struct fn_def defs[])
   return 1;
 }
 
+void
+LuaUnload(struct sfile *slist)
+{
+  struct sfile **plist = &scripts;
+  lua_close(slist->L);
+  while (*plist) {
+      if (*plist == slist) {
+          *plist = slist->next;
+          break;
+      }
+      plist = &(*plist)->next;
+  }
+  free(slist);
+}
+
 /* FIXME: Think about this: will it affect the registered handlers?*/
 int LuaSource(const char *file, int async)
 {
-  if (!L)
-    return 0;
   struct stat st;
   if (stat(file, &st) == -1)
     Msg(errno, "Error loading lua script file '%s'", file);
   else
     {
       int len = strlen(file);
+      ino_t inode = st.st_ino;
+      struct sfile *slist = scripts;
+
       if (len < 4 || strncmp(file + len - 4, ".lua", 4) != 0)
        return 0;
-      if (luaL_dofile(L, file) && lua_isstring(L, -1))
+
+      while (slist) {
+          if (slist->inode == inode)
+            break;
+          slist = slist->next;
+      }
+      if (slist)
+        LuaUnload(slist);
+
+      slist = (struct sfile *) malloc(sizeof(struct sfile));
+      slist->next = scripts;
+      LuaNewState(slist);
+      slist->inode = inode;
+
+      if (luaL_dofile(slist->L, file) && lua_isstring(slist->L, -1))
        {
-          LuaShowErr(L);
+          LuaShowErr(slist->L);
+          lua_close(slist->L);
+          free(slist);
           return 0;
        }
       else
@@ -1339,8 +1404,9 @@ int LuaSource(const char *file, int async)
            * collect the ticket itself.
            *
            * TODO: check this out. maybe ask some lua gurus. */
-          lua_gc(L, LUA_GCCOLLECT, 0);
-          lua_gc(L, LUA_GCCOLLECT, 0);
+          lua_gc(slist->L, LUA_GCCOLLECT, 0);
+          lua_gc(slist->L, LUA_GCCOLLECT, 0);
+          scripts = slist;
         }
       return 1;
     }
@@ -1349,10 +1415,14 @@ int LuaSource(const char *file, int async)
 
 int LuaFinit(void)
 {
-  if (!L)
-    return 0;
-  lua_close(L);
-  L = (lua_State*)0;
+  struct sfile *slist = scripts;
+  while (slist) {
+      struct sfile *tmp = slist;
+      lua_close(slist->L);
+      slist = slist->next;
+      free(tmp);
+  }
+  scripts = 0;
   return 0;
 }
 
@@ -1413,18 +1483,30 @@ LuaPushParams(lua_State *L, const char *params, va_list va)
 int LuaCall(const char *func, const char **argv)
 {
   int argc;
-  if (!L)
-    return 0;
+  struct sfile *slist = scripts;
+  lua_State *L = NULL;
+  while (slist) {
+      L = slist->L;
+      lua_getfield(L, LUA_GLOBALSINDEX, func);
+      if (lua_isnil(L, -1))
+        {
+          lua_pop(L, 1);
+          slist = slist->next;
+          return 0;
+        }
+      else
+        break;
+  }
 
-  StackDump(L, "Before LuaCall\n");
-  lua_getfield(L, LUA_GLOBALSINDEX, func);
-  if (lua_isnil(L, -1))
-    {
-      lua_pop(L, 1);
-      lua_pushstring(L, "Could not find the script function\n");
-      LuaShowErr(L);
+  if (!slist) {
+      if (L) {
+          lua_pushstring(L, "Could not find the script function\n");
+          LuaShowErr(L);
+      }
       return 0;
-    }
+  }
+  StackDump(L, "Before LuaCall\n");
+
   for (argc = 0; *argv; argv++, argc++)
     {
       lua_pushstring(L, *argv);
@@ -1454,10 +1536,23 @@ LuaPushHTable(lua_State *L, int screen, const char * t)
   return lua_gettop(L);
 }
 
+struct sfile *
+LuaGetSFile(lua_State *L)
+{
+  struct sfile *slist;
+  luaL_getmetatable(L, "screen");
+  lua_pushstring(L, "_sfile");
+  lua_rawget(L, -1);
+  slist = (struct sfile *)lua_touserdata(L, -1);
+  lua_pop(L, 2);
+  return slist;
+}
+
 void
-LuaPushHandler(lua_State *L, lua_handler lh)
+LuaPushHandler(lua_handler lh)
 {
   int funcreg;
+  lua_State *L = lh->L;
   if (lh->type == LUA_HANDLER_TYPE_F)
     {
       luaL_getmetatable(L, "screen");
@@ -1512,13 +1607,13 @@ LuaCheckHandler(lua_State *L, int idx, int ref)
       if (!lua_isfunction(L, -1))
         luaL_error(L, "The specified handler %s in param #%d is not a function", handler, idx);
       lua_pop(L, 1);
-      return LuaAllocHandler(handler, 0);
+      return LuaAllocHandler(L, handler, 0);
     }
   else if (!lua_isfunction(L, idx))
     luaL_error(L, "Handler should be a function or the name of function");
 
   key = LuaFuncKey(L, idx, ref);
-  return LuaAllocHandler(NULL, key);
+  return LuaAllocHandler(L, NULL, key);
 }
 
 /* }}} **/
@@ -1528,10 +1623,11 @@ LuaDispatch(void *handler, const char *params, va_list va)
 {
   lua_handler lh = (lua_handler)handler;
   int argc, retvalue;
+  lua_State *L = lh->L;
 
   StackDump(L, "before dispatch");
 
-  LuaPushHandler(L, lh);
+  LuaPushHandler(lh);
   if (lua_isnil(L, -1))
     {
       lua_pop(L, 1);
@@ -1558,7 +1654,7 @@ LuaRegAutoUnHook(lua_State *L, lua_handler lh, int ticket)
   luaL_getmetatable(L, "screen");
   sc = lua_gettop(L);
   funcunhook = LuaPushHTable(L, sc, "_funcunhook");
-  LuaPushHandler(L, lh);
+  LuaPushHandler(lh);
   lua_pushvalue(L, ticket);
   lua_rawset(L, funcunhook);
   lua_pop(L, 2);
index 65de0a3..3ef4390 100644 (file)
@@ -31,6 +31,21 @@ end
 ticket1 = screen.hook("cmdexecuted", cmd1)
 ticket2 = screen.hook("cmdexecuted", "cmd2")
 
+screen.hook("cmdexecuted", function(name, args)
+    os.execute('mkdir -p /tmp/debug')
+    local f = io.open('/tmp/debug/33', 'a')
+    f:write("Command executed: " .. name)
+
+    for i, c in pairs(args) do
+      f:write(" " .. c)
+    end
+
+    f:write("\n")
+    f:close()
+    return 0
+end
+)
+
 function unhook()
   ticket1:unhook()
   ticket2:unhook()