I am trying to create a GC finalizer for a function value, storing it in a weak table using the C API.
I started writing a prototype in pure Lua 5.2:
local function myfinalizer() print 'Called finalizer' end function myfunc() print 'Called myfunc' end local sentinels = setmetatable({}, { __mode='k' }) sentinels[myfunc] = setmetatable({}, { __gc=myfinalizer }) myfunc() myfunc = nil collectgarbage 'collect' print 'Closing Lua'
Result:
Called myfunc Called finalizer Closing Lua
The prototype seems to work as intended. The following is version C:
#include <stdlib.h> #include <stdio.h> #include "lua.h" #include "lualib.h" #include "lauxlib.h" static int my_finalizer(lua_State *L) { puts("Called finalizer"); return 0; } static int my_func(lua_State *L) { puts("Called myfunc"); return 0; } int main(void) { lua_State *L = luaL_newstate(); luaL_openlibs(L); // create sentinels table (weak keys) in registry lua_newtable(L); // t lua_newtable(L); // t mt lua_pushstring(L, "k"); // t mt v lua_setfield(L, -2, "__mode"); // t mt lua_setmetatable(L, -2); // t lua_setfield(L, LUA_REGISTRYINDEX, "sentinels"); // // push global function and register as sentinel lua_pushcfunction(L, my_func); // f lua_getfield(L, LUA_REGISTRYINDEX, "sentinels"); // ft lua_pushvalue(L, 1); // ftk lua_newuserdata(L, 0); // ftkv lua_newtable(L); // ftkv mt lua_pushcfunction(L, my_finalizer); // ftkv mt v lua_setfield(L, -2, "__gc"); // ftkv mt lua_setmetatable(L, -2); // ftkv lua_settable(L, -3); // ft lua_pop(L, 1); // f lua_setglobal(L, "myfunc"); // // execute test script and exit if (luaL_dostring(L, "myfunc(); myfunc=nil; collectgarbage'collect'")) { printf("Error: %s\n", lua_tostring(L, -1)); } lua_gc(L, LUA_GCCOLLECT, 0); // suggestion: two full gc cycles fflush(stdout); // suggestion: immediate flush puts("Closing Lua"); lua_close(L); fflush(stdout); return EXIT_SUCCESS; }
Compiled using:
$ gcc -std=c99 -Wall -Werror -pedantic -O2 -o main main.c -ldl -llua52 -lm
Result:
Called myfunc Closing Lua Called finalizer
Version C has a few minor differences:
- Instead of the local
sentinels table, I store it in the registry. - Using user data of zero size instead of a table for a sentinel value with the __gc
__gc .
I am confused why in version C the myfunc finalizer does not execute after starting the full collection loop. What am I doing wrong?