Maybe a little late, but… The solution is to put new objects into weak table and never create objects already stored there.
-- this should be really C, but for readability, we write it in Lua pseudocode
registry.my_cache = setmetatable({ }, { __mode = "v" })
function push_object(db_id)
local object = registry.my_cache[db_id]
if object == nil then
object = lua_newuserdata(db_id)
registry.my_cache[db_id] = object
end
end
assert(push_object(1) == push_object(1))
Now only unique db_id's go from C side to Lua side, problem almostly disappeared.
But there is one more detail to take care of. Garbage-collection of userdata has two phases: finalization and removal from weak tables. There are moments, when userdata is finalized, but still present in weak table, so the code above may return finalized userdata to the user. Additional check should be made, and if ud is finalized, it should be manually removed from table first.
function push_object(db_id)
local object = registry.my_cache[db_id]
-- check vitality first
if is_finalized(object) then
registry.my_cache[db_id] = nil
object = nil
end
if object == nil then
object = lua_newuserdata(db_id)
registry.my_cache[db_id] = object
end
end
How you know whether userdata is finalized is up to your implementation of finalization method (metatable.__gc).