Некоторое время назад я решил использовать методологию подсчета ссылок для управления временем жизни ресурса, и это помогло. Позже я столкнулся с новой задачей - сохранить время жизни объекта до тех пор, пока 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 в деструктор класса, и почему-то он вызывается дважды. Оба раза вызывается функция уничтожения (один раз, когда я вызываю сборщик мусора, и один раз при выходе из программы), и независимо от того, что я выполняю сброс деструктора общего указателя базового объекта, достигнутый дважды. Память общего указателя и самого указателя одинаковы, идентификатор потока также такой же. Я тоже совершил погружение с Валгриндом, но никаких повреждений обнаружено не было.
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
Я что-то делаю совершенно не так?
Неважно, я неправильно прочитал ваш код.





Я нашел причину. Это была основная вилка внутри библиотеки turbo. Когда я создал новый поток в lua, возникла вилка C, поэтому адреса были одинаковыми, но на самом деле они находились в другом пространстве памяти. Позже память освободилась и появились два комплекта отпечатков.
Я смог подтвердить это с помощью straces.
Эрик Алапяя. Разве я не использую конструктор копирования? Он должен использовать счетчик ссылок существующего shared_ptr AFAIK? Также я сделал печать use_count () после построения, как и ожидалось, было 2.