diff options
| author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2025-07-09 14:40:36 -0300 |
|---|---|---|
| committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2025-07-09 14:40:36 -0300 |
| commit | 85a3c1699c9587a9e952b4ab75b1c6c310ebebea (patch) | |
| tree | 67e17e9e8f74259a56690456e705a4158e5cc33e | |
| parent | f65d1f9e02d891733d4ff1cf8d4bc91291e0098e (diff) | |
| download | lua-85a3c1699c9587a9e952b4ab75b1c6c310ebebea.tar.gz lua-85a3c1699c9587a9e952b4ab75b1c6c310ebebea.tar.bz2 lua-85a3c1699c9587a9e952b4ab75b1c6c310ebebea.zip | |
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.
| -rw-r--r-- | loadlib.c | 75 | ||||
| -rw-r--r-- | testes/attrib.lua | 8 | ||||
| -rw-r--r-- | testes/libs/lib22.c | 51 |
3 files changed, 98 insertions, 36 deletions
| @@ -307,6 +307,16 @@ static void setpath (lua_State *L, const char *fieldname, | |||
| 307 | 307 | ||
| 308 | 308 | ||
| 309 | /* | 309 | /* |
| 310 | ** External strings created by DLLs may need the DLL code to be | ||
| 311 | ** deallocated. This implies that a DLL can only be unloaded after all | ||
| 312 | ** its strings were deallocated. To ensure that, we create a 'library | ||
| 313 | ** string' to represent each DLL, and when this string is deallocated | ||
| 314 | ** it closes its corresponding DLL. | ||
| 315 | ** (The string itself is irrelevant; its userdata is the DLL pointer.) | ||
| 316 | */ | ||
| 317 | |||
| 318 | |||
| 319 | /* | ||
| 310 | ** return registry.CLIBS[path] | 320 | ** return registry.CLIBS[path] |
| 311 | */ | 321 | */ |
| 312 | static void *checkclib (lua_State *L, const char *path) { | 322 | static void *checkclib (lua_State *L, const char *path) { |
| @@ -320,34 +330,41 @@ static void *checkclib (lua_State *L, const char *path) { | |||
| 320 | 330 | ||
| 321 | 331 | ||
| 322 | /* | 332 | /* |
| 323 | ** registry.CLIBS[path] = plib -- for queries | 333 | ** Deallocate function for library strings. |
| 324 | ** registry.CLIBS[#CLIBS + 1] = plib -- also keep a list of all libraries | 334 | ** Unload the DLL associated with the string being deallocated. |
| 325 | */ | 335 | */ |
| 326 | static void addtoclib (lua_State *L, const char *path, void *plib) { | 336 | static void *freelib (void *ud, void *ptr, size_t osize, size_t nsize) { |
| 327 | lua_getfield(L, LUA_REGISTRYINDEX, CLIBS); | 337 | /* string itself is irrelevant and static */ |
| 328 | lua_pushlightuserdata(L, plib); | 338 | (void)ptr; (void)osize; (void)nsize; |
| 329 | lua_pushvalue(L, -1); | 339 | lsys_unloadlib(ud); /* unload library represented by the string */ |
| 330 | lua_setfield(L, -3, path); /* CLIBS[path] = plib */ | 340 | return NULL; |
| 331 | lua_rawseti(L, -2, luaL_len(L, -2) + 1); /* CLIBS[#CLIBS + 1] = plib */ | ||
| 332 | lua_pop(L, 1); /* pop CLIBS table */ | ||
| 333 | } | 341 | } |
| 334 | 342 | ||
| 335 | 343 | ||
| 336 | /* | 344 | /* |
| 337 | ** __gc tag method for CLIBS table: calls 'lsys_unloadlib' for all lib | 345 | ** Create a library string that, when deallocated, will unload 'plib' |
| 338 | ** handles in list CLIBS | ||
| 339 | */ | 346 | */ |
| 340 | static int gctm (lua_State *L) { | 347 | static void createlibstr (lua_State *L, void *plib) { |
| 341 | lua_Integer n = luaL_len(L, 1); | 348 | static const char dummy[] = /* common long body for all library strings */ |
| 342 | for (; n >= 1; n--) { /* for each handle, in reverse order */ | 349 | "01234567890123456789012345678901234567890123456789"; |
| 343 | lua_rawgeti(L, 1, n); /* get handle CLIBS[n] */ | 350 | lua_pushexternalstring(L, dummy, sizeof(dummy) - 1, freelib, plib); |
| 344 | lsys_unloadlib(lua_touserdata(L, -1)); | ||
| 345 | lua_pop(L, 1); /* pop handle */ | ||
| 346 | } | ||
| 347 | return 0; | ||
| 348 | } | 351 | } |
| 349 | 352 | ||
| 350 | 353 | ||
| 354 | /* | ||
| 355 | ** registry.CLIBS[path] = plib -- for queries. | ||
| 356 | ** Also create a reference to strlib, so that the library string will | ||
| 357 | ** only be collected when registry.CLIBS is collected. | ||
| 358 | */ | ||
| 359 | static void addtoclib (lua_State *L, const char *path, void *plib) { | ||
| 360 | lua_getfield(L, LUA_REGISTRYINDEX, CLIBS); | ||
| 361 | lua_pushlightuserdata(L, plib); | ||
| 362 | lua_setfield(L, -2, path); /* CLIBS[path] = plib */ | ||
| 363 | createlibstr(L, plib); | ||
| 364 | luaL_ref(L, -2); /* keep library string in CLIBS */ | ||
| 365 | lua_pop(L, 1); /* pop CLIBS table */ | ||
| 366 | } | ||
| 367 | |||
| 351 | 368 | ||
| 352 | /* error codes for 'lookforfunc' */ | 369 | /* error codes for 'lookforfunc' */ |
| 353 | #define ERRLIB 1 | 370 | #define ERRLIB 1 |
| @@ -361,8 +378,8 @@ static int gctm (lua_State *L) { | |||
| 361 | ** Then, if 'sym' is '*', return true (as library has been loaded). | 378 | ** Then, if 'sym' is '*', return true (as library has been loaded). |
| 362 | ** Otherwise, look for symbol 'sym' in the library and push a | 379 | ** Otherwise, look for symbol 'sym' in the library and push a |
| 363 | ** C function with that symbol. | 380 | ** C function with that symbol. |
| 364 | ** Return 0 and 'true' or a function in the stack; in case of | 381 | ** Return 0 with 'true' or a function in the stack; in case of |
| 365 | ** errors, return an error code and an error message in the stack. | 382 | ** errors, return an error code with an error message in the stack. |
| 366 | */ | 383 | */ |
| 367 | static int lookforfunc (lua_State *L, const char *path, const char *sym) { | 384 | static int lookforfunc (lua_State *L, const char *path, const char *sym) { |
| 368 | void *reg = checkclib(L, path); /* check loaded C libraries */ | 385 | void *reg = checkclib(L, path); /* check loaded C libraries */ |
| @@ -704,21 +721,9 @@ static void createsearcherstable (lua_State *L) { | |||
| 704 | } | 721 | } |
| 705 | 722 | ||
| 706 | 723 | ||
| 707 | /* | ||
| 708 | ** create table CLIBS to keep track of loaded C libraries, | ||
| 709 | ** setting a finalizer to close all libraries when closing state. | ||
| 710 | */ | ||
| 711 | static void createclibstable (lua_State *L) { | ||
| 712 | luaL_getsubtable(L, LUA_REGISTRYINDEX, CLIBS); /* create CLIBS table */ | ||
| 713 | lua_createtable(L, 0, 1); /* create metatable for CLIBS */ | ||
| 714 | lua_pushcfunction(L, gctm); | ||
| 715 | lua_setfield(L, -2, "__gc"); /* set finalizer for CLIBS table */ | ||
| 716 | lua_setmetatable(L, -2); | ||
| 717 | } | ||
| 718 | |||
| 719 | |||
| 720 | LUAMOD_API int luaopen_package (lua_State *L) { | 724 | LUAMOD_API int luaopen_package (lua_State *L) { |
| 721 | createclibstable(L); | 725 | luaL_getsubtable(L, LUA_REGISTRYINDEX, CLIBS); /* create CLIBS table */ |
| 726 | lua_pop(L, 1); /* will not use it now */ | ||
| 722 | luaL_newlib(L, pk_funcs); /* create 'package' table */ | 727 | luaL_newlib(L, pk_funcs); /* create 'package' table */ |
| 723 | createsearcherstable(L); | 728 | createsearcherstable(L); |
| 724 | /* set paths */ | 729 | /* set paths */ |
diff --git a/testes/attrib.lua b/testes/attrib.lua index d8b6e0f3..8a3462ea 100644 --- a/testes/attrib.lua +++ b/testes/attrib.lua | |||
| @@ -300,6 +300,12 @@ else | |||
| 300 | assert(_ENV.x == "lib2-v2" and _ENV.y == DC"lib2-v2") | 300 | assert(_ENV.x == "lib2-v2" and _ENV.y == DC"lib2-v2") |
| 301 | assert(lib2.id("x") == true) -- a different "id" implementation | 301 | assert(lib2.id("x") == true) -- a different "id" implementation |
| 302 | 302 | ||
| 303 | for _, len in ipairs{0, 10, 39, 40, 41, 1000} do | ||
| 304 | local str = string.rep("a", len) | ||
| 305 | local str1 = lib2.newstr(str) | ||
| 306 | assert(str == str1) | ||
| 307 | end | ||
| 308 | |||
| 303 | -- test C submodules | 309 | -- test C submodules |
| 304 | local fs, ext = require"lib1.sub" | 310 | local fs, ext = require"lib1.sub" |
| 305 | assert(_ENV.x == "lib1.sub" and _ENV.y == DC"lib1") | 311 | assert(_ENV.x == "lib1.sub" and _ENV.y == DC"lib1") |
| @@ -447,7 +453,7 @@ do | |||
| 447 | end | 453 | end |
| 448 | 454 | ||
| 449 | 455 | ||
| 450 | -- test of large float/integer indices | 456 | -- test of large float/integer indices |
| 451 | 457 | ||
| 452 | -- compute maximum integer where all bits fit in a float | 458 | -- compute maximum integer where all bits fit in a float |
| 453 | local maxint = math.maxinteger | 459 | local maxint = math.maxinteger |
diff --git a/testes/libs/lib22.c b/testes/libs/lib22.c index 8e656502..b377cce5 100644 --- a/testes/libs/lib22.c +++ b/testes/libs/lib22.c | |||
| @@ -1,3 +1,7 @@ | |||
| 1 | /* implementation for lib2-v2 */ | ||
| 2 | |||
| 3 | #include <string.h> | ||
| 4 | |||
| 1 | #include "lua.h" | 5 | #include "lua.h" |
| 2 | #include "lauxlib.h" | 6 | #include "lauxlib.h" |
| 3 | 7 | ||
| @@ -8,8 +12,54 @@ static int id (lua_State *L) { | |||
| 8 | } | 12 | } |
| 9 | 13 | ||
| 10 | 14 | ||
| 15 | struct STR { | ||
| 16 | void *ud; | ||
| 17 | lua_Alloc allocf; | ||
| 18 | }; | ||
| 19 | |||
| 20 | |||
| 21 | static void *t_freestr (void *ud, void *ptr, size_t osize, size_t nsize) { | ||
| 22 | struct STR *blk = (struct STR*)ptr - 1; | ||
| 23 | blk->allocf(blk->ud, blk, sizeof(struct STR) + osize, 0); | ||
| 24 | return NULL; | ||
| 25 | } | ||
| 26 | |||
| 27 | |||
| 28 | static int newstr (lua_State *L) { | ||
| 29 | size_t len; | ||
| 30 | const char *str = luaL_checklstring(L, 1, &len); | ||
| 31 | void *ud; | ||
| 32 | lua_Alloc allocf = lua_getallocf(L, &ud); | ||
| 33 | struct STR *blk = (struct STR*)allocf(ud, NULL, 0, | ||
| 34 | len + 1 + sizeof(struct STR)); | ||
| 35 | if (blk == NULL) { /* allocation error? */ | ||
| 36 | lua_pushliteral(L, "not enough memory"); | ||
| 37 | lua_error(L); /* raise a memory error */ | ||
| 38 | } | ||
| 39 | blk->ud = ud; blk->allocf = allocf; | ||
| 40 | memcpy(blk + 1, str, len + 1); | ||
| 41 | lua_pushexternalstring(L, (char *)(blk + 1), len, t_freestr, L); | ||
| 42 | return 1; | ||
| 43 | } | ||
| 44 | |||
| 45 | |||
| 46 | /* | ||
| 47 | ** Create an external string and keep it in the registry, so that it | ||
| 48 | ** will test that the library code is still available (to deallocate | ||
| 49 | ** this string) when closing the state. | ||
| 50 | */ | ||
| 51 | static void initstr (lua_State *L) { | ||
| 52 | lua_pushcfunction(L, newstr); | ||
| 53 | lua_pushstring(L, | ||
| 54 | "012345678901234567890123456789012345678901234567890123456789"); | ||
| 55 | lua_call(L, 1, 1); /* call newstr("0123...") */ | ||
| 56 | luaL_ref(L, LUA_REGISTRYINDEX); /* keep string in the registry */ | ||
| 57 | } | ||
| 58 | |||
| 59 | |||
| 11 | static const struct luaL_Reg funcs[] = { | 60 | static const struct luaL_Reg funcs[] = { |
| 12 | {"id", id}, | 61 | {"id", id}, |
| 62 | {"newstr", newstr}, | ||
| 13 | {NULL, NULL} | 63 | {NULL, NULL} |
| 14 | }; | 64 | }; |
| 15 | 65 | ||
| @@ -18,6 +68,7 @@ LUAMOD_API int luaopen_lib2 (lua_State *L) { | |||
| 18 | lua_settop(L, 2); | 68 | lua_settop(L, 2); |
| 19 | lua_setglobal(L, "y"); /* y gets 2nd parameter */ | 69 | lua_setglobal(L, "y"); /* y gets 2nd parameter */ |
| 20 | lua_setglobal(L, "x"); /* x gets 1st parameter */ | 70 | lua_setglobal(L, "x"); /* x gets 1st parameter */ |
| 71 | initstr(L); | ||
| 21 | luaL_newlib(L, funcs); | 72 | luaL_newlib(L, funcs); |
| 22 | return 1; | 73 | return 1; |
| 23 | } | 74 | } |
