Передача shared_ptr Lua - PullRequest
       13

Передача shared_ptr Lua

0 голосов
/ 27 августа 2018

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

Я что-то делаю не так?

1 Ответ

0 голосов
/ 27 августа 2018

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

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

...