Слабая таблица и GC-финализатор с использованием API-интерфейса C

Я пытаюсь создать GC-финализатор для значения функции, сохраняя его в слабой таблице с использованием C API.

Я начал писать прототип в чистом 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' 

Результат:

 Called myfunc Called finalizer Closing Lua 

Кажется, что прототип работает по назначению. Ниже приведена версия C:

 #include  #include  #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; } 

Скомпилировано с использованием:

 $ gcc -std=c99 -Wall -Werror -pedantic -O2 -o main main.c -ldl -llua52 -lm 

Результат:

 Called myfunc Closing Lua Called finalizer 

Версия C имеет несколько незначительных отличий:

  1. Вместо таблицы локальных sentinels я храню в реестре.
  2. Использование пользовательских данных с нулевым размером вместо таблицы для значения дозорного значения с __gc .

Я смущен, почему в версии C финализатор myfunc не выполняется после запуска полного цикла сбора. Что я делаю неправильно?

    Как говорится в руководстве Lua:

    Из слабых таблиц удаляются только объекты с явной конструкцией. Значения, такие как числа и светлые C-функции, не подлежат сборке мусора и поэтому не удаляются из слабых таблиц (если только их связанное значение не собирается).

    Ваш my_func без каких-либо повышений, поэтому это легкая функция C, и она не удаляется из слабых таблиц во время сбора мусора, поэтому связанные пользовательские данные не становятся мусором, прежде чем вы закроете состояние Lua. Ваш код должен работать, если вы используете функцию Lua вместо my_func или если вы нажимаете my_func с upvalues ​​(и если вы исправите порядок аргументов в вызове lua_gc !).

    Подводя итог, следующие типы значений не удаляются из слабых таблиц (учитывая, что их ассоциированные ключи / значения не удаляются):

    • булевы
    • чисел
    • строки
    • легкие пользовательские данные
    • светлые функции C (только Lua 5.2)

    Как следствие, ваша программа должна отлично работать с Lua 5.1, потому что нет легких функций C (вам все равно придется исправить вызов lua_gc ).