Передача shared_ptr в Lua

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

Я что-то делаю совершенно не так?

Эрик Алапяя. Разве я не использую конструктор копирования? Он должен использовать счетчик ссылок существующего shared_ptr AFAIK? Также я сделал печать use_count () после построения, как и ожидалось, было 2.

teoring 27.08.2018 14:27

Неважно, я неправильно прочитал ваш код.

Erik Alapää 27.08.2018 15:32
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
2
355
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я нашел причину. Это была основная вилка внутри библиотеки turbo. Когда я создал новый поток в lua, возникла вилка C, поэтому адреса были одинаковыми, но на самом деле они находились в другом пространстве памяти. Позже память освободилась и появились два комплекта отпечатков.

Я смог подтвердить это с помощью straces.

Другие вопросы по теме