将 shared_ptr 传递给 Lua
Passing shared_ptr to Lua
前段时间我决定采用引用计数方法来管理资源的生命周期,并且它成功了。后来我遇到了一个新的挑战,要保留一个对象的生命周期,直到 Lua 在一个单独的线程中执行一个 long-运行 任务(这是通过 turbo 库实现的)。
我的想法是将 shared_ptr 的副本传递给 Lua 并让垃圾收集器决定何时不再需要该资源。
但问题是 Lua 两次清除数据,我不知道为什么。
在这里我将描述我执行的几个步骤。首先,我正在创建一个 table,为其设置 metatable 并将其设置为 global
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 步中定义的 table 设置一个析构函数。
该文件是一个 class 导出到 LUA 因此用户实际上可以执行对该 class.
的调用
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 放入 class 析构函数中,不知何故它被调用了两次。两次调用 destroy 函数(一次是在我调用垃圾收集器时,一次是在程序退出时),并且无论我是否正在重置底层对象的共享指针析构函数,都达到了两次。共享指针和指针本身的内存是一样的,线程id也是一样的。我也进行了一次 Valgrind 潜水,但没有发现损坏。
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 分支,所以地址是相同的,但实际上它们在不同的内存中 space。后来内存被释放,出现了两套印刷品。
我能够用 straces 确认这一点。
前段时间我决定采用引用计数方法来管理资源的生命周期,并且它成功了。后来我遇到了一个新的挑战,要保留一个对象的生命周期,直到 Lua 在一个单独的线程中执行一个 long-运行 任务(这是通过 turbo 库实现的)。
我的想法是将 shared_ptr 的副本传递给 Lua 并让垃圾收集器决定何时不再需要该资源。 但问题是 Lua 两次清除数据,我不知道为什么。
在这里我将描述我执行的几个步骤。首先,我正在创建一个 table,为其设置 metatable 并将其设置为 global
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 步中定义的 table 设置一个析构函数。 该文件是一个 class 导出到 LUA 因此用户实际上可以执行对该 class.
的调用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 放入 class 析构函数中,不知何故它被调用了两次。两次调用 destroy 函数(一次是在我调用垃圾收集器时,一次是在程序退出时),并且无论我是否正在重置底层对象的共享指针析构函数,都达到了两次。共享指针和指针本身的内存是一样的,线程id也是一样的。我也进行了一次 Valgrind 潜水,但没有发现损坏。
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 分支,所以地址是相同的,但实际上它们在不同的内存中 space。后来内存被释放,出现了两套印刷品。
我能够用 straces 确认这一点。