Некоторое время назад я решил использовать методологию подсчета ссылок, чтобы управлять временем жизни ресурса, и это помогло. Позже я столкнулся с новой проблемой - сохранить время жизни объекта до тех пор, пока Lua не выполнит долгосрочную задачу в отдельном потоке (что достигается с помощью библиотеки Turbo).
Идея, которую я придумал, состояла в том, чтобы передать копию shared_ptr Lua и позволить сборщику мусора решать, когда ресурс больше не нужен.
Но проблема в том, что Lua дважды выполняет очистку данных, и я понятия не имею, почему.
Здесь я опишу несколько шагов, которые я выполнил. Сначала я создаю таблицу, задаю для нее метатабельность и помечаю ее как глобальную
static const struct luaL_reg FileTableDefinition[] = {
{ "__gc", destroy },
{ NULL, NULL } };
const char* id = "PointerManager";
lua_newtable( state );
luaL_newmetatable( state, id );
luaL_register( state, NULL, FileTableDefinition );
lua_pushliteral( state, "__index" );
lua_pushvalue( state, -2 );
lua_rawset( state, -3 );
lua_setglobal( state, id );
На втором шаге я назначаю shared_ptr для объекта в пользовательских данных и устанавливаю деструктор из таблицы, определенной на шаге 2.
Файл - это класс, который экспортируется в LUA, так что пользователь фактически может выполнить вызов этого класса.
void *userData = lua_newuserdata( state, sizeof( std::shared_ptr<File> ) );
// Handle allocation failure
if ( ! userData) { return; }
// Use the placement parameters of the new operator to allocate
// shared_ptr within the userdata provided by Lua. Copy our other
// shared_ptr into it, increasing the reference count
new(userData) std::shared_ptr<File>( aFile );
// Now just set the metatable on this new object
luaL_getmetatable( state, "PointerManager" );
lua_setmetatable( state, -2 );
Деструктор объекта определен следующим образом:
int destroy( lua_State* state )
{
void* resourcePtr = luaL_checkudata( state, 1, "PointerManager" );
if( resourcePtr )
{
auto resource = static_cast<std::shared_ptr<File>*>( resourcePtr );
resource->reset();
return 1;
}
return 0;
}
Я выполняю вызов сценария (lua_pcall) события. Сценарий определяется следующим образом:
local turbo = require("turbo")
function sleep(n)
os.execute("sleep " .. tonumber(n))
end
function on_file_event ( file )
print("on_file_event enter.")
turbo.ioloop.instance():add_callback(function()
local thread = turbo.thread.Thread(function(th)
print("executing")
sleep(1)
print( file:filepath() )
file = nil
collectgarbage("collect")
print("stopped executing")
th:stop()
end)
turbo.ioloop.instance():close()
end):start()
print("on_file_event exit.")
end
Я поместил cout в деструктор класса, и как-то он вызывается дважды. Оба раза вызывается функция destroy (один раз, когда я вызываю сборщик мусора и один раз при выходе из программы), и независимо от того, что я делаю сброс деструктора общего указателя на базовый объект, достигнутый дважды. Память разделяемого указателя и самого указателя одинакова, идентификатор потока также одинаков. Я также совершил погружение в Вальгринде, но никакой коррупции обнаружено не было.
Calling destroy file
>> ~File object deleted (/opt/nids/artifacts/generic/32). Address: 0x1bec850
Calling destroy file
>> ~File object deleted (/opt/nids/artifacts/generic/32). Address: 0x1bec850
Я что-то делаю не так?