Просмотр исходного кода

New method to unload DLLs

External strings created by DLLs may need the DLL code to be
deallocated. This implies that a DLL can only be unloaded after all
its strings were deallocated, which happen only after the run of all
finalizers. To ensure that order, we create a 'library string' to
represent each DLL and keep it locked. When this string is deallocated
(after the deallocation of any string created by the DLL) it closes its
corresponding DLL.
Roberto Ierusalimschy 3 недель назад
Родитель
Сommit
85a3c1699c
3 измененных файлов с 98 добавлено и 36 удалено
  1. 40 35
      loadlib.c
  2. 7 1
      testes/attrib.lua
  3. 51 0
      testes/libs/lib22.c

+ 40 - 35
loadlib.c

@@ -306,6 +306,16 @@ static void setpath (lua_State *L, const char *fieldname,
 /* }================================================================== */
 
 
+/*
+** External strings created by DLLs may need the DLL code to be
+** deallocated. This implies that a DLL can only be unloaded after all
+** its strings were deallocated. To ensure that, we create a 'library
+** string' to represent each DLL, and when this string is deallocated
+** it closes its corresponding DLL.
+** (The string itself is irrelevant; its userdata is the DLL pointer.)
+*/
+
+
 /*
 ** return registry.CLIBS[path]
 */
@@ -320,34 +330,41 @@ static void *checkclib (lua_State *L, const char *path) {
 
 
 /*
-** registry.CLIBS[path] = plib        -- for queries
-** registry.CLIBS[#CLIBS + 1] = plib  -- also keep a list of all libraries
+** Deallocate function for library strings.
+** Unload the DLL associated with the string being deallocated.
 */
-static void addtoclib (lua_State *L, const char *path, void *plib) {
-  lua_getfield(L, LUA_REGISTRYINDEX, CLIBS);
-  lua_pushlightuserdata(L, plib);
-  lua_pushvalue(L, -1);
-  lua_setfield(L, -3, path);  /* CLIBS[path] = plib */
-  lua_rawseti(L, -2, luaL_len(L, -2) + 1);  /* CLIBS[#CLIBS + 1] = plib */
-  lua_pop(L, 1);  /* pop CLIBS table */
+static void *freelib (void *ud, void *ptr, size_t osize, size_t nsize) {
+  /* string itself is irrelevant and static */
+  (void)ptr; (void)osize; (void)nsize;
+  lsys_unloadlib(ud);  /* unload library represented by the string */
+  return NULL;
 }
 
 
 /*
-** __gc tag method for CLIBS table: calls 'lsys_unloadlib' for all lib
-** handles in list CLIBS
+** Create a library string that, when deallocated, will unload 'plib'
 */
-static int gctm (lua_State *L) {
-  lua_Integer n = luaL_len(L, 1);
-  for (; n >= 1; n--) {  /* for each handle, in reverse order */
-    lua_rawgeti(L, 1, n);  /* get handle CLIBS[n] */
-    lsys_unloadlib(lua_touserdata(L, -1));
-    lua_pop(L, 1);  /* pop handle */
-  }
-  return 0;
+static void createlibstr (lua_State *L, void *plib) {
+  static const char dummy[] =  /* common long body for all library strings */
+    "01234567890123456789012345678901234567890123456789";
+  lua_pushexternalstring(L, dummy, sizeof(dummy) - 1, freelib, plib);
 }
 
 
+/*
+** registry.CLIBS[path] = plib          -- for queries.
+** Also create a reference to strlib, so that the library string will
+** only be collected when registry.CLIBS is collected.
+*/
+static void addtoclib (lua_State *L, const char *path, void *plib) {
+  lua_getfield(L, LUA_REGISTRYINDEX, CLIBS);
+  lua_pushlightuserdata(L, plib);
+  lua_setfield(L, -2, path);  /* CLIBS[path] = plib */
+  createlibstr(L, plib);
+  luaL_ref(L, -2);  /* keep library string in CLIBS */
+  lua_pop(L, 1);  /* pop CLIBS table */
+}
+
 
 /* error codes for 'lookforfunc' */
 #define ERRLIB		1
@@ -361,8 +378,8 @@ static int gctm (lua_State *L) {
 ** Then, if 'sym' is '*', return true (as library has been loaded).
 ** Otherwise, look for symbol 'sym' in the library and push a
 ** C function with that symbol.
-** Return 0 and 'true' or a function in the stack; in case of
-** errors, return an error code and an error message in the stack.
+** Return 0 with 'true' or a function in the stack; in case of
+** errors, return an error code with an error message in the stack.
 */
 static int lookforfunc (lua_State *L, const char *path, const char *sym) {
   void *reg = checkclib(L, path);  /* check loaded C libraries */
@@ -704,21 +721,9 @@ static void createsearcherstable (lua_State *L) {
 }
 
 
-/*
-** create table CLIBS to keep track of loaded C libraries,
-** setting a finalizer to close all libraries when closing state.
-*/
-static void createclibstable (lua_State *L) {
-  luaL_getsubtable(L, LUA_REGISTRYINDEX, CLIBS);  /* create CLIBS table */
-  lua_createtable(L, 0, 1);  /* create metatable for CLIBS */
-  lua_pushcfunction(L, gctm);
-  lua_setfield(L, -2, "__gc");  /* set finalizer for CLIBS table */
-  lua_setmetatable(L, -2);
-}
-
-
 LUAMOD_API int luaopen_package (lua_State *L) {
-  createclibstable(L);
+  luaL_getsubtable(L, LUA_REGISTRYINDEX, CLIBS);  /* create CLIBS table */
+  lua_pop(L, 1);  /* will not use it now */
   luaL_newlib(L, pk_funcs);  /* create 'package' table */
   createsearcherstable(L);
   /* set paths */

+ 7 - 1
testes/attrib.lua

@@ -300,6 +300,12 @@ else
   assert(_ENV.x == "lib2-v2" and _ENV.y == DC"lib2-v2")
   assert(lib2.id("x") == true)   -- a different "id" implementation
 
+  for _, len in ipairs{0, 10, 39, 40, 41, 1000} do
+    local str = string.rep("a", len)
+    local str1 = lib2.newstr(str)
+    assert(str == str1)
+  end
+
   -- test C submodules
   local fs, ext = require"lib1.sub"
   assert(_ENV.x == "lib1.sub" and _ENV.y == DC"lib1")
@@ -447,7 +453,7 @@ do
 end
 
 
--- test of large float/integer indices 
+-- test of large float/integer indices
 
 -- compute maximum integer where all bits fit in a float
 local maxint = math.maxinteger

+ 51 - 0
testes/libs/lib22.c

@@ -1,3 +1,7 @@
+/* implementation for lib2-v2 */
+
+#include <string.h>
+
 #include "lua.h"
 #include "lauxlib.h"
 
@@ -8,8 +12,54 @@ static int id (lua_State *L) {
 }
 
 
+struct STR {
+  void *ud;
+  lua_Alloc allocf;
+};
+
+
+static void *t_freestr (void *ud, void *ptr, size_t osize, size_t nsize) {
+  struct STR *blk = (struct STR*)ptr - 1;
+  blk->allocf(blk->ud, blk, sizeof(struct STR) + osize, 0);
+  return NULL;
+}
+
+
+static int newstr (lua_State *L) {
+  size_t len;
+  const char *str = luaL_checklstring(L, 1, &len);
+  void *ud;
+  lua_Alloc allocf = lua_getallocf(L, &ud);
+  struct STR *blk = (struct STR*)allocf(ud, NULL, 0,
+                                        len + 1 + sizeof(struct STR));
+  if (blk == NULL) {  /* allocation error? */
+    lua_pushliteral(L, "not enough memory");
+    lua_error(L);  /* raise a memory error */
+  }
+  blk->ud = ud;  blk->allocf = allocf;
+  memcpy(blk + 1, str, len + 1);
+  lua_pushexternalstring(L, (char *)(blk + 1), len, t_freestr, L);
+  return 1;
+}
+
+
+/*
+** Create an external string and keep it in the registry, so that it
+** will test that the library code is still available (to deallocate
+** this string) when closing the state.
+*/
+static void initstr (lua_State *L) {
+  lua_pushcfunction(L, newstr);
+  lua_pushstring(L,
+     "012345678901234567890123456789012345678901234567890123456789");
+  lua_call(L, 1, 1);  /* call newstr("0123...") */
+  luaL_ref(L, LUA_REGISTRYINDEX);  /* keep string in the registry */
+}
+
+
 static const struct luaL_Reg funcs[] = {
   {"id", id},
+  {"newstr", newstr},
   {NULL, NULL}
 };
 
@@ -18,6 +68,7 @@ LUAMOD_API int luaopen_lib2 (lua_State *L) {
   lua_settop(L, 2);
   lua_setglobal(L, "y");  /* y gets 2nd parameter */
   lua_setglobal(L, "x");  /* x gets 1st parameter */
+  initstr(L);
   luaL_newlib(L, funcs);
   return 1;
 }