Some time ago I have decided to go for a reference count methodology to manage the lifetime of the resource and it did a trick. Later I have faced a new challenge, to preserve an object lifetime till Lua executes a long-running task in a separate thread ( which is achieved with turbo library ).
The idea I came up was to pass a copy of the shared_ptr to the Lua and let garbage collector decide whenever the resource is not needed anymore. But the problem is Lua performing clear of the data twice and I have no idea why.
Here I will describe a few steps I performed. At first, I am creating a table, setting metatable for it and putting it as 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 );
During the step number two, I am allocating a shared_ptr with an object in the user data and setting a destructor from the table defined in step 2. The File is a class which is exported to the LUA so user actually can perform a call to that 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 );
The destructor of the object defined as follows:
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;
}
I am doing a script call (lua_pcall) of the event. Script defined as follows:
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
I put a cout into the class destructor and somehow it is called twice. Both times destroy function is called (once when I am calling garbage collector and once on program exit) and regardless I am doing a reset of shared pointer destructor of the underlying object reached twice. The memory of the shared pointer and pointer itself is the same, thread id is same as well. I did as well a Valgrind dive but no corruption was found.
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
Am I doing something completely wrong?
I found the reason. It was an underlying fork inside of the turbo library. When I created a new thread in lua - C fork occurred so addresses were the same but in fact they were in the different memory space. Later on memory was freed and two set of prints appeared.
I was able to confirm that with straces.